dsh-dbhub-live 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +95 -233
- package/README.en.md +96 -11
- package/README.md +97 -12
- package/doc/ARCHITECTURE.md +108 -0
- package/doc/CLIENT.md +124 -0
- package/doc/DESENSITIZE.md +56 -0
- package/doc/DEVELOPMENT.md +139 -0
- package/doc/IMPACT-MATRIX.md +36 -0
- package/doc/RELEASE.md +93 -0
- package/doc/REQUIREMENTS.md +41 -3
- package/lib/client.js +704 -27
- package/lib/config.mjs +3 -0
- package/lib/i18n.mjs +14 -4
- package/lib/index.mjs +107 -0
- package/lib/meta.mjs +267 -0
- package/lib/options.mjs +16 -1
- package/lib/policy.mjs +92 -0
- package/lib/tools.mjs +4 -1
- package/lib/update.mjs +244 -0
- package/package.json +32 -9
- package/scripts/desensitize-check.mjs +331 -324
- package/scripts/desensitize.config.mjs +18 -10
- package/scripts/docs-check.mjs +214 -0
- package/scripts/release-preflight.mjs +180 -0
- package/scripts/render-check.mjs +286 -2
- package/test/client-format.test.mjs +89 -5
- package/test/meta.test.mjs +234 -0
- package/test/options.test.mjs +28 -15
- package/test/policy.test.mjs +132 -0
- package/test/update.test.mjs +270 -0
package/AGENTS.md
CHANGED
|
@@ -1,254 +1,116 @@
|
|
|
1
|
-
# AGENTS.md — dsh-dbhub-live
|
|
1
|
+
# AGENTS.md — dsh-dbhub-live
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本仓库的 AI/维护者入口:**这里只记结论**(规则、常量、命令、门禁)。原因与踩坑过程见 [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md),操作步骤见 [doc/DEVELOPMENT.md](doc/DEVELOPMENT.md)(调试)/ [doc/CLIENT.md](doc/CLIENT.md)(客户端半)/ [doc/RELEASE.md](doc/RELEASE.md)(发布)/ [doc/DESENSITIZE.md](doc/DESENSITIZE.md)(脱敏),改动前要核查的联动清单见 [doc/IMPACT-MATRIX.md](doc/IMPACT-MATRIX.md)。业务需求 [doc/REQUIREMENTS.md](doc/REQUIREMENTS.md),用户文档 [README.md](README.md)。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 定位
|
|
6
6
|
|
|
7
|
-
DSH host 插件(Node ESM)+ Web client
|
|
7
|
+
DSH host 插件(Node ESM)+ Web client 插件:把 dbhub(Bytebase 的数据库 MCP 服务器)接入 DSH。两个性质:**零知识**(DSN/密码只在宿主侧,模型只见 source 句柄与元数据)+ **一次性进程**(每次调用 spawn 一条 dbhub,跑完即杀)。附配置页(设置 → DBHub 数据库工具 / 侧边栏 / 插件页「配置」):连接管理、连接测试、SSH 层独立测试、环境级只读、SSH 隧道、版本与更新检查;5.2 起三套布局(弹窗 / 宽屏 / 小屏)。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 仓库结构
|
|
10
10
|
|
|
11
11
|
```
|
|
12
|
-
lib/
|
|
13
|
-
|
|
14
|
-
bridge.mjs HTTP 桥(纯函数):/api/dsh-dbhub-live 四条 exact Fetch 路由的请求/响应编解码
|
|
15
|
-
config.mjs 路径/原子持久化(store,runtime,prefs)/DSN 解析/工作区发现/**环境选项(readOnly + normalizeSshOptions/sshOf/withDefaultSshKey/DEFAULT_SSH_KEY_PATH/describeEnvOptions)**/零知识助手(describeConn,connLabel,scrubSecrets;**scrubSecrets 折叠任意 URL 连接串的整个 userinfo**)/小工具
|
|
16
|
-
state.mjs 运行时状态机:enabled/phase/toolCount/lastError/mode + 订阅发布(无服务器生命周期)
|
|
17
|
-
options.mjs 可配置参数:updateIntervalDays、**showSidebarEntry**(Config 采纳 > prefs.json > 0.1.x 设置文档 > 环境变量 > 内置默认;dbhubPackage 仅内部环境knob)。**showSidebarEntry 是选项**(用户「隐藏快捷入口」偏好,默认 true;客户端与 enabled 取交集决定侧边栏入口)
|
|
18
|
-
runtime.mjs dbhub 可执行文件发现、按需自动安装、定期自动更新(已删除孤儿进程清理)
|
|
19
|
-
capability.mjs 上游能力/版本探测(文件级,不 spawn):`[[tools]] readonly` 与 `ssh_*` 需要 1.4.0+,低于 1.0.0 明确拒绝
|
|
20
|
-
toml.mjs 唯一的一次性 dbhub TOML 生成器:buildDbhubToml/writeTempToml/removeTempToml/sweepTempToml(**文本绝不含秘密**)
|
|
21
|
-
mcp.mjs MCP JSON-RPC 客户端 + 工具注册表 + 工作区×环境来源发现(collectSources/resolveSource)+ 摘要缓存 + 测试快路径(resolveTestDsn/cachedRows)
|
|
22
|
-
adhoc.mjs 唯一执行引擎:每次调用一条一次性 dbhub 进程(连接描述符 {dsn,ro,ssh}、--dsn 或一次性 TOML、零知识标签 + stderr 清洗)+ **SSH 层独立探测(probeSshTunnel:先经 withDefaultSshKey 把"密钥认证 + 路径留空"补成 DEFAULT_SSH_KEY_PATH = ~/.ssh/id_ed25519 并附 result.sshTestDefaultKey 说明,再做 TCP 可达 + 私钥文件 + 以 127.0.0.1:1 为目标的隧道尝试;判定顺序固定 = SSH 层特征(SSH_LAYER_RE) 单独定案 → 数据库层证据(DB_LAYER_RE) → 指名转发目标 → 都没有才如实 unknown)** + **诊断尾部与分层(diagnosticTail、DIAG_TAIL_MAX=600、丢掉 at 栈帧及其折行残片、按行边界截断、failureLayerOf)**
|
|
23
|
-
collect.mjs 授权扫描项目配置文件并提取 DSN 候选(含 askUser 桥接)
|
|
24
|
-
tools.mjs host 自有工具定义与注册:恒定 4 个(configure/list_sources/execute_sql/search_objects);configure 探连优先(见零知识契约 5)
|
|
25
|
-
client.js Web 半(手写 lazy-CJS bundle):配置页 = `settings.section` 一级设置页 + 侧边栏面板(**`enabled && showSidebarEntry`**)+ `plugins.row.config`(summary/page 两视图);host face 按运行时探测(settingsScope 或 HTTP 桥);环境级只读开关 + 高级 SSH 折叠区(含「测试 SSH 隧道」)+ 设置条侧边栏开关
|
|
26
|
-
test/ node:test 单元测试(纯逻辑 + bridge 契约 + client bundle 格式契约 + 零知识泄漏门)
|
|
27
|
-
scripts/ 仓库维护脚本:desensitize.config.mjs(**脱敏槽位表 = 单一事实源**)、desensitize-check.mjs(脱敏门禁)、render-check.mjs(客户端布局离屏渲染/交互门禁,无需 React 依赖)
|
|
28
|
-
.github/ CI:push/PR 跑语法检查 + 脱敏门禁 + 单测
|
|
29
|
-
doc/ REQUIREMENTS.md(业务需求,**进 git 与 npm 包**)+ images/(README 截图)+ **仅本地开发用的两份文档**:PLAN-5.1-readonly-and-ssh-tunnel.md(5.1 实施计划)与 implementation-notes.md(实施记录)——后两者被 `.gitignore` 排除、不进 git、不进 npm 包,只服务于本仓库的开发与调试
|
|
30
|
-
.env 本地脱敏黑名单(DSH_DBHUB_DESENSITIZE_RE),gitignore 排除、不进 npm 包——**唯一允许存放真实标识的地方**
|
|
31
|
-
cordis.patch.yml bundle patch:`name: dsh-dbhub-live` 挂载本包
|
|
12
|
+
lib/ host + 客户端实现(模块地图见 ARCHITECTURE)| test/ 单测 | scripts/ 门禁脚本
|
|
13
|
+
release-test/ 版本矩阵实测 | doc/ 文档 | .github/ CI | cordis.patch.yml bundle patch | .env 本地脱敏黑名单
|
|
32
14
|
```
|
|
33
15
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
**取 bundle URL 的正确方式**:index HTML 里嵌着模块清单 JSON(`{"id":"dsh-dbhub-live","url":"/plugins/??…&rev=<rev>"}`),**rev 带 `-<n>` 后缀**(如 `d4aeeae7c6c2a859-55`,每次渲染会变);用正则从组合列表里截 rev 会截断后缀而 404,必须整段取出该 `url` 再请求。
|
|
109
|
-
**0.1.x 线(双栈的另一半)**:另起一个 scratch home 装 0.1.6 的 CLI 跑一遍即可,`dsh web` 会自己初始化 web 模板:
|
|
110
|
-
```powershell
|
|
111
|
-
# 用 0.1.6 的 CLI 初始化 DSH_HOME=.dsh-b,然后把包目录拷进 profiles/web/node_modules/
|
|
112
|
-
# 并在 package.json 的 dsh.profile.bundles 里加上 dsh-dbhub-live(bundle 的 patch 会自行插入插件行)
|
|
113
|
-
# 期望日志:`host apply` + `dbhub 插件已加载(4 个工具)` + `状态命名空间已注册: dsh-dbhub-live`
|
|
114
|
-
# + `浏览器桥接已注册: /api/dsh-dbhub-live/*`(0.1.6 没有 settings.configure,故没有"配置表单策略"那行)
|
|
115
|
-
# 期望接口:/api/dsh-dbhub-live/state 带 cookie 200、不带 401;view 里 entryId=行 id;bundle 200
|
|
116
|
-
```
|
|
117
|
-
`mise r dsh plugin … ls` 只用于核对安装版本;沙箱里 pnpm 的 store 可能不可写,见上面手工路径。
|
|
118
|
-
4. **在位热更新(有失败保护,谨慎)** — 主实例的 `cordis.patch.yml` 支持 HMR,读取/解析失败时保留最后一个可用树,GUI 不会挂。但 HMR 不监听插件源码,只适合挂载/卸载验证,代码迭代请用 1/2/3。
|
|
119
|
-
5. **安全网** — `$DSH_SNAPSHOT=replay` 可从 `cordis.snapshot.yml` 启动回放点;改动主 profile 前先备份 `cordis.patch.yml`。
|
|
120
|
-
|
|
121
|
-
## 修改 client 半(lib/client.js)
|
|
122
|
-
|
|
123
|
-
`lib/client.js` 是**手写 lazy-CJS bundle**,DSH client 模块系统按 `dsh.client` 声明 + `exports["./client"]` 直接服务它,**没有构建步骤**。遵守以下契约(改完跑 `node test/client-format.test.mjs` 守护):
|
|
124
|
-
|
|
125
|
-
- 必须以 `window.__ModuleLoader__.load({ id: "dsh-dbhub-live", factory: (require) => { … } })` 注册;
|
|
126
|
-
- `id` 必须等于 Loader entry 名(`dsh-dbhub-live`,见 cordis.patch.yml 的 `name`),不是行 id;
|
|
127
|
-
- factory 只允许 `require("react")` 等 baseline 模块;不 import 其他插件的值(bundle-purity);
|
|
128
|
-
- 导出 `name/inject/apply`,结尾 `return module.exports`;
|
|
129
|
-
- 注册的 slot 必须是 `plugins.row.config`(key = `dsh-dbhub-live#dbhub-live`;见「状态通道」条),组件同时服务 `props.view === 'summary'`(一行摘要)与 `'page'`(完整表单,页面自己画标题/图标/面包屑);**另外必须注册 `settings.section`(id `dbhub`,order 40)作为主入口**——设置面板是核心 shell,任何部署都在,用户也在那里找连接管理;`plugins.row.config` 只在官方插件页的浏览器半区活跃时存在,不能作为唯一入口。侧边栏快捷入口(`sidebar.panellist` id `dbhub` + `main` key `dbhub`,组件 `PanelIcon`/`ConfigPanel`)由**两个条件同时成立**决定(F0 修正):`sidebarWanted()` = `view.enabled !== false && view.showSidebarEntry !== false`,`syncSidebar()` 挂在 `face.subscribe` 上按当前 view 即时 register/dispose。**「隐藏入口」与「禁用插件」是两件事**:关掉开关只是不显示入口、插件照常工作(工具仍对模型可用);禁用则一律不贡献入口。两种状态都**不得自锁**:`settings.section` 与 `plugins.row.config` **始终注册**(否则从设置页也进不去)。**`showSidebarEntry` 全链路已恢复**(`options.mjs` 默认值 true/snapshot/VALIDATORS、`normalizePrefs` 布尔读回、`STATUS_DEFAULTS`/`STATUS_SCHEMA`/`Config` volatile 字段、客户端字典与设置条复选框、draft),默认开启;它在 dsh 0.1.7+ 同样作为声明式配置暴露。**开关回来时必须同步翻转 `scripts/render-check.mjs` 里 F0 时代的断言 `strip no longer carries a sidebar switch`(改成"设置条重新带开关"),否则 `check:ui` 会直接红。** 回归门:`npm run check:ui` 第 19 组(启用 ∧ 开关 / 禁用一律隐藏 / 设置页始终在)+ `node test/options.test.mjs`。**HTTP 通道的轮询是「按订阅」的**:只有渲染中的页面用 `props.subscribe(fn, { live: true })` 拉起 1.5s 轮询(`document.hidden` 时跳过),插件作用域那次订阅(`face.subscribe(syncSidebar)`)只做一次性拉取——空闲的 GUI 不得持续轮询桥接(曾经漏掉这个区分,`npm run check:ui` 会因残留 interval 挂住不退出)。
|
|
130
|
-
- **host face 的探测与写回(源代码级契约,别退回 inject)**:`export const inject` 只能有 `["slots"]`;`legacyFace(scope)` 与 `httpFace()` 实现同一接口(`getSnapshot/subscribe/setEnabled/saveConfig/configOp`,快照形状 `{ status, value }`),`ctx.get('settingsScope')` 存在时优先 legacy。HTTP face 的四个入口分别是 `GET /state`、`POST /enabled`、`POST /options`、`POST /op`,写完用响应里的 view 直接刷新快照(不等下一次轮询);`configOp` 的 `{op: …}` 对象直接 POST,不再走「写字符串字段再等主机清空」的绕路。**选项写入优先走官方 settings 表单**:Host 在 view 里发布 `entryId`,客户端有 `ctx.configForms` 且 entryId 非空时用 `configForms.get(entryId).set(field, value)`,失败或解析不到 entryId 才回退 `POST /options`(0.1.x 与没有 settings 客户端的部署走这条)。回归门:`node test/client-format.test.mjs` + `npm run check:ui` 第 18 组(无 settingsScope 时走桥接、四个写入口、`configForms` 优先与 entryId 缺失时的回退、轮询可回收)。
|
|
131
|
-
- 组件不得接触 `ctx`,数据/回调一律通过注册时的 `inject: () => face` 传入 props;**样式改为 `apply()` 里 `ensureStyle()` 注入一份作用域样式表(`#dsh-dbhub-live-style`,类名前缀 `dbh-`)**——悬浮/焦点态是伪类,内联样式表达不了;颜色一律用 `--dsw-alias-*` 主题 token + 中性兜底(深色/skin 切换不串色),只在动态取值时用内联样式。**不要把样式退回成一堆内联对象。**
|
|
132
|
-
- **`apply()` 必须能被重复执行(客户端 HMR 会重跑整个 bundle)**:`localeSvc.register(NS, …)` **不是幂等**——同一 namespace+locale 再注册会抛 `locale namespace … already has locale zh`,客户端半区会整块同步失败(页面提示「插件未能完成同步」)。因此:① 捕获 register 返回的 disposer 并用 `ctx.effect(() => dispose, …)` 挂在插件作用域上(`ctx.effect` 是客户端插件的标准清理口,`ctx.slots.inject` 自身已按作用域清理,沿用即可);② 注册失败时 **降级到 `localTranslator(localeSvc)`**(读本 bundle 自己的字典 + `localeSvc.getLocale().active`),绝不让重复注册打断整个插件半;③ 新增/修改 `apply()` 里的任何服务注册都要问一句「reload 第二次还会成功吗」。回归门:`npm run check:ui` 第 14 组(重复注册 + dispose 后再注册 + 降级后文案仍是本地化文本)。
|
|
133
|
-
- 配置页渲染的 `workspaces` 行只显示 `w.conn`(元数据标签),**不得显示/回显 DSN**;改名走 `configOp({op:'rename'})`,改名+改连接走**一条** `{op:'add', renameFrom}`(`configOp` 是单字段,连续两次写会互相覆盖)。
|
|
134
|
-
- **布局契约(4.2 迭代沉淀,别再退回「每行三到四行 + 常显表单」)**:
|
|
135
|
-
- **按 `path` 分组**(`groupRows`,标题不是身份:同名工作区是不同目标,同名组头用路径消歧),`layoutModeOf` **无阈值**判定:所有工作区都只有 1 个环境 → **平铺网格**(`dbh-tiles`,`minmax(280px,1fr)` + 内容列 `max-width:960px` 自然封顶 3 列);只要有任一工作区是多环境 → **分组折叠**(`dbh-group` + 可点击组头 `aria-expanded`)。判定必须是纯函数,改判定先改 `scripts/render-check.mjs` 的断言。
|
|
136
|
-
- **每个连接只占一行**:`环境 chip(窄、淡) · 🔒 连接元数据 · source chip · [▶][✏][🗑] 图标按钮`。三条硬规则:
|
|
137
|
-
① 行内操作**只允许图标**(`primitives` 的 `IconPlayOutline16` / `IconEditOutline16` / `IconCloseOutline16` / `IconTrashOutline16`,无边框 24px,label 走 `title`/`aria-label`)——文字按钮一放上来就吃掉半行,这是被用户点名过的;
|
|
138
|
-
② `source` 句柄做成**可复制 chip**(`sourceChip`):虚线边框 + 复制图标 + **短句柄**(`shortHandle`:≤14 字全显,否则保留"工作区哈希 + 环境段"两段),让人一眼看出"这是 id、点了复制",而不是"复制整行连接串";全文与用途进 tooltip(`ws.srcHint`),复制走 `primitives.writeClipboard`(自带 execCommand 兜底);
|
|
139
|
-
③ 连接元数据用 `connLabelView` 拆成 `🔒 + 前缀(type://host:port/) + 库名`,**前缀可截断、库名绝不截断**(`dbh-conn-pre` 省略号 / `dbh-conn-db` 不收缩)——"窄行里看不到库名"是最常见的抱怨。
|
|
140
|
-
行内只允许出现 `w.conn`,`srcId` 只是句柄、绝不回显 DSN。**「来源」(手填/扫描/复制/自动发现)不得做成行内徽章**——它几乎每行都一样、却是最抢眼的元素;改为环境名 chip 的 tooltip(`originTitle`),只有"自动发现且未持久化"这一例外在 tooltip 里显式点名。
|
|
141
|
-
- **层级**:工作区分组是 level-1(边框卡片 + 极淡阴影),环境行是 level-2(缩进 + 左侧引导竖线,见 `dbh-groupbody{border-left}`);两层绝不能长得像同一层。
|
|
142
|
-
- **主题适配(红线)**:样式表**只允许 `--dsw-alias-*` token**,兜底值必须中性且半透明(`rgba(127,127,127,.3)`、`transparent`),**禁止任何不透明色字面量**(`#ffffff`/`#f6f8fa`/`#1f2328`/品牌色…)——用户会装主题/皮肤插件(本机就有 skin-center,且背景可能是半透明的),写死白底或固定浅灰字会直接看不清;`test/client-format.test.mjs` 有"tokens only"断言门禁。对比度:正文及以上用 `label-primary`/`label-secondary`,`label-dimmed` 只留给极次要说明。
|
|
143
|
-
- **默认展开**:单个工作区自动展开;多工作区时展开「最近活跃工作区」(由 `settings.section` 标准 prop `useWorkspaces` 的 `WorkspaceSnapshot.updatedAt` 推出——设置面板是全局的,拿不到"当前会话"选中态,别去猜);取不到快照就全折叠。另有「全部展开/折叠」总控。展开态、添加面板、设置条存在模块级 `uiMemory`(同一页面会话内跨重挂载保留,**不持久化**——这是视图状态,不是配置)。
|
|
144
|
-
- **顶部只留一行**:状态徽章 + 工具数 + 设置条摘要 + `[⚙ 设置]` + `[启用/禁用]`;自动更新间隔与**「在侧边栏显示入口」复选框**一起收进默认收起的设置条(入口显隐 = `enabled && showSidebarEntry`,见 F0)。**添加连接默认收起**:「+ 添加连接」(全局,工作区用 `<datalist id="dbh-ws-list">` 给已有工作区)/ 分组头「+ 环境」(工作区自动带入,少一个字段);保存仍走原有「试连 → 失败才确认」门禁。
|
|
145
|
-
- **环境选项的落点(5.1,遵守既有布局红线)**:只读是**逐行不同**且改变语义的属性,所以做成环境 chip 内的 `span.dbh-ro`(文案 `只读`/`RO`)并把说明并入 chip 的 `title`;SSH 只影响路由,**不做徽章**,只进同一个 tooltip(`ro.sshTitle`)。开关是 `.dbh-checkbox` 复选框(**不用 `.dbh-input`、不包进 `.dbh-field`**),因此 `render-check` 的 `dbh-field === 3` 与 `typeWithin('dbh-addpanel',0..2)` 索引都不变;SSH 高级区是 `.dbh-adv` 折叠面板,**必须排在 DSN 输入之后**(同上索引约束),折叠态放 `uiMemory.advOpen`;区内「测试该配置」旁是**「测试 SSH 隧道」**(独立按钮/独立回执,只验 SSH 层,见 adhoc 条与 R64)。秘密输入框 `type="password"`、占位符写明"留空 = 保持原值",**编辑态绝不回显已存的密码/口令/密钥路径**(镜像行只给 `authKind`/`hasPassword`/`hasPassphrase`/`keyReady` 布尔)。回归门:`npm run check:ui` 第 20/21/23 组(第 23 组 = SSH 层独立测试按钮:载荷形状、自带 nonce、不带 dsn、测试中计时、二次点击被忽略、dispose 清掉看门狗)。
|
|
146
|
-
- **写入载荷统一走 `connDraftOf(form)` / `withConnDraft(op, form)`**:只读开关与隧道随 `add`/`test`/`options` 三种 op 一起发出,绝不单独再发一条(`configOp` 在 legacy 通道是单字段,两次写会互相覆盖)。表单对象在产生 op 时会带 `_draft` 指回原始草稿,别绕过这两个函数手拼 `readOnly`/`ssh`。
|
|
147
|
-
- **测试中显示已耗时**:`startTest` 记 `startedAt`,`testing` 态渲染 `test.elapsed`(`测试中… Xs`),1s 计时器**只在有行处于 testing 时存在**(effect 依赖 `anyTesting`,cleanup 清掉)——空闲不轮询、不残留 interval 是硬红线,`check:ui` 会因残留 interval 挂住不退出(第 22 组就是这个门)。看门狗按是否隧道分档(30s/60s),必须高于宿主预算。
|
|
148
|
-
- 布局/交互改动的验收 = `npm run check:ui`(离屏渲染 + 点击交互,涵盖模式判定、默认展开、全部展开/折叠、添加面板、复制按钮、中英 i18n 覆盖率)全绿 + `node test/client-format.test.mjs`。
|
|
149
|
-
- **复制必须给出干净字符串**:连接串被拆成两个 flex item 渲染后,浏览器自己的复制序列化会在块级子元素之间插分隔符(粘进 DSN 输入框就成了 `…3306/ app`,测试直接失败)。因此 `connLabelView` 的元素自己接管 `onCopy`(`e.clipboardData.setData('text/plain', w.conn)` 并 `preventDefault()`)——挂在**连接串元素**而不是整行上,复制环境名不受影响;`w.conn` 本身是元数据,不涉及凭据。回归门:`npm run check:ui` 第 17 组。
|
|
150
|
-
- **等待态不给二次提交**:预检进行中(`pendingPretest`)保存/添加按钮 `disabled`,避免重复派发同一个测试。真实的连接探测本来就要几秒(要 spawn dbhub),但**写入结果必须立刻可见**——这靠宿主侧的即时回推(见「工作区连接管理」条的 `publishFast`),不要改回"等整轮扫描"。
|
|
151
|
-
- 若本包脱离仓库(发布 npm),`dsh.client` 与 `exports["./client"]` 必须保留,否则 client-modules 扫描会启动报错。
|
|
16
|
+
模块一览、各文件职责在 [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md#模块地图)。
|
|
17
|
+
|
|
18
|
+
## 红线(结论)
|
|
19
|
+
|
|
20
|
+
### 零知识
|
|
21
|
+
- 工具参数**不得有 `dsn`**;`dbhub_configure` 收到 `args.dsn` 直接拒绝(`result.noDsnViaModel`)。
|
|
22
|
+
- 模型可见文本(结果/列表/标签/错误/stderr/诊断)一律过 `connLabel` 或 `scrubSecrets`;清洗要折叠任意 URL 连接串的整个 userinfo → [原因](doc/ARCHITECTURE.md#零知识边界)。
|
|
23
|
+
- 密码与完整 DSN 只经界面:`askUser`(configure)/ `configOp`(配置页)。门禁 `test/zero-knowledge.test.mjs`。
|
|
24
|
+
|
|
25
|
+
### 一次性进程
|
|
26
|
+
- `resolveSource` 解 DSN → `runAdhoc` 起一条 `dbhub --transport stdio`,跑完 terminate。**无常驻服务**(无指纹 / mtime / 空闲回收 / respawn / 孤儿清理 / 配置重启)→ [原因](doc/ARCHITECTURE.md#一次性进程的理由)。
|
|
27
|
+
- 工具恒 4 个:configure / list_sources / execute_sql / search_objects;不为工作区×环境注册工具。
|
|
28
|
+
- `apply()` 只注册工具 + `startBackgroundBoot` bookkeeping;dbhub 解析发生在每次调用(`runtime.json` 缓存);无数据源照常启动。
|
|
29
|
+
|
|
30
|
+
### 执行路径
|
|
31
|
+
- `ro === true || ssh != null` → 临时 `--config`;其余走 `--dsn`。
|
|
32
|
+
- TOML 里秘密只写 `${DSH_DBHUB_SEC_*}` 占位符,真值走子进程 env;**引用集合 == env 键集合**(`test/toml.test.mjs`)。
|
|
33
|
+
- `[[tools]]` 是白名单:只读必须写**两条** → [原因](doc/ARCHITECTURE.md#一次性toml的由来)。
|
|
34
|
+
- 版本门槛:1.4.0 起实测;`< 1.0.0` 拒绝(`result.dbhubTooOld`);不生成未实测写法。
|
|
35
|
+
- 临时文件在 `$DSH_HOME/storages/dsh-dbhub-live/tmp/`:`finally` 尽力删 + boot 时 `sweepTempToml()`。
|
|
36
|
+
|
|
37
|
+
### 环境选项(只读 / SSH)
|
|
38
|
+
- store:`credentials.json` 顶层 `{ [wsPath]: { environments: { [env]: { dsn, source, updatedAt, readOnly?, ssh? } } } }`;`ssh = {host,port,user,auth,keyPath?,passphrase?,password?,proxyJump?}`。
|
|
39
|
+
- `setWorkspaceEnv(..., opts?)`:opts 缺省**保留** readOnly/ssh;`{readOnly:false}` / `{ssh:null}` 才是删除。`setWorkspaceEnvOptions` 只改选项不碰 DSN。
|
|
40
|
+
- 模型传 `readOnly:true` 允许、`false` 拒绝(`result.readOnlyUserOnly`,拦截在 `runConfigure`)。
|
|
41
|
+
- `handleConfigOp`:`add` / `set` / `rename` / `remove` / `test` / `options`;`options` 可带 `newEnv`(改名+改选项原子);行不存在时提升为持久化行(`source:'promoted'`)。实现在 `applyOptionsOp`。
|
|
42
|
+
- 秘密字段「留空 = 保持原值」;`auth==='key'` 且无已存路径时落 `DEFAULT_SSH_KEY_PATH = '~/.ssh/id_ed25519'`;载荷统一走 `connDraftOf` / `withConnDraft`。
|
|
43
|
+
- 描述符 `{dsn, ro, ssh}`:`probeConnection` 验有效连接,`probeSshTunnel` 只验 SSH 层(`SSH_LAYER_RE` → 断;否则 `DB_LAYER_RE` / 指名转发目标 → 通;都没有 → unknown)。
|
|
44
|
+
- 预算 `PROBE_TIMEOUT_MS = 25s` / `PROBE_TIMEOUT_SSH_MS = 45s` < 客户端看门狗 30s/60s;宿主兜底 = 预算 + 500ms → [原因](doc/ARCHITECTURE.md#环境选项与执行预算)。
|
|
45
|
+
- UI 落点:现代面行内 `.dbh-rochip`(点击发 `{op:'options', readOnly}`),legacy 面 `.dbh-ro`;SSH 只进 tooltip。见 [doc/CLIENT.md](doc/CLIENT.md)。
|
|
46
|
+
|
|
47
|
+
### source 身份
|
|
48
|
+
- 每条 source 只属于一个工作区,同名不同区是不同目标;`sourceIdOf(row)` 是唯一构造点(`<标题slug>_<路径哈希>[_<envSlug>]`)。
|
|
49
|
+
- `envSlug`:`default` 裸值 / 纯 ASCII 原 slug / 非 ASCII `env-<shortHash>` → [原因](doc/ARCHITECTURE.md#envslug事故)。
|
|
50
|
+
- `resolveSource`:精确 → 当前会话工作区 → `{ambiguous}` 澄清,**不按列表顺序猜**。
|
|
51
|
+
- `dbhub_configure` 不回退 `workspaces[0]`(`result.unknownWorkspace` / `result.needWorkspace`);跨区复用走 `copyFrom`。
|
|
52
|
+
|
|
53
|
+
### 状态通道
|
|
54
|
+
- 一个发布核心 `createHostCore`,两条通道:0.1.x settings 命名空间(仅 `settings.register` 存在时)/ `connection.fetch` 的 `/api/dsh-dbhub-live/{state,enabled,options,op}`。
|
|
55
|
+
- 客户端 `inject` **只能 `["slots"]`**;host face 用 `ctx.get('settingsScope')` 探测 → [为什么](doc/ARCHITECTURE.md#状态通道与inject根因)。
|
|
56
|
+
- 注入回调内服务必须 `ctx.get(...)` 就地取;legacy 回调 `wrap()` 防护。
|
|
57
|
+
- view:`{enabled, phase, toolCount, lastError, mode}` + `{updateIntervalDays, showSidebarEntry, announceAccessPolicy, capabilities, workspaces, testResult}` + `{pluginVersion, dshVersion, dshModern, runtime, update}`;legacy 额外 `configOp`。**DSN 只从浏览器流向 Host。**
|
|
58
|
+
|
|
59
|
+
### 部署开关与连接管理
|
|
60
|
+
- `lib/options.mjs` 唯一持有开关;Config 值经 `options.configValuesOf` → `applyPatch(..., {persist:true})`;`entryIdOf(ctx)` 运行时解析行 id;不绕过 `applyPatch`。
|
|
61
|
+
- 页面只见元数据(`collectSources` → `latestSummaries`);增删改改名走 `configOp`;`renameFrom` 让「改名 + 改连接」原子。
|
|
62
|
+
- 写前先问(`requestWrite` / `envCollision` / `normalizeEnvForCompare` → `dbh-warncard`)→ [原因](doc/ARCHITECTURE.md#覆盖门为什么先问)。
|
|
63
|
+
- 即时回推:`handleConfigOp` 返回补丁并 `publishFast`;`reportTest` 只 `publishMirror`;扫描单飞 + 补丁重放;`rename` 补丁幂等 → [原因](doc/ARCHITECTURE.md#即时回推与扫描单飞)。
|
|
64
|
+
- test op 三档快路径 `resolveTestDsn`(store → 卡片缓存 → walk);失败不写 store/lastError/phase;`kind:'ssh'` 只验 SSH 层。
|
|
65
|
+
- 诊断取尾部 600(`diagnosticTail`),前缀 `【SSH 层】/【数据库层】/【插件层】`;页面只认自己派发的 nonce → [原因](doc/ARCHITECTURE.md#诊断为什么取尾部)。
|
|
66
|
+
|
|
67
|
+
### 版本与更新
|
|
68
|
+
- `dshModern` = DSH >= 0.2.1(含预发布);解析不到 → desktop true / web false → [原因](doc/ARCHITECTURE.md#版本门与更新检查)。
|
|
69
|
+
- `{op:'updateCheck'}` 在 `runOp` 中先于 `handleConfigOp` 拦截;不改行、不写 store。门禁 `test/meta.test.mjs` / `test/update.test.mjs`。
|
|
70
|
+
|
|
71
|
+
### 自动发现(mise)
|
|
72
|
+
- 每次探测必须有总超时(`MISE_ENV_TIMEOUT_MS`;`graceMs` 不是超时);结果按 `AUTO_DSN_TTL_MS` 缓存;工作区之间并发。
|
|
73
|
+
- **预算计时器不 `unref()`** → [原因](doc/ARCHITECTURE.md#自动发现的代价与unref教训)。
|
|
74
|
+
- `resolveWorkspaceRef` 必须用与扫描同一个 `listWorkspaces(ctx)`。
|
|
75
|
+
|
|
76
|
+
### 其他不可退化
|
|
77
|
+
- 依赖只进不出:`config ← state ← mcp`;`config ← runtime|toml|capability ← adhoc`;`mcp ← tools ← index`;`adhoc|collect ← tools`;`state|options ← policy ← index`;`meta`/`update` 是叶子。禁循环 import。
|
|
78
|
+
- schema 弹性:`await import('@deepseek-ai/schemastery')` 失败 → 内建最小 schema;不改回静态顶层 import。
|
|
79
|
+
- `policy.mjs` 唯一 system prompt 段(order 8900):已配置连接走 `dbhub_*`、禁 CLI/脚本/驱动/其它 MCP/其它数据库插件绕过、只读被拒不换途径;不支持的库让给用户其它插件、判不准先问。`announceAccessPolicy=false` 时 `text` 为空。门禁 `test/policy.test.mjs`。
|
|
80
|
+
- 启用/禁用:`state.setEnabled` → `credentials.json`;禁用时工具首行返回「插件已禁用」。
|
|
81
|
+
- 存储:`$DSH_HOME/storages/dsh-dbhub-live/`(credentials / prefs / runtime / dbhub-runtime / tmp);`writeJsonFile` tmp+rename,失败只 `warnOnce`;load 时清洗 + 一次性回写迁移,**未知字段保留**;进程环境变量不参与连接解析。
|
|
82
|
+
|
|
83
|
+
### 客户端半(细节见 [doc/CLIENT.md](doc/CLIENT.md))
|
|
84
|
+
- 手写 lazy-CJS bundle,无构建;`id` = Loader entry 名;三入口都要在:`settings.section`(始终)/ 侧边栏(`enabled && showSidebarEntry`)/ `plugins.row.config`(始终)。
|
|
85
|
+
- `apply()` 可重入:locale 注册失败降级 `localTranslator`。
|
|
86
|
+
- 样式只允许 `--dsw-alias-*` token + 中性半透明兜底;组件不接触 ctx。
|
|
87
|
+
- 行内只显示 `w.conn`;操作只用图标;`source` 可复制 chip;库名不截断 → [原因](doc/ARCHITECTURE.md#布局取舍)。
|
|
88
|
+
- 空闲不轮询(残留 interval 会让 `check:ui` 挂住不退出)。
|
|
152
89
|
|
|
153
90
|
## 质量门
|
|
154
91
|
|
|
155
92
|
```bash
|
|
156
|
-
npm run check
|
|
157
|
-
npm run check:secrets
|
|
158
|
-
npm run check:ui
|
|
159
|
-
|
|
160
|
-
node test/util.test.mjs && node test/state.test.mjs && node test/options.test.mjs \
|
|
161
|
-
&& node test/init.test.mjs && node test/i18n.test.mjs \
|
|
162
|
-
&& node test/resolve-source.test.mjs && node test/resolve-workspace.test.mjs \
|
|
163
|
-
&& node test/row-cache.test.mjs && node test/auto-dsn.test.mjs \
|
|
164
|
-
&& node test/bridge.test.mjs \
|
|
165
|
-
&& node test/toml.test.mjs && node test/capability.test.mjs && node test/env-options.test.mjs \
|
|
166
|
-
&& node test/adhoc-toml.test.mjs && node test/configure-options.test.mjs \
|
|
167
|
-
&& node test/configure-ssh.test.mjs && node test/copy-from.test.mjs \
|
|
168
|
-
&& node test/test-op-fastpath.test.mjs && node test/ssh-probe.test.mjs \
|
|
169
|
-
&& node test/client-format.test.mjs && node test/zero-knowledge.test.mjs
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**CI(`.github/workflows/quality.yml`,GitHub Actions 的 `quality` workflow)**:`push` 到 `main` 与 `dev`、所有 PR、以及手动 `workflow_dispatch` 都会跑同一个 job **`gate`**(`ubuntu-latest` + Node 22),六步 = 语法检查 → 脱敏门禁 → 客户端布局门禁 → 逐个单测(21 个文件;5.1 新增 9 个)。`main` 的分支保护把 `gate` 设为必需检查。**CI 是真实执行**:它在 5.0.0 上真的跑挂过(`test/auto-dsn.test.mjs` 全部 cancelled),这正是它存在的意义——**本地 Windows 会掩盖只影响 Linux 的缺陷**(见「自动发现的代价与边界」条的 `unref` 教训),所以"本地全绿"不能当作发布依据,必须看 CI。
|
|
173
|
-
|
|
174
|
-
### 脱敏门禁(`npm run check:secrets`)
|
|
175
|
-
|
|
176
|
-
真实主机/账号/密码/库名/项目名只允许存在于两处:`$DSH_HOME/storages/dsh-dbhub-live/credentials.json`(仓库外)与本地 `.env`(`.gitignore` 排除、`package.json` 的 `files` 不含,永不进 git 历史/公开仓库/npm 包)。仓库内一切内容都视为会公开分发。
|
|
177
|
-
|
|
178
|
-
门禁由 `scripts/desensitize-check.mjs` 执行,规则与槽位来自 `scripts/desensitize.config.mjs`:
|
|
179
|
-
|
|
180
|
-
1. **槽位规则(主力,高精度)**:槽位表登记"真实值可能出现的出口"(宿主文案、浏览器文案、连接构造代码、扫描器、测试夹具、文档示例、包元数据、本提示词、维护脚本),每个槽位声明它需要的规则(`password`/`dsn`/`host`/`name`)。**槽位 glob 必须匹配到文件,否则门禁报 `slot-stale`**——槽位表不会随目录移动悄悄失效。
|
|
181
|
-
2. **通用兜底(覆盖未登记的新出口)**:`ip`(只允许回环、`0.0.0.0` 与 RFC 5737 文档段 `192.0.2.x`/`198.51.100.x`/`203.0.113.x`)、`secret`(GitHub/npm/OpenAI/Slack/AWS/JWT/PEM 等真凭据格式)、`entropy`(大小写/数字混排的高熵串)、`host`(`.local`/`.internal`/`.corp` 等内网后缀)、`name`(本地 denylist)。
|
|
182
|
-
3. **本地 denylist(补充)**:pattern 抓不到的**裸名字**(项目/公司/库名)放在 `.env` 的 `DSH_DBHUB_DESENSITIZE_RE`(`|` 分隔、正则转义、多条取并集)。CI 里没有 `.env`,该层自动降级为提示,不影响其余两层。
|
|
183
|
-
|
|
184
|
-
**约定(新增内容一律照此写)**:示例主机用 `127.0.0.1`/`localhost`/RFC 5737 段;示例密码用 `CHANGE_ME`;测试夹具可用短占位(`u:p`、`root:secret`),但主机仍须回环/保留段;误报走行内标记 `desensitize:allow`(仅限确认无害的行)。
|
|
93
|
+
npm run check # 语法:全部 lib + client.js
|
|
94
|
+
npm run check:secrets # 脱敏门禁(槽位 + 兜底 + 本地 denylist)
|
|
95
|
+
npm run check:ui # 客户端离屏渲染 + 点击交互
|
|
96
|
+
npm run check:docs # 文档链接/锚点、影响矩阵路径、包内文档一致性
|
|
185
97
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
零知识自检(模型可见面不得含密码):`node test/zero-knowledge.test.mjs` 全绿 + 上面 `npm run check:secrets` 零匹配。
|
|
189
|
-
|
|
190
|
-
发布前:按「调试方法」第 3 步在隔离实例完整冷启动一遍,确认 Host 无报错、`/api/dsh-dbhub-live/state` 带会话 cookie 返回 200(不带 cookie 返回 401)、`/plugins/??<包名>/client.js&rev=…` 可访问。
|
|
191
|
-
|
|
192
|
-
## 分支与发布流程(dev 测试版 → main 正式版)
|
|
193
|
-
|
|
194
|
-
**分支职责**
|
|
195
|
-
- `dev`:**集成/测试分支**。所有改动先落这里(直接提交,或功能分支 PR 进 dev);允许 force push;CI(`gate`)在这里跑。
|
|
196
|
-
- `main`:**只用于发布**。已开分支保护:必需检查 `gate`、禁止 force push、禁止删除。改动只能从 `dev` 合入(PR),main 上不再有其它直接提交。
|
|
197
|
-
- 每次发布后 `dev` 与 `main` 收敛:`git checkout dev && git merge --ff-only main`。
|
|
198
|
-
|
|
199
|
-
**触发词与硬约束(最重要,别越界)**
|
|
200
|
-
- 用户说「**可以了,发布**」(或类似)= **发 dev 测试版**。此时**绝不**动 `main`、**绝不**执行 `npm publish` 的默认(`latest`)发布。
|
|
201
|
-
- 只有用户明确说「**合并主分支发布正式版**」(或等价明确指令)时,才走正式版流程;并且**正式版的 npm 发布命令只输出给用户手动执行**——代理不代发(用户自己用浏览器登录拿凭据)。仓库里没有 `NPM_TOKEN` secret,CI 也不会替你发。
|
|
202
|
-
- **已发布的 tag 与 npm 版本绝不移动 / 删除 / unpublish**;发现缺陷就发下一个补丁版本。这条是 5.0.0→5.0.1 那次事故的直接教训(当时为了"对齐"移动过 tag,用户明确否掉了这种做法)。
|
|
203
|
-
- 测试版**必须** `--tag dev`(若发 npm)+ GitHub `--prerelease`;只有正式版才能用 `--latest` 与默认 dist-tag(否则 `latest` 会被指到测试版,普通用户直接中招)。
|
|
204
|
-
- 只有 CI(`gate`)绿的提交才能进 `main`;`pnpm pack` 的产物名必须与 `package.json` 版本一致。
|
|
205
|
-
|
|
206
|
-
**阶段一:测试版(在 dev 上)**
|
|
207
|
-
```powershell
|
|
208
|
-
git checkout dev && git pull
|
|
209
|
-
# 改代码;package.json 版本 = <下一个正式版>-dev.<N>(N 不复用,如 5.1.0-dev.1)
|
|
210
|
-
npm run check; npm run check:secrets; npm run check:ui # 再逐个跑单测
|
|
211
|
-
$env:npm_config_store_dir='D:/my/app/dsh/plugin/.pnpm-store'; pnpm pack # dsh-dbhub-live-<ver>.tgz
|
|
212
|
-
git add -A; git commit -m "..."; git push origin dev # CI 在 dev 上跑 gate
|
|
213
|
-
git tag -a v<ver> -m "..."; git push origin v<ver>
|
|
214
|
-
gh release create v<ver> --prerelease --title "v<ver>" --notes-file <notes> dsh-dbhub-live-<ver>.tgz
|
|
215
|
-
# 可选(只在需要验证 npm 安装路径时):npm publish --registry=https://registry.npmjs.org/ --tag dev
|
|
216
|
-
# 安装验证(不碰 npm 也行):dsh plugin --profile web add <tarball>(或 dsh-dbhub-live@dev)
|
|
217
|
-
```
|
|
218
|
-
预发布不会抢 Release 页的 "Latest" 徽章,普通用户 `dsh plugin add dsh-dbhub-live` 仍拿正式版。
|
|
219
|
-
|
|
220
|
-
**阶段二:正式版(仅在用户明确要求时)**
|
|
221
|
-
```powershell
|
|
222
|
-
# 1) 在 dev 上把版本号转正(去掉 -dev.N),提交 "vX.Y.Z (release): ...",push dev
|
|
223
|
-
# 2) 开 PR 合入 main,等 CI 绿后合并
|
|
224
|
-
gh pr create --base main --head dev --title "vX.Y.Z (release): ..." --body "..."
|
|
225
|
-
gh pr merge --merge
|
|
226
|
-
# 3) 在 main 的合并提交上打 tag
|
|
227
|
-
git checkout main && git pull; git tag -a vX.Y.Z -m "..."; git push origin vX.Y.Z
|
|
228
|
-
# 4) GitHub 正式发布(--latest,附 tarball)
|
|
229
|
-
gh release create vX.Y.Z --latest --title "vX.Y.Z" --notes-file <notes> dsh-dbhub-live-X.Y.Z.tgz
|
|
230
|
-
# 5) npm:把命令交给用户手动执行(代理不代发)
|
|
231
|
-
# npm publish --registry=https://registry.npmjs.org/ # 不带 --tag → latest 指向它
|
|
98
|
+
# 单测必须逐文件跑(沙箱内 node --test 的 runner 子进程 EPERM)
|
|
99
|
+
Get-ChildItem test/*.test.mjs | ForEach-Object { node $_.FullName }
|
|
232
100
|
```
|
|
233
101
|
|
|
234
|
-
|
|
102
|
+
- CI:`.github/workflows/quality.yml` job **`gate`**(ubuntu-latest + Node 22),push `main`/`dev`、所有 PR、`workflow_dispatch`;`main` 分支保护把它设为必需检查。另有 `release-matrix.yml` 定时跑 `support:check`。
|
|
103
|
+
- **CI 是真实执行**,Linux 会暴露 Windows 掩盖的缺陷 → [原因](doc/ARCHITECTURE.md#自动发现的代价与unref教训)。
|
|
104
|
+
- 发布前在隔离实例完整冷启动一遍:见 [doc/DEVELOPMENT.md](doc/DEVELOPMENT.md) 第 3 步(含 200/401 与 bundle 校验)。
|
|
235
105
|
|
|
236
|
-
##
|
|
106
|
+
## 变更影响矩阵
|
|
237
107
|
|
|
238
|
-
|
|
239
|
-
- 原因:pnpm 对 `dsh plugin add <同名同版本 tarball>` 会判「Already up to date」跳过、**不换包**;升版本号才能保证调试包真正部署。
|
|
240
|
-
- `dsh plugin add` 后务必用 `dsh plugin ls` 或核对 `node_modules/<pkg>/package.json` 的版本号确认已替换。
|
|
241
|
-
- **测试完成要发版时,在基线上按「跨度和力度」决定升级幅度**(不以 dev 序号原号发布):
|
|
242
|
-
- 小 bug 修复 / 小功能改动 → 升**补丁号**:`4.0.0` → `4.0.1`(例:测试到 `4.0.0-dev.8` 仅小修 → 发布 `4.0.1`);
|
|
243
|
-
- 大功能新增 / 影响面较广的功能改动 → 升**次版本号**:`4.0.0` → `4.1.0`;
|
|
244
|
-
- 破坏性升级 / 阶段性架构变更 / 跨度过大 → 升**主版本号**:`4.0.0` → `5.0.0`(tag/npm 一律打正式版本号)。
|
|
245
|
-
- 跨档并存时按最高档计(含破坏性改动 → 主版本);归属拿不准时**先向用户确认再发版**,禁止未确认跨度直接升主版本。
|
|
246
|
-
- **发布版本成为新基线**,下一轮测试从 `新基线-dev.1` 重新起序(如 `5.0.0-dev.1`…)。
|
|
108
|
+
改动前先去 [doc/IMPACT-MATRIX.md](doc/IMPACT-MATRIX.md) **按类型核查一遍联动项**(该表按需加载,本文件不内联)。矩阵本身也是受管文档:出现新的联动关系要同次加行,`npm run check:docs` 会校验它的路径与链接。
|
|
247
109
|
|
|
248
110
|
## 约定
|
|
249
111
|
|
|
250
|
-
-
|
|
251
|
-
-
|
|
112
|
+
- 产品文案中文、代码注释英文;零知识不可绕过;扫描必须经 `askUser` 授权。
|
|
113
|
+
- 仓库内一切内容视为公开分发:不得出现真实主机/账号/密码/库名/项目名,也**不得出现本机绝对路径**;示例主机用回环或 RFC 5737、示例密码用 `CHANGE_ME`;新出口同次登记槽位表;发现新真实值只写本地 `.env`(详见 [doc/DESENSITIZE.md](doc/DESENSITIZE.md))。
|
|
252
114
|
- `state.*` 之外不要直接改持久化。
|
|
253
|
-
-
|
|
254
|
-
-
|
|
115
|
+
- 发布:默认只往 `dev` 提交/推送;动 `main`、打 tag、发 npm 前先向用户确认(「可以了,发布」= dev 测试版)。流程见 [doc/RELEASE.md](doc/RELEASE.md)。
|
|
116
|
+
- 增加行为时同次更新本文件 + `doc/REQUIREMENTS.md` + README(双语,`README.md` 为唯一真源,`README.en.md` 由 AI 同次派生)。
|