平台共享模型改为「管理员逐用户授权」+ 品牌中文名改「能力网络」+ 修掉跨机模型落地/语言偏好静默失效

三条线合并入库(同一次部署批次,源码与生产此前已一致):

一、档案 138 · 平台共享模型:管理员逐用户授权(默认关闭)
  用户口径原文:「admin 设置的共享模型,需要 admin 在用户列表中开启(新增选项,默认关闭),
  用户才能在会话中使用(以及在设置的模型设置页面展示)」。
  · DB 迁移 v11:新增 users.shared_model_granted(DEFAULT 0 = 默认关闭)。
    ⚠️ 刻意**不**复用 v6 的 shared_model_enabled —— 那是用户侧偏好(用户能自己关,默认 1),
    而本需求要的是**管理员门禁**;共用一列则用户点一下就给自己授权,门禁形同不存在。
    生效 = granted ∧ enabled(server.ts#sharedLandingRows)。
  · 新路由 POST /api/admin/users/:id/models/shared(requireAdmin)+ 审计 shared_model_grant
    + **尽力而为**重启该用户实例(租约被占/实例未运行都不算失败)。
  · admin 用户列表新增「共享模型」列;用户侧 /api/me/keys 增 shared.granted / sharedModelGranted;
    插件 0.3.24:未授权时「平台共享模型」整块不渲染。

二、顺带修掉一个既有真缺陷:平台侧写用户 home 必须走 UserFs(用户卷在 worker 上)
  landModels 原先 join(owner.home_dir, …) + 本机 fs ⇒ 对「实例不在控制面本机」的用户
  读到空串(**不报错**)⇒ 模型落地一直是**静默空操作**(托管清单还被清空)
  ——即档案 87 的模型设置页对 guest 这类用户**从未生效**。
  · UserFs 新增 readHomeFile/writeHomeFile + 文件名白名单(settings.yaml / .credentials.yaml)
  · worker agent 新增 /fs/home-read /fs/home-write(只认白名单裸文件名)
  · landModels 改走 userFs(与"文件面"同一份按归属路由 ⇒ 落地与实例必然同机)
  · 同轮把 /api/me/locale(档案 102 语言偏好)也改成同一套(原先同样失效)
  · home-files.ts 抽出 backupHomeFile(备份留平台侧,命名规则逐字不变)

三、档案 139 · 品牌中文名:能力枢纽 → 能力网络(其他语言仍 CapabilityNet)
  落点四处:i18n 中文词条 / admin.html 顶栏 / favicon.svg 的 title+aria-label / design.css 注释;
  test/i18n-brand.test.mjs 期望值同步。档案 137 顶部加"后续"指针,不改历史。

验证(全部真机实测):
  · 红腿:未授权 → 106 上 guest 的 .credentials.yaml refs 变空(共享 key 被撤)
  · 绿腿:授权 → key 回来 + 托管清单恢复 ["DEEPSEEK_API_KEY"]
  · admin 列表带出 sharedModelGranted;用户侧 granted 随授权翻面(true/shared ↔ false/none)
  · 开关两次均 200(不再假失败);插件实装 0.3.24 且含 gating 字符串
  · 语言偏好:106 上 settings.yaml 出现 locale.preference=en(属主=实例属主,既有段逐字保留)
  · 本机 npm test 226 tests / 225 pass / 0 fail / 1 skipped;四个 verify 脚本全绿

部署:47 推 51 个 lib 产物、106 推 12 个(lib/ 是 gitignore ⇒ 回滚点物化在
/opt/dsh/backups/seq138b-20260919-125345/,逐文件对账 0 不一致;先 106 后 47);
插件 business-plugins 0.3.24(两机 artifacts 与本机 pack md5 一致)。
This commit is contained in:
admin committed 2026-09-19 13:51:01 +08:00
1 parent 971ccc3703
commit c2b7c5ef71
30 files changed
+770 -70

No files matched your search

@@ -1,5 +1,8 @@
# 137-品牌标识改造(去 DeepSeek 图形 → 平台自有标识)
> ⚠️ **后续(2026-09-19)**:中文品牌名已由「能力枢纽」改为 **「能力网络」**(其他语言仍 `CapabilityNet`)
> —— 见档案 **139**。本档正文保留当时的定名,**不改历史**。
> **一句话**:把我们自己的页面上所有 **DeepSeek 品牌标识**(鲸鱼图标 / 「DeepSeek」文字图形 / favicon)
> 换成**平台自有标识** —— 中文「**能力枢纽**」,英语及其他语言「**CapabilityNet**」。
> **触发**:用户 2026-09-19 原话三句 ——
@@ -0,0 +1,154 @@
# 138 · 平台共享模型 —— 管理员**逐用户**授权(默认关闭)
- 日期:2026-09-19
- 状态:✅ **已上线并端到端验证**(控制面 + 前端 + 插件 **0.3.24**;两腿实测:未授权撤销、授权恢复)
- 触发(用户口径,原话):「**调整设计 admin设置的共享模型,需要admin在用户列表中开启(新增选项,默认关闭),用户才能在会话中使用(以及在设置的模型设置页面展示)**」
- 落点:`src/db/{schema,types,repo,pg,sqlite,adapter}.ts`、`src/web/routes/{admin,auth}.ts`、`src/web/server.ts`、`web/admin.html`、`poc/business-plugins/lib/client.js`(**0.3.24**)+ **顺带修掉一个既有真缺陷**:`src/fs/{user-fs,local-user-fs,remote-user-fs}.ts`、`src/worker/agent.ts`、`src/web/home-files.ts`(见 §五)
> **TL;DR**|① 共享模型从「人人默认可用(用户可自关)」改成「**管理员逐用户授权**」——新增列
> `users.shared_model_granted`(**默认 0 = 关闭**),**生效 = granted ∧ enabled**(前者 admin 在**用户列表**里开,
> 后者是用户自己的偏好,保留不动);② 未授权时**设置页连"平台共享模型"那一块都不渲染**(用户原话"开启才展示");
> ③ 🔴 **顺带发现并修掉一个既有真缺陷**:落地层原先用**本机 fs** 写用户 home,而用户卷在 worker 上
> ⇒ 对"实例不在本机"的用户,**模型落地一直是静默空操作**(托管清单被清空、目标文件一个字节没动)
> —— 也就是说档案 87 那套「模型设置页」对 **guest 这类用户从来就没生效过**;本档把它改成走
> `UserFs`(与"文件面"同一份按归属路由),并给 worker agent 补了两个**白名单**端点。
---
## 一、为什么是"新开一列"而不是改旧列的默认值(本轮最关键的设计判断)
| 事实 | 依据 |
|---|---|
| 旧列 `users.shared_model_enabled`(v6)语义 = **用户侧偏好**(用户自己能在设置页关掉,默认 1) | `schema.ts` 的 `SQLITE_V6` 注释 + `dashboard`(档案 87 口径②) |
| 用户要的是**管理员门禁** —— "只有 admin 开了,用户才能用" | 本轮触发原话 |
| 若两者共用一列:**用户在自己设置页点一下就把自己"授权"了** ⇒ 门禁形同不存在 | 直接推论 |
⇒ 故**两列两人**:`shared_model_granted`(admin,默认 **0**)+ `shared_model_enabled`(用户,默认 1,**不变**),
**生效 = `granted ∧ enabled`**(`server.ts#sharedLandingRows`)。缺一不给。
> ⚠️ `DEFAULT 0` 让**存量行也一并变 0**(两个方言加列都用默认值回填)⇒ 迁移后所有既有用户都"未授权"。
> 这正是"默认关闭"的字面语义;代价是 **guest 需要 admin 开一次**才会重新拿到共享模型(见 §四-③)。
## 二、改动清单
| 层 | 文件 | 改动 |
|---|---|---|
| DB 迁移 | `src/db/schema.ts` | **v11**:`ALTER TABLE users ADD COLUMN shared_model_granted INTEGER NOT NULL DEFAULT 0;`(两方言同款;注释写清"为什么新开列") |
| 类型 | `src/db/types.ts` | `User.shared_model_granted` + `PublicUser.sharedModelGranted`;`toUser` 用 **`Number(row.x ?? 0) !== 0`** |
| 仓储 | `src/db/repo.ts` | `USER_COLS` 加列、`findSessionWithUser` 补列、新增 `get/setSharedModelGranted`(缺失行按 **false** = 失败关闭) |
| PG | `src/db/pg.ts` | 🔴 **本文件另有一份 `USER_COLS`** —— 加列未同步它 ⇒ `listPublicUsers` 读不到该列、admin 列表恒显示"未开启"(**实测踩到**,见 §六-2) |
| SQLite | `src/db/sqlite.ts` / `adapter.ts` | 转发 + 接口声明 |
| 生效判据 | `src/web/server.ts` | `sharedLandingRows` 三条件(**granted ∧ enabled ∧ 不是那个 admin 本人**);`resolveApiKey` 的回退注入**也判门禁**(失败关闭) |
| 管理面路由 | `src/web/routes/admin.ts` | 新增 **`POST /api/admin/users/:id/models/shared`**(`requireAdmin`,body `{enabled}`)→ 落库 + 审计 `shared_model_grant` + **尽力而为**重启该用户实例 |
| 用户面接口 | `src/web/routes/auth.ts` | `sharedKeyInfo` 加 `granted`;`keySourceOf` 加门禁(未授权 ⇒ `none`);`GET /api/me/keys` 增 `sharedModelGranted` |
| 管理页 | `web/admin.html` | 用户表**新增「共享模型」列**(已开启/已关闭按钮;admin 与 pending 行显示「—」)+ 一行说明 |
| 插件 | `poc/business-plugins/lib/client.js`(0.3.24) | 「平台共享模型」区块改为 **`shared.granted` 为真才渲染**(未授权整块不出现) |
| 测试 | `test/db.test.mjs`、`test/local-user-fs.test.mjs`、`scripts/verify-platform-admin-section.mjs` | 新增 4 条:granted 默认关 ∧ 可开关 ∧ 与偏好互不影响、列表带出该列、**未授权 ⇒ 区块不渲染(正反两腿)** |
## 三、🔴 三条判据(都是踩出来的)
1. **门禁类判据一律"失败关闭"**:`getSharedModelGranted` 对"查不到这个人"返回 **false**(而 `getSharedModelEnabled`
对同样情形返回 true —— 两条**故意相反**,已在单测里钉死)。
2. **门禁判在"使用点",不判在"归属判据"里**(本轮红腿实测):`sharedDeepseekKey()` 同时被
① 「一次性交接」用来**认领**老实现写下的那行 key(这是**归属**问题)② 回退 env 注入(这是**授权**问题)。
第一版把门禁加在函数内部 ⇒ 被撤销授权的用户**认不出自己写过的行** ⇒ 那行永远删不掉
⇒ "关掉即生效"不成立。**修法**:函数保持不判,门禁挪到 `resolveApiKey` 的调用点。
3. **授权路由里的"重启实例"必须尽力而为**:授权已落库才是真结果;实例没在跑、或归属租约被别的持有者占着,
都**不算开启失败**(第一版直接 `await restartMain` ⇒ 租约被占时回 500,admin 界面显示"操作失败",
而库里其实已经改对了)。
## 四、验证(全部真机实测)
| # | 项 | 证据 |
|---|---|---|
| ① | **迁移 v11 生效** | `SELECT version FROM schema_migrations` ⇒ `11`;`\d users` 出现 `shared_model_granted integer not null default 0`;四个用户该列全 0(默认关闭) |
| ② | **红腿:未授权 ⇒ 共享 key 被撤** | guest(实例在 **w-106**)设为 `false` → 重启实例 → 106 上 `.credentials.yaml` 的 `refs:` **变空**(`DEEPSEEK_API_KEY` 消失)、托管清单 `{"refs":[],"routes":[]}` |
| ③ | **绿腿:授权 ⇒ 恢复** | 设为 `true` → 重启 → `refs: DEEPSEEK_API_KEY: 'sk-…'` **回来**、托管清单 `{"refs":["DEEPSEEK_API_KEY"]}` |
| ④ | **admin 列表带出状态** | `GET /api/admin/users` ⇒ guest `sharedModelGranted=True`(其余 false) |
| ⑤ | **用户侧展示判据翻面** | guest 会话 `GET /api/me/keys`:授权时 `shared.granted=true effective=shared`;撤销后立刻 `granted=false effective=none` |
| ⑥ | **开关不再报假失败** | 撤销/开启两次调用均 **200** `{ok:true, sharedModelGranted:…, restarted:false}`(实例未运行) |
| ⑦ | **插件实装** | 106 上 guest 的 `profiles/web/node_modules/@dsh-local/business-plugins` = **0.3.24**,`lib/client.js` 含 `shared.granted` ×2;`.dsh-stage/` 有 0.3.24.tgz(本机 pack 与 `/opt/dsh/artifacts/` md5 一致 `75f342eb…`) |
| ⑧ | **零回归(本机)** | `npm test` **226 tests / 225 pass / 0 fail / 1 skipped**;`verify-static` / `verify-inject` / `verify-platform-admin-section`(含新 2 条)/ `verify-models-render` 全绿 |
| ⑨ | **语言偏好跨机修复生效** | guest(实例在 **w-106**)`POST /api/me/locale {locale:'en'}` ⇒ 200 `changed:true`;106 上 `settings.yaml` 出现 `locale: preference: en`(属主 = 实例属主,既有段逐字保留);平台侧备份 `settings-<id>-<ts>.yaml` 同步落地 |
⚠️ **未做**:无浏览器的 UI 目视(admin 表格多了一列、「共享模型」区块的隐藏效果)—— 留给你看一眼。
⛔ 未重启任何**正在服务**的用户实例去做验证(验证用的 guest 实例由我自己停/起,收口时回到 stopped)。
## 五、🔴 顺带修掉的既有真缺陷:**跨机用户的"模型落地"一直是空操作**
**怎么发现的**:按上面 §四-② 第一次跑红腿时,guest 的凭据文件**一个字节没动**、而托管清单却被清空了。
根因链(文件级):
| # | 位置 | 事实 |
|---|---|---|
| 1 | `server.ts#landModels`(改前) | `join(owner.home_dir, '.credentials.yaml')` + **本机 fs** 读 —— 而 `home_dir` 是**worker 上的路径** |
| 2 | 实测 | 47 上 `/var/lib/dshs/users/<guest>/` **只有 `ws/`**、没有 `home/` ⇒ 读到空串(**不报错**) |
| 3 | 后果 | `reconcileCredentials('', [], [...])` ⇒ 无变化 ⇒ 不写盘;但 `writeManaged()` 照写 ⇒ **托管清单被清成空**(平台从此"忘记"自己写过的那行) |
⇒ 影响面**不止本档**:**档案 87 的整套「模型设置页」对"实例不在控制面本机"的用户从来就没生效过**
(guest 的实例在 w-106 ⇒ 用户改模型设置不生效);档案 102 的**语言偏好**(`/api/me/locale`)走的是同一套
`home-files` 写入,**同样命中这个缺陷**(⚠️ 本档**未修**它,见 §七-3)。
**修法**(与既有"文件面"设计对齐 —— `server.ts:848-850` 那段注释原本就写着"文件写到 A、实例起在 B"的教训):
| 层 | 改动 |
|---|---|
| `src/fs/user-fs.ts` | 新增 `readHomeFile` / `writeHomeFile` + **文件名白名单** `HOME_FILE_NAMES = ['settings.yaml','.credentials.yaml']`(⛔ 只收裸文件名,等于"平台只管自己那两个配置") |
| `local-user-fs.ts` | 本机实现(`0600` + chown 给 home 属主);非白名单名 ⇒ `bad_path` |
| `remote-user-fs.ts` | 走 agent 的 `/fs/home-read` `/fs/home-write`,目标是 `hostIdFor` **钉住的那台机**(与实例同机) |
| `worker/agent.ts` | 新增两个端点(**只认白名单裸文件名**,非法即 400 `bad_path`) |
| `web/home-files.ts` | 抽出 `backupHomeFile()`(**平台侧**备份;`writeHomeFile` 改为复用它,命名规则逐字不变) |
| `web/server.ts` | `landModels` 的读写**全部改走 `userFs`**:`readHomeFile(...) ?? ''` ∧ `backupHomeFile(...)` + `userFs.writeHomeFile(...)` |
**为什么这是"必须做"而不是"顺手优化"**:不做 ⇒ 用户这条需求("admin 开了才能在会话中使用")
对 guest 这类用户**永远不成立**(授权改了、实例里什么也没变)。R11「只做正向迭代」不允许留这种"看起来做了"的状态。
**部署面**(`lib/` 是 gitignore 的 ⇒ 回滚点只能物化,⛔ 别指望 `git checkout`):
- **106** `/opt/dshs-cluster/lib`:`fs/*` + `worker/agent.*` 共 12 个(回滚点 `/opt/dsh/backups/seq138b-20260919-125345/opt/dshs-cluster/lib`,先抓旧版再覆盖,对账 0 不一致)
- **47** `/opt/dshs/lib`:42 + 3 + 3 个(同上,回滚点同目录 `/opt/dsh/backups/seq138b-…/opt/dshs/lib`);`restart dshs dshs-worker`
- 顺序:**先 worker(106)后 Manager(47)** —— 新端点必须先在位;两版并存时表现为"落地仍不生效"(= 改前状态),⛔ 不会更糟
- ⚠️ **106 上跑 `ensure-biz-plugins.cjs` 需要补丁**:它 `require('/opt/dshs/node_modules/better-sqlite3')`
而 106 只有 `/opt/dshs-cluster/node_modules`(且该原生模块在 106 上没为 node 22 编译)⇒ 用**临时副本**
`/tmp/ebp-106.cjs`(只改两处:require 置空、用户清单改由 `DSH_USERS_JSON` 注入,清单取自权威 PG)。
⛔ 正本 `scripts/ensure-biz-plugins.cjs` **未动**。
## 六、事故 / 踩坑
1. **用户卷在远端时,"落地"与"备份"要分开**:备份(平台自己的副本)留在控制面 `/opt/dsh/backups` 正合适;
⛔ 不要为了备份再往远端开第二条通道。
2. 🔴 **`pg.ts` 有自己的一份 `USER_COLS`**(与 `repo.ts` 那份是**两份**)⇒ 加列必须**两处都改**。
只改 `repo.ts` 时:SQLite 路径正常、**PG 路径(= 生产)读不到新列**,admin 用户列表**恒显示"未开启"**
(而 DB 里其实是 1)—— 这种"两个方言行为不一致"的缺陷只在生产暴露,本档已实测踩到。
3. **临时会话(`mksess.cjs`)TTL = 10 分钟** ⇒ 验证脚本必须"现建现用";跨多次手工调用必撞 401
(本轮撞了两次,写成了 `seq138-leg.sh` 一次跑完)。
4. `scp [email protected]:` 不通(无对应密钥/别名),要用别名 `test106`。
## 七、未做 / 待办
1. **UI 目视**留给用户(无浏览器验收已全绿):admin 用户列表多一列「共享模型」、未授权时设置页不出现共享区块。
2. **`.lock-131/132/133/134/138` 空目录堆积**(占号后没清)—— 文档库卫生,`docs-audit.py` 不报。
3. ✅ **已修(同轮收口时一并做掉)**:`/api/me/locale`(档案 102 的语言偏好)原先**也走本机 fs**
⇒ 对远端用户同样失效。改法与 `landModels` **完全同构**(`userFs.readHomeFile('settings.yaml')`
+ `backupHomeFile` + `userFs.writeHomeFile`)。
**实测**:guest(实例在 106)`POST /api/me/locale {locale:'en'}` ⇒ **200 `changed:true`**;
106 上 `settings.yaml` 出现 `locale: preference: en`、属主 = 实例属主 `dsh-<hash>`、
既有段(`agent-default-model` / `agent-presets` / `llm-pi-ai`)**逐字保留**;
平台侧备份同步落地 `settings-<userId>-<ts>.yaml`。
⇒ **本档之后,平台侧"写用户 home"只剩 `UserFs` 这一条路**(`readTextOrEmpty` / `writeHomeFile`
的**本地版**仍保留给 `deployMode=local` 的单机形态与 `home-files.ts` 内部复用)。
4. ⚠️ **`shared_model_enabled`(用户偏好)现在只在"已授权"时才有可见效果**:用户侧那条
`POST /api/me/models/shared` 路由**未加门禁**(有意 —— 见 `auth.ts` 该路由注释:让用户能先关掉自己不想用的、
也允许先打开,授权一到即生效;且不泄露"管理员是否授权了别人")。若将来要"授权后用户不得自关",需另立需求。
## 八、回滚
```bash
# ① 控制面代码:从本仓重建 HEAD 版 lib 并覆盖(lib/ 不入仓,⛔ 不能用 git checkout)
git -C <本机仓> show HEAD:src/... # 或直接在 47 侧用回滚点:
cp -r /opt/dsh/backups/seq138b-20260919-125345/opt/dshs/lib/* /opt/dshs/lib/ && systemctl restart dshs
# ② worker(106):同样用该回滚点下的 /opt/dshs-cluster/lib
# ③ 插件:确保产物回落 0.3.23(/opt/dsh/artifacts 保留旧包)
# ④ DB:v11 的 ADD COLUMN 不回滚(SQLite 无 down migration;空列不影响旧代码)
```
⚠️ 回滚 ① 会把 §五 的跨机落地修复一起回退 ⇒ 远端用户的模型落地重新变成空操作(不致命,但那是既有缺陷)。
@@ -0,0 +1,55 @@
# 139 · 品牌中文名:能力枢纽 → **能力网络**
- 日期:2026-09-19
- 状态:✅ **已上线**(静态资源直覆,无需重启;页面与接口双端实测)
- 触发(用户口径,原话):「**把页面上的能力枢纽 改为能力网络**」(同轮先说过「能力领域」,随即更正为「能力网络」)
- 上游:档案 **137**(品牌标识去 DeepSeek 图形,定名「能力枢纽」)—— 本档**只改名,不动结构**
- 落点:`web/i18n.js`、`web/admin.html`、`web/favicon.svg`、`web/design.css`(注释)、`test/i18n-brand.test.mjs`
> **TL;DR**|中文品牌名 **「能力枢纽」→「能力网络」**;**其他语言不变**(仍 `CapabilityNet`,
> 走 `en` 词条 = 所有其他语言的回退)。改名落在**四处**:i18n 的中文词条、`admin.html` 顶栏文字、
> **`favicon.svg` 的 `<title>` 与 `aria-label`**(浏览器标签页/读屏可见 —— 最容易漏)、`design.css` 注释。
> 回归测试与 `verify-static`(含 SVG 真解析)全绿;线上 4 文件两端 md5 一致。
---
## 一、为什么改这几处(判据:**用户可见面**,不是"字符串出现即改")
| 落点 | 是否用户可见 | 处置 |
|---|---|---|
| `web/i18n.js` 的 `zh` 表 `brand.name` | ✅ 登录 / 注册 / 唤醒 / 工作台页头 | **改** 为 `能力网络` |
| `web/i18n.js` 的 `en` 表 `brand.name` | ✅ 其他语言**全靠它回退** | **不动**(仍是 `CapabilityNet`) |
| `web/admin.html` 顶栏 `<span class="wordmark">` | ✅ 管理页页头(该页纯中文、未接 i18n) | **改** |
| `web/favicon.svg` 的 `<title>` / `aria-label` | ✅ 标签页提示 / 读屏 | **改** —— 只 grep 改 `.html/.js` 会漏掉它(本轮第一版就漏了) |
| `web/design.css` 第 53 行注释 | ❌ 不可见,但**写的是现行事实** | 顺手更正(留着会误导下一个人) |
| `test/i18n-brand.test.mjs` | — 测试断言 | 六条语言路径的期望值同步 |
| 档案 137 / 本档引用的历史记述 | — | **不改历史**(137 保持原样,仅由 TOP 指针指过来) |
## 二、验证
| 项 | 结果 |
|---|---|
| 品牌回归测试 | `test/i18n-brand.test.mjs` **2/2 通过**(中文三条路径 = 能力网络;英文与未支持语言 = CapabilityNet) |
| 静态校验 | `verify-static.mjs` 全合格(含 `favicon.svg` 段) |
| SVG 真解析 | `xml.etree.ElementTree.parse('web/favicon.svg')` = **OK**(⚠️ 137 那次踩过"注释含 `--` 导致整份 SVG 静默失效",本档只改文本节点,仍逐次复验) |
| 旧名残留 | `grep -rn 能力枢纽\|能力领域 web/ test/ scripts/ poc/ src/` ⇒ **仅剩 i18n 注释里一句"此前用过哪个名"的留痕**(有意保留) |
| 线上 | 4 文件(`admin.html` / `i18n.js` / `design.css` / `favicon.svg`)本机 ↔ `/opt/dshs/web/` **md5 逐一致**;回滚点 `/opt/dsh/backups/seq138-web-20260919-125345/`(4 个旧文件) |
| 零回归 | 全量 `npm test` **226 / 225 pass / 0 fail / 1 skipped** |
## 三、两个执行细节(都是既有经验的重用,不是新坑)
1. **行尾按"目标目录既有风格"逐文件对齐**(⛔ 不按 HEAD 判):服务器 `web/` 目录实测
`admin.html = CRLF` / `design.css = CRLF` / `i18n.js = LF` / `favicon.svg = LF` ⇒ 推 `admin.html` 前先用
脚本转 CRLF 并断言 `CR 数 == 行数`(本机工作树是 LF,直接 scp 会把整文件行尾翻掉)。
2. **改 i18n 的"改名类"改动必须配断言**:品牌词条漏配/写错是**静默失败**(中文用户看到英文名),
所以 `test/i18n-brand.test.mjs` 用 `node:vm` 跑**仓库里那份真实 `i18n.js`**,断言六条语言解析路径的
实际渲染结果 —— 本轮把期望值从「能力枢纽」改为「能力网络」,跑通即证。
## 四、未做 / 待办
1. 🔴 **`alotbuy.com` 的旧域页面仍会显示旧名?** 不会 —— 旧域已降级为 301 过渡装置(档案 135),
页面由同一份 `/opt/dshs/web/` 直出 ⇒ 旧域访客看到的也是新名。
2. ⚠️ **会话页面左上角**(用户实例内的 dsh 会话窗口)不属本仓 `web/`,由官方 `@deepseek-ai/dsh` 的 client bundle
渲染、受 **R2** 约束 ⇒ 本档不涉及(与档案 137 的范围声明一致)。
3. ⚠️ **词条语义澄清**:用户曾提到「小写 CapabilityNet」;本档按「中文=能力网络/其他语言=CapabilityNet」实现。
若原意是"再加一行小写英文",改动量 = `i18n.js` 一处 + `design.css` 一行(⛔ 不碰 HTML)。
+3 -1
View File
@@ -44,7 +44,7 @@
## 二、全量清单
> **状态摘要**(**机器生成,勿手改**):档案 **121** 份(`04-*`),另含根级编号 5 条(01/02/03/06/07),另有非编号行 14 条(README / INDEX / 技能 / poc 等)—— ✅ 20 | 🔄 5 | 🔍 1 | 📋 23 | 🟡 1 | 未标记 76。复跑 `python3 scripts/docs-index-stats.py` 取数,`--write` 就地刷新本行。
> **状态摘要**(**机器生成,勿手改**):档案 **123** 份(`04-*`),另含根级编号 5 条(01/02/03/06/07),另有非编号行 14 条(README / INDEX / 技能 / poc 等)—— ✅ 22 | 🔄 5 | 🔍 1 | 📋 23 | 🟡 1 | 未标记 76。复跑 `python3 scripts/docs-index-stats.py` 取数,`--write` 就地刷新本行。
> **分层与机读明细**(路径 / 日期 / 字符数 / 被引次数 / tier,可 `jq` 先筛后读):**`docs-manifest.json`**;复跑 `scripts/docs-manifest.py` 即刷新。
> 图例:✅已落地 | 🔄维护中 | 🧪PoC | 📝待开发 | 🔍核查完成 | 📋评估 | 🟡保留兜底 | 🗄归档|🔧修复|🔴|🚧|❓|PoC 实证完成(插件 v0|已上线|生效|已实施|已实施 + API 全链路验|已冻结|已落地 + 验证通过|审计已完成 → 加固项待用户|已上线,页面全部 200|已封板,实施中|核查完成 → 收敛项 H1-|✅ 已实施并在 admin/|🔄 批次 1 因改走 bun|方案 A + handoff|根因已定位并修复(nginx|修复已编译(CI ✅),待重|已实施并上线|已修复并上线(服务已重启|清单建立|✅ 已实施并端到端验证|✅ 已实施并 live 验证|✅ 已实施并视觉验证|🔍 核查完成,P0 待决策|✅ 已实施(commit 见|✅ 已实施(commit `|✅ 已修复(commit `|🔍 取证完成|🔍 核查完成|✅ 已实施(`99de8c5|✅ 已落地|✅ 已修复|已落地并实测验证|已落地并端到端验证|🔄 第一批已修复|✅ 已实施(两批):第一批|✅ 已实施并端到端验证(co|✅ 已实施并验证(服务器已生|admin 侧已实施|✅ 已部署并生效(服务器 `|✅ 已实现、已验证、已部署|✅ 已完成并部署|✅ 全部完成并验证|✅ 已完成(2026-09-|✅ 已止损|✅ 已落地并部署(2026-|✅ 已完成并部署(2026-|✅ 已实现并单点验证|✅ 已落地并实测验证(配置|📋 标准已立|✅ 代码改造 + 组件级验证|✅ 已落地并验证(服务器 `|✅ 已部署并验证(2026-|✅ 已修复并部署|📋 评估中|🚧 执行中(R0 已完成|🚧 代码完成、产物已出|✅ 已完成并上线验证|✅ 已上线并端到端验证(后端|✅ 已修复并验证(源仓 `c|🔄 进行中(L2 机制级已验
@@ -182,6 +182,8 @@
| 04-135 | ✅ | **旧域 `alotbuy.com` 从项目移除**(2026-09-19 落地):代码 / 服务端 / 文档技能三层收敛为 `ai1net.com` 单域;旧域站点**降级为 301 过渡装置**(保留 ACME + `/dshs-relay` + `/dshs-overlay/bootstrap`,**30 天零流量**后退役);relay 候选 **5 → 3**(删掉两条“摘名不同、同落 47”的伪多样性);修 `wake.html` 转义点残留 + 演练脚本硬编码域造成的**假红**;参数表 2 键与 §10 指纹同步 |
| 04-136 | 📋 | **控制面按两台中继取并集 —— 立项交接单**(2026-09-19 立项 · **未落地**):平台 web 层只取 47 中继 ⇒ 106 中继不被使用;根因链 7 行(取址路径 `src/web/server.ts` / `rendezvous.ts`)、**先红后绿**强制、候选 A/B 各带优缺点、验收 E1–E8 + 回滚 |
| 04-137 | ✅ | **品牌标识改造 —— 去 DeepSeek 图形 → 平台自有标识**(2026-09-19 上线):**四页 + favicon 一并替换** —— `login` / `register` / `admin` 页头删 **DeepSeek 鲸鱼图标 +「DeepSeek」文字图形** 改平台标识(中文「**能力枢纽**」/英语及其他语言「**CapabilityNet**」,走 i18n 词条 `brand.name`,`en` 一条即覆盖其他语言回退);`portal` 顶栏换图标(页面名「管理门户」保留);**新建 `web/favicon.svg`**(平台自有 hub 图标,避开 DeepSeek 蓝);🔴 **会话页面左上角那处不动**(用户明示「先不替换」+它属官方 dsh client bundle,受 R2 约束);🔴 踩坑:**SVG 的 XML 注释不得含 ASCII 双连字符 `--`**(写 CSS 变量名即踩)⇒ 整份 SVG 解析失败、图标**静默不显示**,已加进 `verify-static.mjs` 并做反向自证;新增 `test/i18n-brand.test.mjs`|档案 `04-调整方案/137-品牌标识改造-去DeepSeek图形.md` |
| 04-138 | ✅ | **平台共享模型 —— 管理员逐用户授权(默认关闭)**(2026-09-19 上线):新增列 `users.shared_model_granted`(v11,**默认 0**),**生效 = `granted ∧ enabled`** —— 前者 admin 在**用户列表**里逐个开(新增「共享模型」列),后者仍是用户自己的偏好(保留);未授权时设置页**连"平台共享模型"那一块都不渲染**(插件 0.3.24);🔴 **顺带修掉一个既有真缺陷**:落地层原先用**本机 fs** 写用户 home,而用户卷在 worker 上 ⇒ **模型落地对"实例不在本机"的用户一直是静默空操作**(托管清单被清空、目标文件一字节没动;档案 87 的模型设置页对 guest 这类用户从未生效)⇒ 改为走 `UserFs` + worker agent 新增**白名单**端点 `/fs/home-read|write`;两腿实测(未授权撤销 / 授权恢复)|档案 `04-调整方案/138-平台共享模型-管理员逐用户授权.md` |
| 04-139 | ✅ | **品牌中文名:能力枢纽 → 能力网络**(2026-09-19 上线):其他语言仍 `CapabilityNet`(en 词条即回退);落点四处 = i18n 中文词条 / `admin.html` 顶栏 / **`favicon.svg` 的 `<title>`+`aria-label`**(最易漏)/ `design.css` 注释;品牌回归测试期望值同步,`verify-static` + SVG 真解析全绿;🔴 行尾按**目标目录既有风格**逐文件对齐(服务器 `admin.html`=CRLF 而本机=LF,直传会翻掉整个文件)|档案 `04-调整方案/139-品牌中文名改为能力网络.md` |
| — | 🗄 | `archive/dsh-improvement-plan-20260909-full.md`:拆分前 19 章 |
| — | ✅ | `skills/dsh-change-workflow/SKILL.md`:六阶段 + **红线 R1-R11**(工作副本在本机 `.workbuddy/skills/`)|
| — | ✅ | `skills/dsh-decision-method/SKILL.md`:**改造决策方法论**(用户有效决策 U1-U12 / AI 有效决策 A1-A13 / 反例 X1-X8 + 确认最优解十问 + 交互 UI 专项清单)(工作副本在本机 `.workbuddy/skills/`)|
+43 -13
View File
@@ -1,20 +1,20 @@
{
"generatedFrom": "scripts/docs-manifest.py",
"counts": {
"files": 194,
"chars": 2050190
"files": 196,
"chars": 2062355
},
"tiers": {
"hot": 6,
"cur": 25,
"warm": 61,
"cold": 42,
"cold": 44,
"doc": 60
},
"domains": {
"platform": 75,
"platform": 76,
"method": 47,
"ui": 17,
"ui": 18,
"plugin": 27,
"ops": 16,
"?": 8,
@@ -22,7 +22,7 @@
},
"layers": {
"L0": 1,
"L5": 149,
"L5": 151,
"L4": 22,
"L2": 3,
"?": 4,
@@ -218,7 +218,7 @@
"date": "04-10",
"chars": 6261,
"lines": 129,
"refs": 58,
"refs": 60,
"refsCurrent": 4,
"tier": "hot",
"layer": "L5",
@@ -713,7 +713,7 @@
"date": "04-13",
"chars": 2444,
"lines": 58,
"refs": 9,
"refs": 12,
"refsCurrent": 0,
"tier": "warm",
"layer": "L5",
@@ -786,8 +786,8 @@
"title": "137-品牌标识改造(去 DeepSeek 图形 → 平台自有标识)",
"status": "✅",
"date": "09-19",
"chars": 6287,
"lines": 119,
"chars": 6401,
"lines": 122,
"refs": 0,
"refsCurrent": 0,
"tier": "cold",
@@ -795,6 +795,36 @@
"domain": "ui",
"tldr": ""
},
{
"path": "04-调整方案/138-平台共享模型-管理员逐用户授权.md",
"num": "138",
"title": "138 · 平台共享模型 —— 管理员**逐用户**授权(默认关闭)",
"status": "✅ 已上线并端到端验证(控制",
"date": "2026-09-19",
"chars": 8578,
"lines": 148,
"refs": 0,
"refsCurrent": 0,
"tier": "cold",
"layer": "L5",
"domain": "platform",
"tldr": "① 共享模型从「人人默认可用(用户可自关)」改成「**管理员逐用户授权**」——新增列"
},
{
"path": "04-调整方案/139-品牌中文名改为能力网络.md",
"num": "139",
"title": "139 · 品牌中文名:能力枢纽 → **能力网络**",
"status": "✅ 已上线(静态资源直覆,无",
"date": "2026-09-19",
"chars": 2666,
"lines": 56,
"refs": 0,
"refsCurrent": 0,
"tier": "cold",
"layer": "L5",
"domain": "ui",
"tldr": "中文品牌名 **「能力枢纽」→「能力网络」**;**其他语言不变**(仍 `CapabilityNet`,"
},
{
"path": "04-调整方案/14-实例会话敏感信息暴露面审计与加固.md",
"num": "14",
@@ -1898,7 +1928,7 @@
"date": "2026-09-13",
"chars": 14028,
"lines": 218,
"refs": 14,
"refs": 18,
"refsCurrent": 0,
"tier": "warm",
"layer": "L5",
@@ -2181,8 +2211,8 @@
"title": "dsh 平台文档导航(INDEX)",
"status": "?",
"date": "",
"chars": 24468,
"lines": 268,
"chars": 25275,
"lines": 270,
"refs": 0,
"refsCurrent": 0,
"tier": "doc",