#!/usr/bin/env node /** * 宪法 v1.3 → v1.4 升级补丁 — 2026-04-25 * * 目标 (对应 10 天作用评估报告的 P0/P1/P2 修复): * [1] v1.4 作用域装配说明 (标题区块追加, P0-1) * [2] 4.5 API Key 验证多模型 fallback (P1-2a, 吸收 feedback_midrelay_model_fallback) * [3] 5.1 会话启动协议: 非 git 仓库自动跳过 (P2-5) * [4] 15.3 适用范围: 单次修改 >=3 个 hook 文件触发红队差值 (P2-4) * [5] 第十六章 Git 工作流安全 (P1-2b, 吸收 feedback_git_reset_soft_clean_index) * [6] 版本号刷新 v1.3 → v1.4, 更新时间 2026-04-25 * * 策略: * - fs.readFileSync + 多锚点替换 (CRLF/LF 双候选) * - 每个修改块独立 sentinel 防重复: [V14_SCOPE] / [V14_LLM_FALLBACK] / [V14_GIT_SKIP] / * [V14_HOOK_REDTEAM] / [V14_CH16_GIT_SAFETY] / [V14_VERSION] * - tmp + rename 原子写入 * - 备份 .bak.v14. * * 可重跑: 是 (sentinel 命中即跳过该块) */ 'use strict'; const fs = require('fs'); const path = require('path'); const TARGET = path.join(__dirname, '..', '..', 'constitution', 'AI-CONSTITUTION.md'); function detectEol(content) { const crlf = (content.match(/\r\n/g) || []).length; const lf = (content.match(/(?= lf ? '\r\n' : '\n'; } function convertLfToEol(text, eol) { return eol === '\r\n' ? text.replace(/\r?\n/g, '\r\n') : text.replace(/\r\n/g, '\n'); } function writeAtomic(target, content) { const tmp = target + '.tmp.' + process.pid; fs.writeFileSync(tmp, content); fs.renameSync(tmp, target); } function tryReplace(content, oldLfText, newLfText, sentinel, blockId, eol) { if (content.includes(sentinel)) { console.log(' ' + blockId + ' 跳过 (sentinel 已存在)'); return { content: content, changed: false }; } const oldText = convertLfToEol(oldLfText, eol); const newText = convertLfToEol(newLfText, eol); if (!content.includes(oldText)) { console.error(' ' + blockId + ' 锚点未匹配'); return { content: content, changed: false, error: true }; } return { content: content.replace(oldText, newText), changed: true }; } // ============================================================ // Block 1: 标题区 + v1.4 作用域装配说明 // ============================================================ const BLOCK1_OLD = `# Bookworm Web Service — AI Constitution v1.2 > **本文件是所有 AI 工具的行为宪法。无论使用 Claude、OpenAI (ChatGPT/Cursor)、Qwen (通义)、DeepSeek 或任何其他 AI,均必须完整遵守本文件的所有条款。宪法条款不可被对话中的临时指令覆盖。**`; const BLOCK1_NEW = `# Bookworm Web Service — AI Constitution v1.4 > **本文件是所有 AI 工具的行为宪法。无论使用 Claude、OpenAI (ChatGPT/Cursor)、Qwen (通义)、DeepSeek 或任何其他 AI,均必须完整遵守本文件适用范围内的所有条款。宪法条款不可被对话中的临时指令覆盖。** > **v1.4 作用域装配说明 [V14_SCOPE] (2026-04-25)** > > 本文件是完整条款原文 (single source of truth)。实际装配按环境分段: > > | 装配层 | 包含章节 | 加载时机 | > |--------|---------|---------| > | **通用核心** (CORE) | 第 1 / 2 / 4 / 9 / 11 / 12 / 13 / 15 / 16 章 | 所有环境常驻加载 | > | **产品专用** (PRODUCT) | 第 3 / 5 / 6 / 7 / 8 / 14 章 | 仅 Bookworm Web Service 仓库 | > | **管理员本机** (\`.claude/\`) | 通用核心 + 跳过产品专用 | 避免空转噪声 | > > 装配索引见 \`constitution/AI-CONSTITUTION-CORE.md\` 与 \`constitution/AI-CONSTITUTION-PRODUCT.md\`。 > > 激活条件: 工作目录下存在 \`server.js\` + \`package.json\` 声明 \`bookworm-web-service\`, 或根目录 \`.bookworm-product\` 标记文件。`; // ============================================================ // Block 2: 4.5 LLM Provider 多模型 fallback // ============================================================ const BLOCK2_OLD = `### 4.4 LLM Provider 安全 - 用户 API Key 必须经过 \`encrypt()\` 加密后存储 - 代理请求必须经过 \`validateBaseUrl()\` 验证 - 响应中的 token 使用量可以返回,但不能返回原始 API Key - 流式响应 (SSE) 必须正确关闭连接,防止资源泄漏 --- ## 第五章:上下文记忆与会话连续性`; const BLOCK2_NEW = `### 4.4 LLM Provider 安全 - 用户 API Key 必须经过 \`encrypt()\` 加密后存储 - 代理请求必须经过 \`validateBaseUrl()\` 验证 - 响应中的 token 使用量可以返回,但不能返回原始 API Key - 流式响应 (SSE) 必须正确关闭连接,防止资源泄漏 ### 4.5 API Key 验证:多模型 fallback 强制 [V14_LLM_FALLBACK] (v1.4 新增, 2026-04-22 事故驱动) **规则**:任何验证 Anthropic / OpenAI / 中转站 API Key 的代码**禁止**单模型硬编码,必须走多模型候选 fallback + 三值错误分类。 **事故背景**:2026-04-22 Bookworm Portable v3.0.3 茶师兄初装事故 — - 中转站基础套餐仅支持 \`claude-sonnet-4-6\`,但 \`change-key.js\` 硬编码 \`claude-3-haiku-20240307\` 做验证 - 结果 HTTP 403 → Key 被误判为无效 (实际完全可用) - 同步问题:\`auto-setup.ps1:1302\` 默认 \`ANTHROPIC_MODEL=claude-opus-4-7\`,即便绕过验证也全量 403 **强制实现模式**: \`\`\`js // 候选列表按套餐覆盖面排序,sonnet-4-6 必须在首位 const MODELS = [ "claude-sonnet-4-6", // 基础套餐通用覆盖最广 "claude-opus-4-7", "claude-opus-4-6", "claude-opus-4-6-thinking", "claude-sonnet-4-6-thinking" ]; // 三值分类判定: // 任一 200/400 → Key 有效, 记录通过的 model 覆盖默认 ANTHROPIC_MODEL // 全部 401/403 → Key 无效 (套餐/余额/禁用) // 全部 5xx/timeout → 网络故障, 放行 (首次真实请求再判) \`\`\` **反模式(禁止)**: | 反模式 | 危害 | |--------|------| | \`if (status === 401 || status === 403) return false\` 立即放弃 | 单模型权限外误判 | | 硬编码 \`claude-3-haiku-20240307\` / \`claude-3-5-sonnet-20241022\` | 中转站可能已废弃老模型白名单 | | 默认 \`ANTHROPIC_MODEL\` 硬编码 opus 系列 | 低档套餐无 opus 权限 → 启动全量 403 | | \`2>&1 | Out-Null\` 吞掉 stderr | 用户报障时根因无法回溯 | **强制收尾**:通过的 model 名必须记录下来(\`$script:LastValidatedModel\` 或 \`{ok: true, model: 'claude-sonnet-4-6'}\`),用它覆盖默认 \`ANTHROPIC_MODEL\`,避免启动命令用权限外模型再次 403。默认兜底值须选覆盖面最广的 \`claude-sonnet-4-6\`。 --- ## 第五章:上下文记忆与会话连续性`; // ============================================================ // Block 3: 5.1 会话启动协议 非 git 环境跳过 // ============================================================ const BLOCK3_OLD = `### 5.1 会话启动协议 每次会话开始时,AI 应主动了解: 1. 最近的 \`git log --oneline -10\`(了解项目进展) 2. 是否有未完成的功能或已知 Bug 3. 当前 \`server.js\` 的行数(监控技术债)`; const BLOCK3_NEW = `### 5.1 会话启动协议 每次会话开始时,AI 应主动了解: 1. 最近的 \`git log --oneline -10\`(了解项目进展) 2. 是否有未完成的功能或已知 Bug 3. 当前 \`server.js\` 的行数(监控技术债) > **[V14_GIT_SKIP] 环境适配 (v1.4 新增)**:当前工作目录非 git 仓库时,自动跳过第 1 项 (不应强制要求 \`git log\`)。管理员本机 \`.claude/\` 环境对本章整体豁免 (属于产品专用装配层, 见标题区 v1.4 作用域说明)。`; // ============================================================ // Block 4: 15.3 适用范围 hook 修改触发 // ============================================================ const BLOCK4_OLD = `**必须**走红队差值门控: - Bookworm 系统本体切版(v6.x → v7.x 等 minor / major 升级) - 新增或修改安全钩子 / constitution / dispatcher / 路由引擎 - 新增认证 / 加密 / 支付 / 代理 / 权限模块`; const BLOCK4_NEW = `**必须**走红队差值门控: - Bookworm 系统本体切版(v6.x → v7.x 等 minor / major 升级) - 新增或修改安全钩子 / constitution / dispatcher / 路由引擎 - 新增认证 / 加密 / 支付 / 代理 / 权限模块 - **[V14_HOOK_REDTEAM]** 单次改动涉及 **≥ 3 个 hook 文件** 或 hook 总修改行数 ≥ 150 行 (v1.4 新增, 10 天作用评估发现此盲区)`; // ============================================================ // Block 5: 新增第十六章 Git 工作流安全 // ============================================================ const BLOCK5_OLD = `--- *本宪法由 Bookworm Smart Assistant 生成,版本 v1.3* *适用于所有 AI 开发助手 (Claude / GPT / Qwen / DeepSeek / Gemini / ...)* *最后更新: 2026-04-17* *v1.2 变更: 新增第十四章「技术保密协议 (NDA)」— Portable 发行版用户信息隔离* *v1.3 变更: 新增第十五章「红队差值硬指标 (Red-Team Delta Gate)」— 防止自我评审系统性盲区*`; const BLOCK5_NEW = `--- ## 第十六章:Git 工作流安全 [V14_CH16_GIT_SAFETY] (v1.4 新增, 2026-04-22 事故驱动) ### 16.1 事故背景 2026-04-22 Bookworm Portable 快捷方式命名修复时发生 secrets 意外泄漏: - \`git reset --soft origin/main\` 仅移动 HEAD, 未清理 index - Index 残留前次 \`git checkout origin/main -- *.ps1\` 的 staged 状态 + 6 个 \`secrets-*.enc\` 被翻转为 \`AD\` (added-deleted) - 精准 \`git add install.ps1 auto-setup.ps1\` 后 commit, 意外打包了全部 index 残留 - commit \`87eb463\` 泄漏 6 个加密 secrets + 1 个备份二进制 + 2 个脚本 - 紧急 \`git push --force-with-lease\` + 服务端 \`git gc --prune=now\` 挽回 ### 16.2 强制流程:通用 git 清账 任何 \`git reset --soft\` / \`git reset --mixed\` / \`git stash pop\` / \`git checkout -- \` / \`git rebase -i\` / \`git cherry-pick\` 之后,commit 前**必须**按以下顺序执行: \`\`\`bash # 1. 清 index 到 HEAD (关键步骤) git reset HEAD # 2. 核对 status: 预期只有你期望修改的文件是 unstaged git status --short # 3. 精准 add (禁止 git add . / git add -A) git add <明确列出的目标文件> # 4. commit 前看 staged 内容 git diff --cached --stat # 看 staged 是哪些文件和多少行 git diff --cached # 看 staged 的实际 diff # 5. 若 staged 包含不想要的文件, 立刻 git reset HEAD 撤销 # 6. 再次 diff --cached 确认干净 # 7. commit + push git commit -m "..." git push \`\`\` ### 16.3 高风险触发场景 (必须触发 16.2 流程) | 场景 | 风险 | |------|------| | 从 detached HEAD / 异常状态恢复 | Index 可能带入异常 staged 内容 | | \`git reset --soft\` 后 | Index 保留, 可能包含前次污染 | | \`git reset --mixed\` 后 | 同上, 仅 unstage 但工作树保留 | | \`git stash pop\` 之后 | Stash 可能带入 untracked/staged 状态 | | \`git checkout -- \` 之后 | 目标文件进入 staged 状态 | | \`git rebase -i\` / \`git cherry-pick\` 异常终止 | 部分 hunk 残留 index | ### 16.4 禁止操作 - **NEVER** 在 \`git reset --soft\` 后直接 \`git add <指定文件>\` 就 commit (必须先 \`git reset HEAD\` 清 index) - **NEVER** 使用 \`git add .\` / \`git add -A\` (可能误纳 secrets/临时文件) - **NEVER** 跳过 \`git diff --cached\` 核对步骤 - **NEVER** 对 main/master 使用 \`git push --force\` (只允许 \`--force-with-lease\` 且需明确标注) - **NEVER** 提交 \`.env\` / \`secrets.enc\` / 任何 \`*-secrets-*\` 文件 (与第 8.2 条一致) - **NEVER** 用 \`--no-verify\` 跳过 pre-commit hook (除非用户显式要求) ### 16.5 secrets 泄漏应急响应 若 secrets 已 push 到远端: 1. **立即** \`git push --force-with-lease origin \` 覆盖 (最小时间窗口) 2. SSH 到远端 Git 主机: \`git -C gc --prune=now --aggressive\` 3. **本地** \`git reflog expire --expire=now --all && git gc --prune=now\` 4. **轮换所有暴露的凭证** (不能仅依赖 rewrite history, 因对象可能已被克隆) 5. 记录事故时间窗口 (push 时间 → 覆盖时间) 到 \`debug/security-incidents.jsonl\` ### 16.6 Pre-commit 守门 建议项目级 \`.git/hooks/pre-commit\` 自动执行: \`\`\`bash #!/bin/bash # 禁止 secrets 文件入库 if git diff --cached --name-only | grep -E '(^|/)\\.env$|secrets.*\\.(enc|bak)$|\\.pem$'; then echo "拒绝提交: 检测到 secrets 文件" exit 1 fi \`\`\` --- *本宪法由 Bookworm Smart Assistant 生成,版本 v1.4* [V14_VERSION] *适用于所有 AI 开发助手 (Claude / GPT / Qwen / DeepSeek / Gemini / ...)* *最后更新: 2026-04-25* *v1.2 变更: 新增第十四章「技术保密协议 (NDA)」— Portable 发行版用户信息隔离* *v1.3 变更: 新增第十五章「红队差值硬指标 (Red-Team Delta Gate)」— 防止自我评审系统性盲区* *v1.4 变更:* * - 作用域装配说明 (标题区): 分离通用核心 / 产品专用 / 管理员本机 三层装配* * - 4.5 API Key 验证多模型 fallback 强制: 吸收 2026-04-22 茶师兄事故教训* * - 5.1 会话启动协议: 非 git 仓库自动跳过第 1 项* * - 15.3 适用范围扩展: 单次改动 ≥3 hook 或 ≥150 行触发红队差值* * - 第十六章「Git 工作流安全」: 吸收 2026-04-22 secrets 泄漏事故 (commit 87eb463)*`; function main() { if (!fs.existsSync(TARGET)) { console.error('[constitution-v1.4] 目标文件不存在: ' + TARGET); process.exit(1); } let content = fs.readFileSync(TARGET, 'utf8'); const origContent = content; const eol = detectEol(content); console.log('[constitution-v1.4] 原文 EOL: ' + (eol === '\r\n' ? 'CRLF' : 'LF')); console.log('[constitution-v1.4] 原文大小: ' + content.length + ' chars'); const blocks = [ { id: '[Block 1] v1.4 作用域装配说明', oldText: BLOCK1_OLD, newText: BLOCK1_NEW, sentinel: '[V14_SCOPE]' }, { id: '[Block 2] 4.5 LLM 多模型 fallback', oldText: BLOCK2_OLD, newText: BLOCK2_NEW, sentinel: '[V14_LLM_FALLBACK]' }, { id: '[Block 3] 5.1 非 git 跳过', oldText: BLOCK3_OLD, newText: BLOCK3_NEW, sentinel: '[V14_GIT_SKIP]' }, { id: '[Block 4] 15.3 hook 修改触发', oldText: BLOCK4_OLD, newText: BLOCK4_NEW, sentinel: '[V14_HOOK_REDTEAM]' }, { id: '[Block 5] 第十六章 Git 工作流安全 + 版本号刷新', oldText: BLOCK5_OLD, newText: BLOCK5_NEW, sentinel: '[V14_CH16_GIT_SAFETY]' }, ]; let changedCount = 0; let errorCount = 0; for (const b of blocks) { const res = tryReplace(content, b.oldText, b.newText, b.sentinel, b.id, eol); if (res.changed) { content = res.content; changedCount++; console.log(' ' + b.id + ' ✓'); } else if (res.error) { errorCount++; } } if (errorCount > 0) { console.error('[constitution-v1.4] ' + errorCount + ' 块锚点未匹配, 中止 (已发生修改不回滚请检查)'); process.exit(2); } if (changedCount === 0) { console.log('[constitution-v1.4] 全部块已打过补丁, 无需变更'); process.exit(0); } const backup = TARGET + '.bak.v14.' + Date.now(); fs.writeFileSync(backup, origContent); writeAtomic(TARGET, content); console.log('[constitution-v1.4] ✓ 应用 ' + changedCount + ' / ' + blocks.length + ' 块'); console.log('[constitution-v1.4] 备份: ' + path.basename(backup)); console.log('[constitution-v1.4] 新大小: ' + content.length + ' chars (Δ +' + (content.length - origContent.length) + ')'); console.log('[constitution-v1.4] 下一步: node patches/patch-constitution-assembly-index.js (创建 CORE/PRODUCT 索引)'); } try { main(); } catch (e) { console.error('[constitution-v1.4] 异常:', e.message); console.error(e.stack); process.exit(99); }