@dsh-workbench/core 0.3.3 → 0.3.5
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/client.js +1749 -114
- package/cordis.patch.yml +32 -0
- package/dist/client/client.body.js +1749 -114
- package/dist/client/index.js +1749 -114
- package/dist/host/index.js +3101 -1156
- package/dist/types/app/client.d.ts +306 -60
- package/dist/types/app/layouts/approval-detail.d.ts +13 -0
- package/dist/types/app/layouts/sidebar/RecordFooterAction.d.ts +16 -0
- package/dist/types/app/layouts/sidebar/record-footer.d.ts +4 -0
- package/dist/types/modules/panels/client.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-components.d.ts +3 -0
- package/dist/types/modules/panels/model/diy-components.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-context.d.ts +4 -5
- package/dist/types/modules/panels/model/diy-observers.d.ts +2 -2
- package/dist/types/modules/panels/model/diy-page.d.ts +1 -1
- package/dist/types/modules/panels/model/diy-record-bar.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-capture.d.ts +27 -0
- package/dist/types/modules/panels/model/diy-record-capture.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-failure.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-routes.d.ts +2 -0
- package/dist/types/modules/panels/model/diy-record-routes.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-scripts.d.ts +6 -0
- package/dist/types/modules/panels/model/diy-record-text.d.ts +89 -0
- package/dist/types/modules/panels/model/diy-record-text.test.d.ts +1 -0
- package/dist/types/modules/panels/model/diy-record-tools.d.ts +2 -0
- package/dist/types/modules/panels/model/diy-record.d.ts +76 -0
- package/dist/types/modules/panels/model/diy-record.test.d.ts +1 -0
- package/dist/types/modules/panels/model/handoff.d.ts +1 -0
- package/dist/types/modules/panels/unit.d.ts +10 -5
- package/dist/types/shared/api/context-chips.d.ts +20 -0
- package/dist/types/shared/api/host/diy-acts.d.ts +4 -0
- package/dist/types/shared/api/host/diy-components.d.ts +65 -0
- package/dist/types/shared/api/host/diy-debug-act.d.ts +24 -7
- package/dist/types/shared/api/host/diy-record-act.d.ts +19 -0
- package/dist/types/shared/api/host/diy-workshop.d.ts +1 -3
- package/dist/types/shared/api/host/mount-units.d.ts +4 -0
- package/dist/types/shared/api/host/preset-scope.d.ts +16 -0
- package/dist/types/shared/api/host/tool-scope.d.ts +6 -2
- package/dist/types/shared/api/host/workshop-root.d.ts +7 -0
- package/dist/types/shared/components/BlockerState.d.ts +12 -0
- package/dist/types/shared/components/PanelFoot.d.ts +9 -0
- package/dist/types/shared/components/Tip.d.ts +8 -0
- package/dist/types/shared/hooks/bundle-queries.d.ts +12 -0
- package/dist/types/shared/hooks/fact-transports.d.ts +4 -0
- package/dist/types/shared/hooks/panel-state-report.d.ts +12 -0
- package/dist/types/shared/hooks/use-api-resource.d.ts +22 -0
- package/dist/types/shared/hooks/use-blocker-actions.d.ts +8 -0
- package/dist/types/shared/hooks/use-capabilities.d.ts +55 -0
- package/dist/types/shared/hooks/use-capability-gate.d.ts +16 -0
- package/dist/types/shared/hooks/use-card-summary.d.ts +7 -0
- package/dist/types/shared/hooks/use-diy-debug.d.ts +13 -0
- package/dist/types/shared/hooks/use-diy-record.d.ts +21 -0
- package/dist/types/shared/hooks/use-facts.d.ts +10 -0
- package/dist/types/shared/hooks/use-host-write.d.ts +12 -0
- package/dist/types/shared/hooks/use-long-task.d.ts +8 -0
- package/dist/types/shared/hooks/use-unit-board.d.ts +17 -0
- package/dist/types/shared/hooks/use-visible-busy.d.ts +17 -0
- package/package.json +2 -2
- package/scripts/seed-workshop.sh +42 -2
- package/skills/workbench-module-diy/BUILD.md +4 -3
- package/skills/workbench-module-diy/DATASOURCE.md +2 -2
- package/skills/workbench-module-diy/DESIGN.md +26 -1
- package/skills/workbench-module-diy/FAMILY.md +56 -0
- package/skills/workbench-module-diy/KIT.md +111 -96
- package/skills/workbench-module-diy/PANEL.md +24 -13
- package/skills/workbench-module-diy/REVIEW.md +9 -8
- package/skills/workbench-module-diy/SKILL.md +16 -6
- package/skills/workbench-module-diy/TOOLING.md +5 -2
- package/skills/workbench-module-diy/TOOLS.md +7 -41
- package/skills/workbench-module-diy/WIRING.md +14 -6
- package/skills/workbench-module-diy/WRITES.md +47 -6
|
@@ -14,15 +14,19 @@ description: 在工作台里凭需求造一个模块(面板上的一块内容
|
|
|
14
14
|
这一步的产出是 ASCII 画面 + 三张表,不是文字。三张表当场落进模块里的 `design.json`
|
|
15
15
|
(命令与形状:[DESIGN.md](DESIGN.md) §5)—— 只留在对话里等于没定。
|
|
16
16
|
3. **先定数据从哪来** —— [DATASOURCE.md](DATASOURCE.md):干净机器上没有现成 skill 可抄,
|
|
17
|
-
用 `
|
|
17
|
+
用 `workbench_diy_eval` / `_find` 带 `read=<站点地址>`(跑在 `workbench-read` 上)复用他的登录去探 —— 没在站点上我自己开一次,`not-logged-in` / `not-on-site` 两条码按读线约定归一
|
|
18
18
|
都在那把工具里;探明后**固化成一条读**(`@dsh-workbench/shared/host` 的 `browserRead`)。顺手把三态写掉。
|
|
19
19
|
要 agent **真去改东西**(提交 / 发布 / 建单 / 改配置)的,读完 [WRITES.md](WRITES.md) 再动手:
|
|
20
20
|
四步(现成写命令 → 入参 / 出参来源 → 没有预览档的不许试写 → 写完后回读确认)。
|
|
21
|
+
有写工具就**顺手把审批屏那一屏也画进图里**([DESIGN.md](DESIGN.md) §3 末尾那节 + KIT.md §3):
|
|
22
|
+
标题一句 + `ApprovalPlan` 详情表 —— 那两格的形状是固定的(仓内 ADR-0065,这份指引不链仓内路径)。
|
|
21
23
|
4. **从模板改** —— [BUILD.md](BUILD.md) §1–§2:在工坊里 `dsh-wb-module init` 一个骨架,
|
|
22
24
|
把 `design.json` 里的**三张表与数据源记录先改成你问出来的那份**(`design check` 会点名叫出还空着哪一格),
|
|
23
25
|
再改数据源与渲染。
|
|
24
26
|
**不从零写**。
|
|
25
27
|
命令写成 `pnpm exec dsh-wb-module …`(工坊里 PATH 上也有同名入口,但 `pnpm exec` 到处都对)。
|
|
28
|
+
`init` 完先在工坊里跑一次 `pnpm install`:模板默认带了 dsh 原语那个 devDependency
|
|
29
|
+
([KIT.md](KIT.md) §8),不装 `pnpm typecheck` 会报"找不到模块"。
|
|
26
30
|
5. **自己过门禁** —— `dsh-wb-module build` + `check` + `design check`;
|
|
27
31
|
红了自己修([REVIEW.md](REVIEW.md) 有对照),不抛给他、也不贴报错给他看。
|
|
28
32
|
6. **装上、上板** —— `workbench_diy_install`(`unit=<单元 id>`)装上:
|
|
@@ -35,8 +39,8 @@ description: 在工作台里凭需求造一个模块(面板上的一块内容
|
|
|
35
39
|
要看 / 动**他那一屏**(浏览器那 14 把 + `logs_*`)得先开约定:
|
|
36
40
|
`workbench_diy_debug_start`(`unit=…`;会请他点头一次,他会被告知「调试中请留在那一屏」)——
|
|
37
41
|
**没有这场约定,上面那一族一律当场拒**;不看屏了先 `workbench_diy_debug_pause`,收工 `…_stop`。
|
|
38
|
-
(`workbench_diy_panel` / `_status` / `
|
|
39
|
-
走法(全表与参数在 [
|
|
42
|
+
(`workbench_diy_panel` / `_status` 不碰他那一屏,不用开;`_find` / `_eval` 带 `read=` 时也**不碰** —— 那走只读线。)
|
|
43
|
+
走法(全表与参数在 [FAMILY.md](FAMILY.md)):
|
|
40
44
|
- **这一张卡长什么样**:`workbench_diy_screenshot`(要卡那一块就先 `eval` 量盒子、填 `clip`)+
|
|
41
45
|
`workbench_diy_html`(`--as json` 给结构树);
|
|
42
46
|
- **元素与语义**:`workbench_diy_find`(`--css '$card …'`、或 `--role` / `--name`)、`workbench_diy_state`;
|
|
@@ -73,7 +77,10 @@ description: 在工作台里凭需求造一个模块(面板上的一块内容
|
|
|
73
77
|
| **要 agent 真去改东西**(提交 / 发布 / 建单 / 改配置:写通道四步) | [WRITES.md](WRITES.md) |
|
|
74
78
|
| 面板上该有什么、状态矩阵怎么写、界面习惯 | [PANEL.md](PANEL.md) |
|
|
75
79
|
| **要做什么 → 用哪件 kit → 最小写法**(照抄面) | [KIT.md](KIT.md) |
|
|
76
|
-
|
|
|
80
|
+
| **kit / dsh 原语里到底有什么、某一件怎么用**(DIY 会话里直接调这把工具) | `workbench_diy_components`(`name=Button` 回它的类型文件与声明;工程域用 `check --only kit` · `--only primitives`)· [KIT.md](KIT.md) §8 |
|
|
81
|
+
| **kit 那些 hook 怎么搭**(四个场合、一格卡片怎么拼) | [KIT.md](KIT.md) §4′(逐件的 API 看 `.d.ts` 的 JSDoc 或 `workbench_diy_components`) |
|
|
82
|
+
| 能力怎么划、传什么拿回什么、哪几件要点头 | [TOOLS.md](TOOLS.md) |
|
|
83
|
+
| **工坊会话里那 33 把的全表**(枚举 opencli 的 14 把 + 看/装/探 + 调试约定 + 录制交接、参数与如实边界) | [FAMILY.md](FAMILY.md) |
|
|
77
84
|
| agent 动完面板哪一格变、他正在干的时候看哪儿 | [LINKAGE.md](LINKAGE.md) |
|
|
78
85
|
| 落点、文件形状、命令、两个面 | [BUILD.md](BUILD.md) |
|
|
79
86
|
| 依赖、会被拦的写法、装、热换、发布 | [WIRING.md](WIRING.md) |
|
|
@@ -84,7 +91,9 @@ description: 在工作台里凭需求造一个模块(面板上的一块内容
|
|
|
84
91
|
## 找不到一件事的时候,按这个顺序找
|
|
85
92
|
|
|
86
93
|
1. **先跑门禁**:`dsh-wb-module check --only <那一条>` 的报错原文最准。
|
|
87
|
-
「外壳的 kit 里到底有什么组件 / hooks
|
|
94
|
+
「外壳的 kit 里到底有什么组件 / hooks」「dsh 原语里有什么、某一件怎么用」在工坊会话里直接调
|
|
95
|
+
`workbench_diy_components`(不填给清单,`name=Button` 回它的类型文件与声明);工程域用
|
|
96
|
+
`check --only kit` · `check --only primitives`(`--all` 全表、`--json` 机器档)。
|
|
88
97
|
2. **再看模板**:已经有一个同形状、能跑的最小实现,比任何描述都准 ——
|
|
89
98
|
在你工坊的 `node_modules/@dsh-workbench/module-tools/template-diy/`(带真数据源的样板)
|
|
90
99
|
与 `template/`(空壳)里,原文都是可读的。
|
|
@@ -100,4 +109,5 @@ description: 在工作台里凭需求造一个模块(面板上的一块内容
|
|
|
100
109
|
- **dsh 已经有的,先从 `kit` 拿**(`@dsh-workbench/core/client`),不许从 DOM + CSS 重画;
|
|
101
110
|
也不直接 import dsh 的包 —— 直接引会被门禁拦,而且那是第二份实例。
|
|
102
111
|
- **工坊里的模块一律 `private: true`,不发到任何源。**
|
|
103
|
-
-
|
|
112
|
+
- **源码里只许 JSDoc,且贴着导出**(用法住在声明里,tsc 把它搬进 `.d.ts`);`//`、实现里的解释、
|
|
113
|
+
以及 `.css` 里的注释照旧不许有。要说的"为什么"写进模块自己的 `README.md` 或 spec。
|
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
| 落点、分层文件形状、六个脚本([BUILD.md](BUILD.md) §1–§2) | `dsh-wb-module init --template diy`(骨架带分层目录 + 一行 `arch.config.mjs` + 两行 `eslint.config.mjs`(`moduleLint({ rootDir: import.meta.dirname })`)+ 一条真读盘的数据源)+ `scripts/smoke.mjs` |
|
|
17
17
|
| 语法卫生红了、要自动修([REVIEW.md](REVIEW.md) 第十三条) | `dsh-wb-module lint [--fix]`(**`check` 永不动手改文件**;`--fix` 才写回) |
|
|
18
18
|
| 装、上板、不重装([WIRING.md](WIRING.md) §3) | `workbench_diy_install` / `workbench_diy_show` / `workbench_diy_status`(工坊那一跳自己上板) |
|
|
19
|
+
| 想知道 dsh 原语 / 这份外壳的 kit 里有什么、某一件怎么用([KIT.md](KIT.md) §8) | `workbench_diy_components`(不填给清单 + 类型面根;`name=Button` 回它的类型文件与那份声明——JSDoc 里写着默认值与语义;`bucket=icons` 把那一桶列全——`all=true` 的全表在真工坊超 8192、中间会被修剪器裁掉,按桶取才装得下;工程域用 `dsh-wb-module check --only primitives --file <名字>`) |
|
|
19
20
|
| 它到底起得来没(装配抛的错 / 声明被拒的原文) | `workbench_diy_status`("起得来"那一行,见 [REVIEW.md](REVIEW.md) 四步取证的第二步) |
|
|
20
|
-
| 看 / 动**用户那一屏**之前要先取得约定(ADR-0055 §7「调试是一次约定」) | `workbench_diy_debug_start`(`unit=…`,**要用户点头一次**)—— 没有它,浏览器那 14 把 / `logs_*` **一律当场拒**(拒的时候一条命令都不发);不看屏了 `_pause`、收工 `_stop`(**收工顺手把网络捕获与日志钩子也收了**)。`_panel` / `_status` / `
|
|
21
|
+
| 看 / 动**用户那一屏**之前要先取得约定(ADR-0055 §7「调试是一次约定」) | `workbench_diy_debug_start`(`unit=…`,**要用户点头一次**)—— 没有它,浏览器那 14 把 / `logs_*` **一律当场拒**(拒的时候一条命令都不发);不看屏了 `_pause`、收工 `_stop`(**收工顺手把网络捕获与日志钩子也收了**)。`_panel` / `_status` 不碰他那一屏,不用开(`_find` / `_eval` 的 `read=` 档也不碰 —— 那是只读线)。**全表与参数见 [FAMILY.md](FAMILY.md)** |
|
|
22
|
+
| 抓那一次**在外站页面上点出来的写**(他的写只发生一次,也不在我们那条会话里) | `_record_prepare`(`unit` / `url` / `doing`,**要用户点头一次**)→ 请他按「开始录制」→ 做**一次**那动作 → 按「结束录制」→ `_record_read` 取那一段 → `_record_stop` 收工(**先 read 再 stop**)。它不走调试约定,只碰自己的 `workbench-rec` 会话;三颗键、边界与实话见 [FAMILY.md](FAMILY.md) §5 · [WRITES.md](WRITES.md) 步骤 3 |
|
|
21
23
|
| 看 / 动那一屏的浏览器动作怎么发(枚举 opencli,argv 与结果原样转发) | 14 把:`workbench_diy_state` / `_html` / `_find` / `_click` / `_hover` / `_type` / `_fill` / `_keys` / `_scroll` / `_wait` / `_eval` / `_screenshot` / `_network` / `_dialog` —— 公共参数只有 `unit?` / `args?` / `timeoutMs?`(另有 `scope` / `in` / `clip` 只长在该长的那几把上;`$card` 在任何 `args` 里都替换)。**不知道参数就问 `--help`**(也照样转发);返回体是 opencli 的原样,事实在 `stdout` 里 |
|
|
22
24
|
| 这一页的控制台 / 网络怎么取证([REVIEW.md](REVIEW.md) 四步取证) | `workbench_diy_logs_watch` / `_read` / `_stop`(钩子有上限:每页 200 条、单条 400 字)—— **opencli 的 `console` 是空桩**(恒回空数组、还 exit 0),控制台只能我们自己做;网络是 `workbench_diy_network` 一把(`--follow` 臂上、`--failed` / `--since` 这些原样转发;收工 `_debug_stop` 顺手收),但**`--detail` 取不到 body** —— 缓存落在会话可写根之外,只能列请求 key;要 body 走 `_eval` 里的 `fetch`。网络那一头工作台走的是**隔离 opencli**(自己的 `HOME`),它的缓存与你自己 shell 里 `~/.opencli` 那份不是同一个 —— 核验要用工具,别看错文件 |
|
|
23
25
|
| 面板上现在是什么样(每张卡读到没 / 崩没崩) | `workbench_diy_panel`(浏览器半侧上报,宿主按 unit 留最新一条) |
|
|
@@ -29,7 +31,8 @@
|
|
|
29
31
|
| 这个能力要靠什么才跑得起来([TOOLS.md](TOOLS.md) §8) | `manifest.env` + `libInstallCommand`,`check --only contracts` 查 |
|
|
30
32
|
| 配置存不存([PANEL.md](PANEL.md) §5.1) | `config[].persistence`(`prefs` / `view`)+ `check --only contracts` |
|
|
31
33
|
| agent 动完面板哪一格变([LINKAGE.md](LINKAGE.md) §8) | `factsOf` + `factOfRoute` / `context` / `warmReads` / `values`,`check --only contracts` 只查形状 |
|
|
32
|
-
| kit 不够用时能不能引 dsh 的界面原语([PANEL.md](PANEL.md) §9) | 只在客户端侧引 + `dsh.client.external`
|
|
34
|
+
| kit 不够用时能不能引 dsh 的界面原语([PANEL.md](PANEL.md) §9 · [KIT.md](KIT.md) §8) | 只在客户端侧引 + `dsh.client.external` 里声明 + `devDependencies` 精确版对齐外壳钉的那份;宿主侧一个字都不许碰(`check --only source` / `--only primitives`) |
|
|
35
|
+
| dsh 原语里到底有什么(名字 / 哪几件 kit 已有 / 该写哪两行 / **这一件在哪份类型文件**) | `dsh-wb-module check --only primitives`(单跑当查询口;`--all` 全表、`--file <名字>` 给绝对路径、`--files \| grep` 全表、`--json` 机器档);kit 那条同形(`--only kit --file Card`)—— 取装机那份 `lib/types/**/*.d.ts`,不手抄清单 |
|
|
33
36
|
| DOM 读怎么读([DATASOURCE.md](DATASOURCE.md) 第 2 步) | `probe`(探)+ `browserFind`(固化)—— 切格 / 按列取值是两个纯函数 |
|
|
34
37
|
| 链接只有一副样子([PANEL.md](PANEL.md) §6) | kit 的 `Link` 是唯一一份(**没有 `icon` 口子**),`check --only style` 拦裸 `<a>` |
|
|
35
38
|
| 输入框不用自己配样式([PANEL.md](PANEL.md) §5.1) | `TextField` 的默认内边距 + kit 的 `Field`(标签 + 提示的排布) |
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
- `key`(lowerCamelCase):给了就跑一次 `dsh-wb-module sync` 生成 `src/declared-tools.ts`,代码写 `DECLARED_TOOLS.rebuild.name` —— 工具名字面量只剩清单这一处(键写错编译错;生成物过期 / 半写 key / 又写字面量都红)。
|
|
63
63
|
- `needsApproval` 回 `{ kind: 'read' }` = 不问,`{ kind: 'ask', question, fallback }` = 要问;**`writes: true` 却不认领 = 当场 deny**。`check --only contracts` 先替你看:缺 `needsApproval`、zh/en 不齐、`approvalDetail` 没给 → 红。
|
|
64
64
|
- `question` 是问句、`fallback` 是拼不出细节时的兜底,都要 zh/en;`approvalDetail` 是 §7 那段明细(一行一个事实),拿不到料回 `{ blocked: '…' }` = **不弹**。缺东西先自己走一遍预览摆事实,补不齐就停下把原因交给 agent 查。
|
|
65
|
+
- **那屏的样子是定死的**([DESIGN.md](DESIGN.md) §3 末节):标题**只有一句**(`approvalDetail` 那块明细 ≤20 字一行,且**只给没有详情表的能力**);**事实与长内容走客户端面的 `approval.render`**,用外壳的 `ApprovalPlan` 画成两列表 —— 详情座那边等宽、能换行,路径 / diff / 多字段都放得下。宿主算的料到不了浏览器侧,事实要从**工具参数**重建。
|
|
65
66
|
|
|
66
67
|
## 7 点头的全场景
|
|
67
68
|
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
|
|
89
90
|
跑几十秒的事(构建、发布、批量删)做成**长任务**。宿主半侧用 `mount` deps 里的 `longTasks` 建注册表并挂回返回值:`longTasks.create({ key, run, step, outcome, view })` —— `run({ payload, onStep })` 里用 `onStep({ kind: 'start', text })` 报步骤、`return` 结果;`step`/`outcome`/`view` 各映射一段给界面;返回里带上 `longTasks: runs`。
|
|
90
91
|
|
|
91
|
-
浏览器半侧用 kit
|
|
92
|
+
浏览器半侧用 kit 现成的 `useLongTask`(→ `feed.runs`),画用 `LongTaskRow` 或 `longTaskDot`(每条的字段与四种状态见 `.d.ts`);**契约里没有"停止"**,用户要中途停得自己在 `run` 里看信号。判据(`check --only contracts`):注册表五个方法要全、`create` 的 `key`/`run`/`view` 不能缺、产物里必须有 `useLongTask`/`LongTaskRow`/`longTaskDot` 之一。
|
|
92
93
|
|
|
93
94
|
## 10 用户自己按的那个入口 / 范例
|
|
94
95
|
|
|
@@ -99,47 +100,12 @@
|
|
|
99
100
|
工具是**这台机器上的**,但项目不止一个:在 A 有意义的到 B 就是噪音。**别让工具自己判断再回"这个项目用不了"**,当场从清单里拿掉 —— `gate: { deniedTools(workspace), subscribe(listener) }`(挂进 `mount` 返回值):
|
|
100
101
|
|
|
101
102
|
- `deniedTools(workspace)` 回"这个项目里我的哪几个工具不存在"(名字跟 `registerTools()` 注册的一致;`workspace` 也可能是 `null`)。`subscribe(listener)`:你状态变了(用户把能力关了)时通知外壳重算 —— 不接要等下次重启。
|
|
102
|
-
- 外壳两道都做:清单里不出现 + 真调到时当场拒。它自己就这么用:工坊那一族 **
|
|
103
|
+
- 外壳两道都做:清单里不出现 + 真调到时当场拒。它自己就这么用:工坊那一族 **33 把**(14 枚举 + 12 看 / 装 / 探 + 4 调试 + 3 录制交接,见 [FAMILY.md](FAMILY.md))只在工坊会话里出现。
|
|
103
104
|
|
|
104
105
|
**"哪些工作区出现"不用你声明**(「模块开发者不能定义 scope」):他用 `workbench_panels_show` / `_hide` 放上 / 收起,卡片与**全部**工具同判。`deniedTools` 可选。
|
|
105
106
|
|
|
106
|
-
## 13 工坊会话里那一族:
|
|
107
|
+
## 13 工坊会话里那一族:33 把
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- 只在**工坊会话**里出现。看 / 动 / 网络 / 日志这一族要**先立调试约定** `workbench_diy_debug_start`(`unit=…`,请用户点头一次):**没有约定一律当场拒、零命令**;不看屏 `_debug_pause`、收工 `_debug_stop`(顺手收网络与日志钩子)。
|
|
113
|
-
- 还要**工坊那块板画在屏上**;会话钉死 `workbench`、每条带 `--window background`。`_list`/`_status`/`_panel`/`_recon`/`_probe`/`_spec`/`_install`/`_show` 不碰他那一屏,不用开。
|
|
114
|
-
|
|
115
|
-
### 13.2 14 把浏览器工具(工具名 → argv;前缀都是 `workbench_diy_`)
|
|
116
|
-
|
|
117
|
-
`_state`→`state` · `_html`→`get html` · `_find`→`find` · `_click`→`click` · `_hover`→`hover` · `_type`→`type` · `_fill`→`fill` · `_keys`→`keys` · `_scroll`→`scroll` · `_wait`→`wait` · `_eval`→`eval` · `_screenshot`→`screenshot` · `_network`→`network` · `_dialog`→`dialog`
|
|
118
|
-
|
|
119
|
-
干什么:名字直说;不直观的几个 —— `_state` URL + 标题 + 可点元素(ref 来源)、`_html` HTML(`--as json` 树)、`_find` 找元素回 `{matches_n, entries[]}`、`_network` 收发包(`--follow`;body 拿不到)、`_screenshot` 写文件(`clip` 只裁一块)。
|
|
120
|
-
|
|
121
|
-
**参数**(`scope` / `in` 默认都是 `page`):
|
|
122
|
-
|
|
123
|
-
- 公共:`unit?`(哪张卡;可省 —— 只剩一个模块就用它。**要卡作用域就得给**:写 `scope: 'card'` / `in: 'card'` 或用 `$card` 却不给 ⇒ **当场拒、零命令**)· `args?: string[]`(原样接在 `opencli browser workbench <verb…>` 后)· `timeoutMs?`(默认 30000;≤ 0 或非数也按默认)。
|
|
124
|
-
- `scope: 'card'` 只给吃 CSS 的**七把**:`_find`(`--css`)· `_html`(`--selector`,没给它我们补)· `_click`/`_hover`/`_type`/`_fill`(**位置参数 0**)· `_wait`(**只在 `wait selector`**,位置参数 1)—— `card` = 给那个目标挂 `#wb-card-<unit> .wb-card `。
|
|
125
|
-
- `in: 'card'` 只 `_eval`(注入卡根 `root`,**脚本自己 `return`**);`clip`(CSS px)只 `_screenshot`(拍完本地裁);`$card` **任何 `args` 里**都替换成 `#wb-card-<unit> .wb-card`。
|
|
126
|
-
|
|
127
|
-
**拿回**:`{ ok, exitCode, timedOut, truncated, stdout, stderr }` + `workshop`/`unit`/`verb`/`argv`(落到命令行的词);`_screenshot` 另给 `file`/`cropped`/`cropReason`。`ok` 只管进程;事实全在 `stdout`(报错也是一份 `{"error":{…}}`,`ok` 可能仍 `true`);不抠 JSON / 不翻中文 / 不做 ok 归一,**要参数先问 `--help`**。**"原样"有折损**:`args` 里空串 / 纯空白 / 非字符串**丢掉**、留下的词**前后 trim**、`stdout`/`stderr` 也 trim ⇒ **`_fill` 清空不了输入框**(`''` 被丢)、**`_type` 敲不了空格** —— 没有别的口子就如实说做不到。
|
|
128
|
-
|
|
129
|
-
### 13.3 老工具 → 新命令
|
|
130
|
-
|
|
131
|
-
| 老 | 新 |
|
|
132
|
-
|---|---|
|
|
133
|
-
| `_look` | `state` + `get html` + `eval` + `screenshot`(卡图靠 `clip`,上游没有 selector) |
|
|
134
|
-
| `_reload` | `eval 'location.reload()'` |
|
|
135
|
-
| `_viewport` | `screenshot --width/--height` |
|
|
136
|
-
| `_select` | **没了**(上游只认原生 `<select>`);下拉两拍:卡内点触发器 → `scope: 'page'` + `--role menuitem` 按名字点 |
|
|
137
|
-
| `_net_watch` / `_net_read` / `_net_stop` | `network`(`--follow` 臂上、收工 `_debug_stop` 顺手收;**取 body 得走 `_eval` 的 `fetch`**,见 §13.4) |
|
|
138
|
-
| `_click` `_hover` `_type` `_fill` `_keys` `_scroll` | 同名,参数改成原样转发 |
|
|
139
|
-
| `_logs_*` | 同名(`console` 是空桩,控制台只能我们做) |
|
|
140
|
-
|
|
141
|
-
### 13.4 如实边界
|
|
142
|
-
|
|
143
|
-
- `screenshot` **没有上游 selector**:卡图先 `eval` 量盒子再填 `clip`;不给路径我们补一个到工坊同级的 `shots/<unit|page>-<时间戳>.png`(否则 opencli 回 base64);要裁的块不在视口里就退回整屏图,原因在 `cropReason`。
|
|
144
|
-
- `state` **没有 `scope`**(卡内定位用 `find --css '$card …'`);**opencli 没有右键 / contextmenu**;ref 只在**一次快照**里有效,别跨调用存;`_dialog` 的 `accept|dismiss` 不校验;`_network --detail` **取不到 body**(缓存落在会话可写根之外)—— 要 body 走 `_eval` 里的 `fetch`。
|
|
145
|
-
- 不在这张表上的另外 15 把:`_list`/`_status`/`_install`/`_show`(台账)、`_panel`/`_status`(宿主运行态)、`_recon`/`_probe`(数据源)、`_spec`(问一条 opencli 命令的形状:`--help -f json` 原样回,**插件也适用**)、`_logs_*`(`console` 空桩)、`_debug_*`(约定本身)。
|
|
109
|
+
那一族(枚举 opencli 的 14 把 + 看 / 装 / 探 12 把 + 调试约定 4 把 + 录制交接 3 把)与它的前置、
|
|
110
|
+
参数、如实边界,全表在 [FAMILY.md](FAMILY.md) —— 这一份讲**你写的模块**的能力怎么写;
|
|
111
|
+
那一份讲**工坊会话里 agent 手上真实有什么**。为什么按 opencli 子命令枚举见仓内 ADR-0062(这里不链仓内路径)。
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|---|---|
|
|
12
12
|
| 宿主侧要用的纯函数与常量(`param`、平台解析、工作区存储…) | `@dsh-workbench/shared/host`(写进 `dependencies`) |
|
|
13
13
|
| 浏览器侧要的一切(hooks、组件、图标、文案工具) | `@dsh-workbench/core/client` 的 `kit` |
|
|
14
|
+
| kit 里确实没有的 dsh 控件 / 图标(如 `Button`、整套 `Icon*Regular`) | `@deepseek-ai/dsh-client-ui-primitives`(声明 external + devDependency 精确版;清单见 `KIT.md` §8) |
|
|
14
15
|
| 类型 | `import type` 随便用(编译时擦除,不会进产物) |
|
|
15
16
|
| 外壳本体 | `@dsh-workbench/core`(peer 依赖,装的时候要比版本) |
|
|
16
17
|
|
|
@@ -18,6 +19,10 @@
|
|
|
18
19
|
kit 成员(`components` / `hooks` / `api` / `lib` 各有哪些)列出来 —— 它取的是外壳真实产物
|
|
19
20
|
`client.js` 里那一份,不是另抄的清单,所以永远和用户手上那版一致。
|
|
20
21
|
|
|
22
|
+
**kit 里没有的 dsh 原语有哪些,同样问命令**:`dsh-wb-module check --only primitives`
|
|
23
|
+
(`--all` 全表 / `--json` 机器档)列装机那份的导出、与 kit 求交、并给出该写进 `package.json`
|
|
24
|
+
的两行。两边都是"取真产物",没有手抄清单(口径见 ADR-0063 · `.scratch/dsh-primitives-face/spec.md`)。
|
|
25
|
+
|
|
21
26
|
⚠️ **浏览器侧不许 `import` `@dsh-workbench/shared` 的运行值**(两个入口都不行):
|
|
22
27
|
它**不在**客户端构建的 external 默认表里(工坊里那份是
|
|
23
28
|
`node_modules/@dsh-workbench/module-tools/src/build.mjs`),
|
|
@@ -32,14 +37,15 @@ import 运行值会被**打进你的包**(第二份实例),而且浏览器
|
|
|
32
37
|
| 别这么写 | 为什么 |
|
|
33
38
|
|---|---|
|
|
34
39
|
| 从 DOM + CSS 手搓一个 dsh 已经有的东西(按钮、弹窗、输入框、图标) | 重画就是第二份材质与交互(悬停、禁用、焦点环、色板),迟早跟 dsh 漂。**先从 `kit` 拿**;kit 里没有再自己写,并在 spec 里记理由 |
|
|
35
|
-
| `import … from '@deepseek-ai/dsh-client-ui-primitives'` |
|
|
40
|
+
| `import … from '@deepseek-ai/dsh-client-ui-primitives'` | **可以**,但要三处齐:只在浏览器侧(宿主侧碰它 `source` 判红)、在 `dsh.client.external` 里声明、**用了就必须在产物里是 `require`**(漏声明 = 被内联成第二份实例,`primitives` 判红)。kit 里有的先拿 kit(`KIT.md` §8);模板默认已经把这三处备好了 |
|
|
36
41
|
| `import … from '@/shared/…'`、`../../workbench/src/…` | 装你的人手上没有工作台的源码。`@/` 只许指**你自己**的 `src/` |
|
|
37
42
|
| 在公开包里写内网地址 / 内网域名 / 企业内部名 | 门禁会扫出来并指到行 |
|
|
38
43
|
|
|
39
44
|
另外两条不是"被拦",是纪律:**样式走 `*.module.css`,类名别带 `wb-` 前缀**(`wb-*` 是外壳的**全局**
|
|
40
45
|
类名:模块定义它、或借契约里没有的那一个,`check --only style` 都判红;模块自己的类名由构建**按文件路径
|
|
41
46
|
哈希**,天然不会跟谁撞。要借外壳契约里的类,写 `:global(.wb-…)`,且那个类必须在契约里);
|
|
42
|
-
**`src/**`
|
|
47
|
+
**`src/**` 里只许 JSDoc、且贴着导出**(用法归声明;`//`、实现里的解释与 `.css` 注释都不许)——
|
|
48
|
+
为什么写进 `README.md` 或 spec。
|
|
43
49
|
|
|
44
50
|
## 3 装上、上板
|
|
45
51
|
|
|
@@ -64,8 +70,8 @@ import 运行值会被**打进你的包**(第二份实例),而且浏览器
|
|
|
64
70
|
实测会拼错(把"要重启才注册"读成"没装上",或者源码改了没 build 却去重装一遍)。
|
|
65
71
|
- **已经装过、只是没上板**:`workbench_diy_show`(只上板,不装)。
|
|
66
72
|
|
|
67
|
-
工坊那一族 `workbench_diy_*`(
|
|
68
|
-
全表见 [
|
|
73
|
+
工坊那一族 `workbench_diy_*`(33 把:14 把枚举的 opencli + 12 把看/装/探 + 4 把调试约定 + 3 把录制交接,
|
|
74
|
+
全表见 [FAMILY.md](FAMILY.md))**只在工坊那个会话里**(工具清单按工作区收窄,别处连它都不在),
|
|
69
75
|
落在别的项目里调用会被当场拒 —— 这就是"DIY 模式"在工具面上的样子。
|
|
70
76
|
普通项目里装别的东西是另一套工具:`workbench_modules_install` / `workbench_modules_uninstall` 等(见 TOOLS.md)。
|
|
71
77
|
|
|
@@ -74,8 +80,10 @@ import 运行值会被**打进你的包**(第二份实例),而且浏览器
|
|
|
74
80
|
装完之后**不要再装一遍**。改源码 → `dsh-wb-module build` → 用户面板上那张卡自己变。
|
|
75
81
|
|
|
76
82
|
- **宿主侧**(`src/host.ts` / `balance.ts` … → `dist/host.js`):**秒级热换**,不重启、不刷新
|
|
77
|
-
(加一行探针 → build → 3 秒内接口就返回新内容)。`cordis.patch.yml`
|
|
78
|
-
|
|
83
|
+
(加一行探针 → build → 3 秒内接口就返回新内容)。`cordis.patch.yml` 里 hmr 那两件都不能去掉:
|
|
84
|
+
`followSymlinks: false`(跟着软链会把文件描述符用光),以及 `ignored` 里那两条 `*(../)`
|
|
85
|
+
extglob(工坊那个根在外壳包目录之外,相对路径以 `..` 打头,而 picomatch 的 `**` 不吃 `..` ——
|
|
86
|
+
少了它工坊 `node_modules` 的实目录会整棵进监听表)。为什么、实测数字与边界见仓内 ADR-0034 §3。
|
|
79
87
|
- **客户端侧**(`src/client.tsx` / CSS → `client.js`):浏览器**不会自己**换,**刷新一下页面**即可
|
|
80
88
|
(实测:重建后 60 秒页面没有任何反应)。`*.module.css` 的改动是就地改写那一份 `<style>`,刷新即生效。
|
|
81
89
|
- **改 `cordis.patch.yml`、改 `package.json` 里的 `dsh.client` / `exports["./client"]` 需要重启。**
|
|
@@ -38,8 +38,32 @@
|
|
|
38
38
|
- `opencli <site> <name> --help`(`-f yaml` 更好读)—— 补**取值枚举**:manifest 里没有 `choices`,
|
|
39
39
|
枚举只在 help 文本里;`_spec` 那条路回的是 JSON,`-f yaml` 适合人眼扫。
|
|
40
40
|
|
|
41
|
-
3.
|
|
42
|
-
|
|
41
|
+
3. **没有现成命令时,让他配合录一次**(agent 主导:你备好、他只动那一下、交回来):
|
|
42
|
+
- `workbench_diy_record_prepare({ unit, url, doing })` —— 开一扇窗**顶到最前**(**要用户点头一次**:审批屏上写清
|
|
43
|
+
站点、录哪一下、落在哪、录完不替他切回来),窗里有一根浮条。请他 ① 按「开始录制」 ② 做**一次**那动作 ③ 按「结束录制」;
|
|
44
|
+
**别让他点第二次**。`url` 必须是**这个模块读源同域**的页面 —— 模块的 `design.json` 说过读源时,跨域会**当场拒**
|
|
45
|
+
(没说过就不拦;那一条比的是"别把别处的请求原文带进这个模块")。
|
|
46
|
+
- `workbench_diy_record_read` 取那一段(他按完结束再取):回的是**清单** `rows`(每条 `index` / `key` / `method` /
|
|
47
|
+
`url` / `status` / `ct` / `size` / `source` / `hasRequest` / `hasResponse` / `likelyWrite` / `marked`),
|
|
48
|
+
**一条都不删、也不回体**;`expect` 只用来标 ⭐ 并把 ⭐ 排到前面。要哪一条的原文再调
|
|
49
|
+
**`workbench_diy_record_detail`**(给清单里那条的 `key`,或序号 `index`;**同一个 key 跨轮会重名**,
|
|
50
|
+
再带上那一行的 `at` 才钉得住是哪一次)——它回**入参(`request.body`)与
|
|
51
|
+
出参(`response body`)原文**,外加请求头(可能含凭据,别落进 `design.json`)。他还没按结束时 read 会说清"还在录"
|
|
52
|
+
(要等就带 `waitMs`,帽 15 分钟;等不到会挂一条 notice 叫你);浮条被导航干掉过会顺手重新装上。
|
|
53
|
+
**抓不满就重新 prepare 一场**:一场就是一个周期(开始 → 结束),结束之后那一页上的「开始」按不动了。
|
|
54
|
+
- `workbench_diy_record_stop` 收工:停捕获、收掉那个标签、清状态;**原文另存成 `shots/rec-<unit>.jsonl.last`** ——
|
|
55
|
+
收工之后给 `_record_detail` 一个 `unit` 还能看那一条。**先 read(或先 detail 完)再 stop**。
|
|
56
|
+
左栏底部那一格是给他的状态:等出手 `[取消]` / 录制中 `[停]`,按下去只会**给你一条消息**(他不替你按工具);
|
|
57
|
+
**录完那一档没有键** —— 你自己来取、自己收工(宿主每 3 秒看一眼那一页,看到结束就发 notice 叫你)。
|
|
58
|
+
- 抓不到的照旧如实说:`<form>` 原生提交 / WebSocket / service worker / **别处的标签**;
|
|
59
|
+
页面钩子只认 `fetch` 与 XHR,缓冲上限 50 条、单条请求体截 16 KB(`sendBeacon` 2026-10-20 起抓了)。
|
|
60
|
+
会话捕获在浏览器那一层,盖得比钩子全、且带体 —— 两处合并去重,同名以钩子为主、缺的 key / 入参 / 出参由捕获补齐。
|
|
61
|
+
- **兜底的老路**(那一页在别的浏览器里、或录制族用不上时):`opencli browser <你的会话名> bind`(只认**当前聚焦窗的活动标签**)
|
|
62
|
+
或 `open … --window foreground` 自己开一扇,**先把捕获臂上再让他动手**:`opencli browser <你的会话名> network --follow`
|
|
63
|
+
(一次性 `network` 不会替你开捕获;每读一次就把 live 缓冲抽干一次)。取那一段用 `network --raw`
|
|
64
|
+
(**同一次调用**才带 body;跨调用的 `--detail` 在我们这份隔离运行时落不了盘,恒 `cache_missing`),
|
|
65
|
+
还是没有请求体就请他打开 DevTools,把那次请求**复制成 curl** 给你。
|
|
66
|
+
- agent 自己点只限**测试环境 / 可撤销的目标**(同下面第一条纪律)。
|
|
43
67
|
取证结果要固化成模块自己的写能力:`writes: true` + `needsApproval` + `approvalDetail`([TOOLS.md](TOOLS.md) §6)。
|
|
44
68
|
|
|
45
69
|
4. **文档 / 问用户** —— 接口文档、别人给的 curl 样例都行;这属于**需求确认**那一拍
|
|
@@ -54,6 +78,17 @@
|
|
|
54
78
|
—— 调一次就是**真发布 / 真构建**。有预览档的先用预览档;没有的**不许 agent 试写**。
|
|
55
79
|
- **要新凭据 / 多步长流程 / 新依赖 ⇒ 工程域**(同 [DESIGN.md](DESIGN.md) §0)。
|
|
56
80
|
|
|
81
|
+
## 三条写纪律(与 opencli 自己那份 `deep-recon` 对齐)
|
|
82
|
+
|
|
83
|
+
1. **授权观察一次 ≠ 授权重放。** 他点一次是"给你看这一次";**不许因为你已经看过就再放一次**。
|
|
84
|
+
要再放一次,就得再跟他确认一次(同一场里"再录一段"算同一件事,见下)。
|
|
85
|
+
2. **写后"不确定"要当一档说清,并且**不许自动重试**。命令报错不等于没写成功(可能已经写了);
|
|
86
|
+
这种时候停下、**先回读**把状态读回来再决定,别重放。`design.json` 的 `failure` 那一格要写到这一档。
|
|
87
|
+
3. **写要有配对的撤销 / 反操作**(`follow` ↔ `unfollow`、`publish` ↔ `delete`…)。opencli 自己有一条
|
|
88
|
+
机器判据 `write-without-delete-pair` 盯这个;你交出去的那张卡至少要在**写通道探明记录**里写清
|
|
89
|
+
"这一步错了怎么退"(哪条命令 / 平台上哪里点)—— 那就是 `writes[].undo` 那一格,**必填**:
|
|
90
|
+
真没有配对就写"没有配对,一次就是真的"(**空着 = 没想过**,`design check` 会点名 `write.undo`)。
|
|
91
|
+
|
|
57
92
|
## 出参的最终确认靠**回读**
|
|
58
93
|
|
|
59
94
|
写完拿**同族的读**(或卡片那条投影)把那一格读回来,**别看"命令没报错"就当成功** ——
|
|
@@ -62,9 +97,11 @@
|
|
|
62
97
|
|
|
63
98
|
## 边界(如实)
|
|
64
99
|
|
|
65
|
-
- **`browser network --detail` 在工坊会话里拿不到 body
|
|
66
|
-
(`EPERM …/opencli/home/.opencli/cache/browser-network
|
|
67
|
-
|
|
100
|
+
- **`browser network --detail` 在工坊会话里拿不到 body**:它依赖一份盘上缓存,默认路径落在会话可写根之外
|
|
101
|
+
(`EPERM …/opencli/home/.opencli/cache/browser-network`,工具只发一个 `cache_warning`)⇒ 跨调用的 `--detail` 取不到。
|
|
102
|
+
**录制族不依赖它**:它自己那份 `shots/rec-<unit>.jsonl` 就是原文(`_record_detail` 按 key 现取)。
|
|
103
|
+
手工取 body 的另一条路是 `workbench_diy_eval` 里的 `fetch` —— 用它自己的登录在页面里读,返回 JSON;
|
|
104
|
+
或把 `OPENCLI_CACHE_DIR` 指到可写处,再让 `--detail <key>` 生效。
|
|
68
105
|
- **两套 opencli 真相**:PATH 上那份、你机器上 `~/.opencli` 那份、工作台**隔离运行时**那份
|
|
69
106
|
可能不是同一代。工坊一律以**隔离运行时**(`$DSH_HOME/workbench/opencli/`)为准;
|
|
70
107
|
核验用工具,别去看错文件。
|
|
@@ -73,7 +110,7 @@
|
|
|
73
110
|
## 落进 `design.json`
|
|
74
111
|
|
|
75
112
|
写通道填 `writes[]`(一个动作可以打几条写:先建后发布;只有一条写也可以写成 `write: {}`)。
|
|
76
|
-
|
|
113
|
+
字段**七个必填**、一个可空;下表右边是 `design render` 渲染出来的**行名(逐字)**:
|
|
77
114
|
|
|
78
115
|
| 字段 | 渲染出来的行 |
|
|
79
116
|
|---|---|
|
|
@@ -82,9 +119,13 @@
|
|
|
82
119
|
| `credentials` | 凭据从哪来 |
|
|
83
120
|
| `preview` | 有没有预览档(没有就写"没有,一次就是真的") |
|
|
84
121
|
| `readback` | 写完之后怎么回读确认(哪条读 / 出参哪个字段) |
|
|
122
|
+
| `undo` | 配对的那个撤销 / 删除(没有就写"没有配对,一次就是真的")← 第三条写纪律那一格 |
|
|
85
123
|
| `failure` | 失败长什么样(含"可能已经写了") |
|
|
86
124
|
| `probeCommand`(可空) | 当初探明 / 预览用的那次调用(原文) |
|
|
87
125
|
|
|
88
126
|
`design render` 把它写进 README 的 **`### 写通道探明记录`** 那一节,位置在「数据源探明记录」与「验收」之间
|
|
89
127
|
(一条一张表;多条渲染成 `#### 写 N`)。`dsh-wb-module design check` 会把空着的格点名叫出来。
|
|
90
128
|
**没有写通道的模块不用写** —— 它不判"一条都没有",`check` 不红。
|
|
129
|
+
|
|
130
|
+
`undo` 是 2026-10-19 加的(照 opencli 的 `write-without-delete-pair`):**旧模块**跑 `design check`
|
|
131
|
+
会多出一格 `write.undo` 的空格 —— 补一句"没有配对,一次就是真的"(或真配对那条命令)就过,别的不用动。
|