/** * 开发态自动登录(**仅本地静态服务**,绝不进入应用包) * * ## 它解决什么 * * 本地跑生产构建(`npm run build` + 静态服务)时,登录要人工点「Token 直登」并粘贴 * 一长串 `r:` Token —— 每建一个浏览器上下文就要重复一次,Playwright / 冒烟 / * 真实账号回归全都卡在这一步,无法自动化。 * * ## 它怎么工作 * * 本模块由 `scripts/dev-server.mjs` 进程通过 `import()` 加载,把一段**内联脚本** * 注入 `index.html` 的 ``。那段脚本唯一做的事:在 Angular 启动前,把 * `.claude/dev-login.json` 里的 token / me 写进 localStorage(键名与 * `SessionService` 完全一致:`fmode_session_token` / `fmode_me`)。 * * · 不在 `src/` 下 → 不在任何 tsconfig 的输入里 → **不会被打进应用构建** * · 不写进 `index.html` 源文件 → 仓库里的 `index.html` 保持干净 * · 只有 `scripts/dev-server.mjs` 会调用 → 生产部署链路完全不知道它存在 * * ## 为什么不放在环境文件里 * * `environment*.ts` 会被编译进包,`ng build` 之后 token 就固定在产物里了。 * 本模块走运行时注入,token 改了只需刷新页面,且产物始终是干净的生产包。 */ import { readFileSync, existsSync } from 'node:fs'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const here = dirname(fileURLToPath(import.meta.url)); // 本文件在 scripts/ 下;.claude/dev-login.json 在**项目根**下,故上溯一级 const CONFIG_PATH = resolve(here, '../.claude/dev-login.json'); export const DEV_LOGIN_KEYS = { TOKEN: 'fmode_session_token', ME: 'fmode_me' }; /** * 读 `.claude/dev-login.json`。 * * @returns {{ enabled: boolean, reason?: string, payload?: { token: string, me: object } }} */ export function loadDevLogin() { if (!existsSync(CONFIG_PATH)) { return { enabled: false, reason: `未找到 ${CONFIG_PATH}` }; } let raw; try { raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf8')); } catch (e) { return { enabled: false, reason: `dev-login.json 解析失败:${e.message}` }; } const token = typeof raw.token === 'string' ? raw.token.trim() : ''; const me = raw.me && typeof raw.me === 'object' ? raw.me : null; if (!token) return { enabled: false, reason: 'dev-login.json 的 token 为空' }; if (!me || !me.id) return { enabled: false, reason: 'dev-login.json 的 me.id 为空' }; return { enabled: true, payload: { token, me } }; } /** * 生成注入用的内联脚本。 * * 🔴 用 `JSON.stringify` 而不是模板拼接 —— 值里的引号/换行会被正确转义。 * 🔴 但 `JSON.stringify` **不会**转义 `<` / `>` —— 值里若含 `` 会提前闭合 * 标签,把后面的内容当成 HTML 执行(实测踩到)。所以额外把 `<` `>` `&` * 转成 `<` / `>` / `&`(JSON 里等价,且无法闭合标签)。 */ export function buildInjectionScript(payload) { const { TOKEN, ME } = DEV_LOGIN_KEYS; const data = JSON.stringify({ key: TOKEN, meKey: ME, token: payload.token, me: payload.me }) .replace(//g, '\\u003e') .replace(/&/g, '\\u0026'); return ``; } /** * 把注入脚本插到 `` 前。幂等:已注入过就原样返回。 */ export function injectIntoHtml(html, payload) { const marker = ''; if (html.includes(marker)) return html; const script = marker + buildInjectionScript(payload); const i = html.toLowerCase().indexOf(''); if (i < 0) return script + html; return html.slice(0, i) + script + html.slice(i); }