@dsh-workbench/core 0.3.9 → 0.3.11
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/README.md +19 -7
- package/bin/dsh-workbench +2 -2
- package/client.js +1687 -1022
- package/cordis.patch.yml +60 -35
- package/dist/client/client.body.js +1687 -1022
- package/dist/client/index.js +1372 -707
- package/dist/host/index.js +2897 -2335
- package/dist/types/app/client.d.ts +2 -1
- package/dist/types/app/layouts/env-gate/boot-gate.d.ts +1 -1
- package/dist/types/app/layouts/market/use-module-versions.d.ts +13 -2
- package/dist/types/app/layouts/rightbar/MarketView.d.ts +6 -3
- package/dist/types/app/layouts/rightbar/market-act.d.ts +6 -3
- package/dist/types/app/layouts/rightbar/market-rows.d.ts +1 -1
- package/dist/types/app/layouts/rightbar/market-settle-hold.d.ts +2 -0
- package/dist/types/app/layouts/rightbar/market-settle.d.ts +20 -0
- package/dist/types/app/layouts/rightbar/market-shared-rows.d.ts +0 -1
- package/dist/types/app/layouts/rightbar/market-update-verdict.d.ts +41 -0
- package/dist/types/app/layouts/rightbar/market-view-text.d.ts +4 -1
- package/dist/types/app/layouts/rightbar/store.d.ts +14 -0
- package/dist/types/app/layouts/sidebar/LongTaskDot.d.ts +12 -0
- package/dist/types/app/layouts/sidebar/LongTaskHover.d.ts +12 -0
- package/dist/types/app/layouts/sidebar/edition-boot.d.ts +24 -0
- package/dist/types/app/layouts/sidebar/edition.d.ts +22 -0
- package/dist/types/app/layouts/workbench-settings/ConfigSyncItem.d.ts +5 -0
- package/dist/types/app/layouts/workbench-settings/SettingsItem.d.ts +17 -0
- package/dist/types/app/layouts/workbench-settings/SidebarEditionItem.d.ts +11 -0
- package/dist/types/app/layouts/workbench-settings/WorkbenchSettingsSection.d.ts +11 -0
- package/dist/types/modules/panels/model/approval.d.ts +12 -4
- package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -1
- package/dist/types/modules/panels/model/diy-context.d.ts +31 -14
- package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
- package/dist/types/modules/panels/model/diy-record.d.ts +3 -3
- package/dist/types/modules/panels/model/diy-report.d.ts +6 -7
- package/dist/types/modules/panels/model/market-dependency-update.d.ts +21 -0
- package/dist/types/modules/panels/model/market-dispatch.d.ts +1 -0
- package/dist/types/modules/panels/model/market-types.d.ts +1 -0
- package/dist/types/modules/panels/model/market-update-bundle.d.ts +25 -0
- package/dist/types/modules/panels/model/market-verify.d.ts +31 -0
- package/dist/types/modules/panels/unit.d.ts +3 -4
- package/dist/types/shared/api/bundle-install.d.ts +13 -0
- package/dist/types/shared/api/host/approval-gate.d.ts +39 -0
- package/dist/types/shared/api/host/bundle-routes.d.ts +2 -1
- package/dist/types/shared/api/host/diy-artifacts.d.ts +14 -0
- package/dist/types/shared/api/host/diy-modules.d.ts +41 -0
- package/dist/types/shared/api/host/host-routes.d.ts +13 -0
- package/dist/types/shared/api/host/permission-mode.d.ts +9 -0
- package/dist/types/shared/api/host/runtime-seed.d.ts +2 -0
- package/dist/types/shared/api/host/self-update.d.ts +9 -2
- package/dist/types/shared/api/host/sidebar-edition.d.ts +46 -0
- package/dist/types/shared/api/host/workspace-root.d.ts +13 -0
- package/dist/types/shared/api/sidebar-edition.d.ts +14 -0
- package/dist/types/shared/api/unit-board.d.ts +20 -0
- package/dist/types/shared/api/write-outcome.d.ts +15 -0
- package/dist/types/shared/components/IconModules.d.ts +8 -0
- package/dist/types/shared/components/blocker-action.d.ts +16 -0
- package/dist/types/shared/hooks/sidebar-edition.d.ts +7 -0
- package/dist/types/shared/hooks/use-capabilities.d.ts +5 -0
- package/dist/types/shared/hooks/use-capability-gate.d.ts +1 -2
- package/dist/types/shared/hooks/use-host-write.d.ts +1 -1
- package/dist/types/shared/hooks/use-unit-board.d.ts +1 -1
- package/docs/HANDOFF.md +3 -3
- package/docs/OPERATIONS.md +16 -5
- package/package.json +3 -3
- package/preset/diy.patch.yml +2 -2
- package/profile/isolation.patch.yml +11 -0
- package/skills/workbench-module-diy/BUILD.md +62 -131
- package/skills/workbench-module-diy/COPY.md +89 -0
- package/skills/workbench-module-diy/DATASOURCE.md +84 -112
- package/skills/workbench-module-diy/DESIGN.md +75 -109
- package/skills/workbench-module-diy/FAMILY.md +70 -54
- package/skills/workbench-module-diy/KIT.md +77 -133
- package/skills/workbench-module-diy/LINKAGE.md +66 -105
- package/skills/workbench-module-diy/PANEL.md +79 -171
- package/skills/workbench-module-diy/REVIEW.md +67 -81
- package/skills/workbench-module-diy/SKILL.md +57 -96
- package/skills/workbench-module-diy/STEPS.md +66 -69
- package/skills/workbench-module-diy/TOOLING.md +34 -51
- package/skills/workbench-module-diy/TOOLS.md +110 -103
- package/skills/workbench-module-diy/WIRING.md +51 -83
- package/skills/workbench-module-diy/WRITES.md +89 -142
- package/dist/types/app/layouts/config-sync/ConfigSyncSection.d.ts +0 -5
- package/dist/types/shared/api/host/diy-workshop.d.ts +0 -23
- package/dist/types/shared/api/host/workshop-root.d.ts +0 -16
- /package/dist/types/shared/{api/host/diy-workshop.test.d.ts → components/blocker-action.test.d.ts} +0 -0
|
@@ -1,150 +1,87 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 实现:落点、模板、命令、两个面
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
门禁红了看 [REVIEW.md](REVIEW.md)。
|
|
3
|
+
只管怎么落。依赖 / 安装 / 热换 / 发布见 [WIRING.md](WIRING.md);需求见 [DESIGN.md](DESIGN.md) / [STEPS.md](STEPS.md);
|
|
4
|
+
门禁见 [REVIEW.md](REVIEW.md)。
|
|
6
5
|
|
|
7
|
-
## 0
|
|
6
|
+
## 0 模块结构
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
外壳通过服务名 `workbenchUnits` 收下它。所以"造模块"只发生在一个目录里,不动外壳一个字节。
|
|
11
|
-
|
|
12
|
-
它同时是两半:
|
|
13
|
-
|
|
14
|
-
| 半 | 文件 | 跑在哪 | 干什么 |
|
|
8
|
+
| 半 | 文件 | 运行于 | 职责 |
|
|
15
9
|
|---|---|---|---|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
10
|
+
| 宿主 | `src/host.ts` → `dist/host.js` | dsh 宿主进程(Node) | 服务、路由、agent 工具、长任务 |
|
|
11
|
+
| 浏览器 | `src/client.tsx` → `client.js` | 页面 | 卡片、审批明细渲染 |
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
两侧不互相 import,只经服务名 `workbenchUnits` 相接。
|
|
20
14
|
|
|
21
|
-
## 1
|
|
15
|
+
## 1 落点
|
|
22
16
|
|
|
23
17
|
```
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
<工作区>/ ← `workbench_diy_status` 的 `工作区:`
|
|
19
|
+
pnpm-workspace.yaml ← 包声明
|
|
26
20
|
packages/
|
|
27
|
-
module-<unit>/
|
|
21
|
+
module-<unit>/
|
|
28
22
|
```
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
工作区可以是 monorepo / 单包 / 空项目。模块必须落在会话工作区内,包目录由工作区声明
|
|
25
|
+
(`pnpm-workspace.yaml` 的 `packages:`、`package.json` 的 `workspaces`,或根本身即包)。不改工作区根配置。
|
|
31
26
|
|
|
32
|
-
⚠️
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`DSH_WB_SHELL_ROOT` 显式传入)比一次,不同代就在后台把它同步到同代 —— 所以升级之后
|
|
36
|
-
**下一次启动**就对上了(`workbench_diy_status` 里能看到「⏳ 工坊工具链正在后台同步到 <版本>」,
|
|
37
|
-
或同步没成时的「⚠️ 工坊工具链还是上一代(…):<原因>」加一条修法;同步那几行在
|
|
38
|
-
`$DSH_HOME/workbench/seed.log`)。
|
|
27
|
+
⚠️ 无可放代码的包(无 `package.json` 或未声明 workspace)→ 问用户:在此建 workspace,还是去工坊脚手架工作区。
|
|
28
|
+
不改用户仓库为 monorepo。从「造一个模块」进入的会话已在工坊,`<dir>` 即工坊。
|
|
29
|
+
工具链与外壳不同代不用处理:启动时比对并后台同步(`workbench_diy_status` 有状态)。
|
|
39
30
|
|
|
40
|
-
|
|
41
|
-
`pnpm build` / `pnpm exec dsh-wb-module` 和工坊根的 `.bin/dsh-wb-module` 是同一个二进制,
|
|
42
|
-
`check --only kit` 比的也是用户**真正在跑**的那份外壳。别在模块里另装一份——两代并存是静默的
|
|
43
|
-
(你会在模块里看到旧模板、旧报错,而工坊根那份什么都说得好好的)。
|
|
31
|
+
模块内的 `pnpm build` / `pnpm exec dsh-wb-module` 与工作区根同一二进制,不要在模块里另装(会静默出现两代)。
|
|
44
32
|
|
|
45
|
-
## 2
|
|
33
|
+
## 2 从模板开始
|
|
46
34
|
|
|
47
35
|
```bash
|
|
48
|
-
pnpm exec dsh-wb-module init <unit> --dir
|
|
36
|
+
pnpm exec dsh-wb-module init <unit> --dir <工作区>/packages/module-<unit> --template diy --description "<一句话>"
|
|
49
37
|
```
|
|
50
38
|
|
|
51
|
-
`<unit
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
生成的骨架是**能直接跑的**(十三条判据 + 六个脚本开箱全绿;门禁还在,模板改坏了当场报)。
|
|
55
|
-
|
|
56
|
-
**两个模板,按需要挑**(加 `--template <名字>`):
|
|
39
|
+
`<unit>`:小写短横线,同时是单元 id、包名后缀、样式前缀;全局唯一(与已装模块、外壳单元冲突时装配抛错)。
|
|
40
|
+
骨架开箱即绿(十三条判据 + 六脚本)。
|
|
57
41
|
|
|
58
|
-
| 模板 |
|
|
42
|
+
| 模板 | 内容 | 适用 |
|
|
59
43
|
|---|---|---|
|
|
60
|
-
| `minimal
|
|
61
|
-
| `diy` |
|
|
44
|
+
| `minimal`(默认) | 空壳:卡壳、文案、测试;无数据源 | 已明确读什么 |
|
|
45
|
+
| `diy` | 样板卡:宿主一条只读路由 + 五态 | 需要可运行的数据样板(推荐) |
|
|
62
46
|
|
|
63
|
-
|
|
47
|
+
骨架分层:`src/model/**`(纯逻辑)、`src/components/**`(视图 + 同目录 `*.module.css`)、
|
|
48
|
+
`src/messages.ts`、`src/host.ts`、`src/client.tsx`、`scripts/smoke.mjs`、`design.json`。
|
|
49
|
+
模板原文可直接读。非显然项:
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
有状态逻辑才需要 `model/`(`diy` 两样都有,`minimal` 不硬造 `model/`)。骨架里的文件各有其职
|
|
67
|
-
(`minimal` 是空壳那几份,`diy` 多一份 `design.json`、`src/model/`、`src/components/OverviewCard.test.ts` 与 `types/styles.d.ts`,见下面那张 ⚠️):
|
|
68
|
-
|
|
69
|
-
| 文件 | 职责 |
|
|
51
|
+
| 文件 | 要点 |
|
|
70
52
|
|---|---|
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `src/model/**` | 纯逻辑:类型、换算、状态机(`diy` 是 `overview.ts` + 配对测试)。两个运行时都能读,不碰 react / kit / dsh / `node:*` |
|
|
76
|
-
| `src/components/**` | 浏览器侧的视图件,**样式与它同目录**(`X.module.css`;`diy` 是 `OverviewCard.tsx`)。整片算 client 面,宿主侧碰不得 |
|
|
77
|
-
| `src/host.ts` | 宿主面:`manifest` + `mount()`(路由 / 工具);类型从 `src/model/` 引,同一个概念不留两处真相 |
|
|
78
|
-
| `src/client.tsx` | 浏览器面:`manifest` + `card` —— **只剩面**(注册 + 把 props 交给 `components/` 那个组件) |
|
|
79
|
-
| `src/messages.ts` | zh / en 文案;键必须带 `<unit>.` 前缀,两种语言同构 |
|
|
80
|
-
| `src/plugin.test.ts` | 单测:两侧都把自己的面注册进了外壳(`diy` 还多一条宿主路由) |
|
|
81
|
-
| `src/components/X.test.ts` | 组件自己的渲染断言(`diy` 把 `OverviewCard` 的五态各画一遍拆在这儿。**测试文件不吃方言的 S16「文件 200 行」**(2026-10-19 起内置豁免,后缀式与 `__tests__/` 两种命名都算),所以并回一个文件也不会红 —— 拆开只为读着顺) |
|
|
82
|
-
| `vitest.config.ts` | 测试配置(`@/modules/<unit>/` 的别名) |
|
|
83
|
-
| `scripts/smoke.mjs` | `selfcheck`:把 `client.js` 当浏览器加载器跑一遍,看卡片真进没进外壳 |
|
|
84
|
-
| `tsconfig.json` | `paths` 里 `@/modules/<unit>/*` → `./src/*`,值必须是**相对路径** |
|
|
85
|
-
| `types/globals.d.ts` | 经典 JSX 运行时的全局类型垫片 |
|
|
86
|
-
| `types/styles.d.ts` | CSS Modules 的类名表声明(`declare module '*.module.css'`)。`globals.d.ts` 顶层有 import、是模块,那里的 `declare module` 会变成模块增强而不是环境声明 —— 用了 `X.module.css` 就必须有这一份(`diy` 有) |
|
|
87
|
-
| `design.json` | 三张表 + 状态矩阵 + 数据源记录 + 验收清单(**唯一真相**;README 里那一段由它生成) |
|
|
88
|
-
| `README.md` | 这个模块自己的说明 |
|
|
89
|
-
|
|
90
|
-
改哪一个:**画什么改 `src/components/*.tsx` + 它同目录的 `*.module.css`,纯逻辑(换算 / 状态机 / 类型)
|
|
91
|
-
改 `src/model/**`,文案改 `src/messages.ts`;`src/client.tsx` 只留注册那一层**,需要能力再动 `src/host.ts`。
|
|
92
|
-
模板已经把组件套好了外壳的卡壳(`kit.components.Card`)、三态与测试;你只改里面。
|
|
93
|
-
**宿主侧不许 import `src/components/**`** —— 那是跨运行时,`arch` 判据当场红(层号拦不住它:
|
|
94
|
-
components 第 2 层、host 第 10 层)。
|
|
95
|
-
|
|
96
|
-
⚠️ **三张表落在 `design.json`**(`diy` 骨架自带一份填好的,`minimal` 里没有 —— 用它就照
|
|
97
|
-
[DESIGN.md](DESIGN.md) §5 建一个):把它改成你刚问出来的那份,**再改代码**。
|
|
98
|
-
README 里 `<!-- dsh-wb:design:start/end -->` 之间那一段**由它生成**(`design render`)—— 手改会判红。
|
|
99
|
-
|
|
100
|
-
## 3 命令与脚本
|
|
53
|
+
| `arch.config.mjs` | 一行 `moduleArch()`(`@dsh-workbench/module-tools/arch`);层序、运行时、阈值随工具链 |
|
|
54
|
+
| `eslint.config.mjs` | 两行 `moduleLint({ rootDir: import.meta.dirname })`;`rootDir` 钉 tsconfig 根 |
|
|
55
|
+
| `types/styles.d.ts` | 用 `X.module.css` 必须有;`globals.d.ts` 是模块,其 `declare module` 不算环境声明 |
|
|
56
|
+
| `design.json` | 三张表 / 矩阵 / 数据源 / 验收;README 段由它生成(`minimal` 无此文件,按 [DESIGN.md](DESIGN.md) §5 建) |
|
|
101
57
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
dsh-wb-module check # 十三条判据,全跑
|
|
105
|
-
dsh-wb-module check --only cards # 单跑一条(contracts / protocol / bundles / source / arch / lint /
|
|
106
|
-
# primitives / kit / cards / standalone / markers / style / alias)
|
|
107
|
-
dsh-wb-module check --only primitives [--all|--json] # 查询口:dsh 原语里有什么
|
|
108
|
-
dsh-wb-module lint [--fix] # 跑模块自己那份 eslint 配置;--fix 写回
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
三张表的填/查/生成/打勾见 [DESIGN.md](DESIGN.md) §5。
|
|
112
|
-
|
|
113
|
-
⚠️ 写成 `pnpm exec dsh-wb-module …`(工坊里 PATH 上也有同名入口,由启动器挂);
|
|
114
|
-
模块目录里更省事的是那几个脚本(`pnpm build` / `pnpm selfcheck`)。
|
|
58
|
+
修改位置:视图改 `src/components/*.tsx` + 同目录 `*.module.css`;逻辑改 `src/model/**`;文案改 `src/messages.ts`;
|
|
59
|
+
`src/client.tsx` 只留注册。宿主侧不 import `src/components/**`(跨运行时,`arch` 判红)。
|
|
115
60
|
|
|
116
|
-
|
|
61
|
+
## 3 命令
|
|
117
62
|
|
|
118
63
|
```bash
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
64
|
+
dsh-wb-module build # 生成 client.js 与 dist/host.js
|
|
65
|
+
dsh-wb-module check # 十三条判据
|
|
66
|
+
dsh-wb-module check --only cards # 单条(contracts / protocol / bundles / source / arch / lint /
|
|
67
|
+
# primitives / kit / cards / standalone / markers / style / alias)
|
|
68
|
+
dsh-wb-module check --only primitives [--all|--json] # 查询 dsh 原语
|
|
69
|
+
dsh-wb-module lint [--fix] # 模块 eslint;--fix 写回
|
|
125
70
|
```
|
|
126
71
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
72
|
+
用 `pnpm exec dsh-wb-module …`。`package.json` 六个脚本(`build` / `typecheck` / `test` / `selfcheck` /
|
|
73
|
+
`check` / `lint`)必须保留,`contracts` 点名缺失。
|
|
74
|
+
|
|
75
|
+
- `check` 不跑 `pnpm test`;状态、换算、边界测试自己跑。
|
|
76
|
+
- "不适用"不是"通过":`standalone` 需外壳源码在场,`markers` 只扫公开包(DIY 模块 `private`)。
|
|
77
|
+
汇总行会列出未检查项。
|
|
133
78
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`workbench_diy_show` 只上板 —— 见 §7。**上完板自己逐条走一遍(这就是验收那一拍)**:浏览器那 14 把
|
|
137
|
-
(`state` / `get html` / `find` / `click` / `hover` / `type` / `fill` / `keys` / `scroll` / `wait` / `eval` /
|
|
138
|
-
`screenshot` / `network` / `dialog`,argv 与结果原样转发,全表见 [FAMILY.md](FAMILY.md))打的是
|
|
139
|
-
**用户当前那一页**上工坊那块板里的那张卡 —— 用户那一页不会被换掉,但他得把工作台切到前台。
|
|
140
|
-
**看屏 / 动屏那一族(浏览器那 14 把 / `logs_*`)要先 `workbench_diy_debug_start`**(`unit=…`;
|
|
141
|
-
会请他点头一次)—— 没有这场约定它们一律当场拒;不看屏了先 `_pause`、收工 `_stop`。
|
|
142
|
-
卡片有没有报错先看 `workbench_diy_panel` 的「崩过 / 读到了」;真要这一页的控制台,用
|
|
143
|
-
`workbench_diy_logs_watch` → 做那件事 → `workbench_diy_logs_read`(钩子有上限,见 [REVIEW.md](REVIEW.md) 四步取证的 4)。
|
|
79
|
+
安装 / 上板是 agent 工具:`workbench_diy_install`、`workbench_diy_show`、`workbench_diy_status`
|
|
80
|
+
([WIRING.md](WIRING.md) §3)。上板后逐条走查即验收。
|
|
144
81
|
|
|
145
|
-
## 4
|
|
82
|
+
## 4 两个面
|
|
146
83
|
|
|
147
|
-
|
|
84
|
+
宿主面:
|
|
148
85
|
|
|
149
86
|
```ts
|
|
150
87
|
export const name = '<包名>';
|
|
@@ -152,25 +89,19 @@ export const inject = ['workbenchUnits'];
|
|
|
152
89
|
|
|
153
90
|
export function apply(ctx) {
|
|
154
91
|
const units = ctx.get('workbenchUnits');
|
|
155
|
-
if (units === undefined) return;
|
|
92
|
+
if (units === undefined) return;
|
|
156
93
|
ctx.effect(() => units.register({ manifest, mount }), '<unit>: 注册');
|
|
157
94
|
}
|
|
158
95
|
```
|
|
159
96
|
|
|
160
|
-
`mount()` 返回 `{ routes, registerTools, prefs? }
|
|
161
|
-
|
|
162
|
-
`
|
|
163
|
-
否则装配期直接抛;只读路由要表达"出错"时回 `{ ok: false, error: { code, message, hint? } }`(HTTP 仍是 200,外壳原样转发)。
|
|
164
|
-
|
|
165
|
-
浏览器面(`src/client.tsx`):`units.registerUnit({ manifest, messages, card })`。
|
|
166
|
-
`card` 拿到 `{ t, world, refreshToken, onRefresh, onSummary }`(`world.current.workspace` 就是当前项目),
|
|
167
|
-
返回一棵元素树。**三态用外壳给的组件,别自己画**:
|
|
168
|
-
加载 → `kit.components.SkeletonRows`;空 → `kit.components.EmptyState`(不带按钮);
|
|
169
|
-
出错 → `kit.components.BlockerState` + `kit.lib.blockerOfError(error, t)`。
|
|
170
|
-
⚠️ **`idle` 是独立的一格**(没工作区、或取数被 `enabled: false` 关掉时),不许画成"空" ——
|
|
171
|
-
"空"和"还没开始"是两件事,说错了他会去查他根本没配的东西。
|
|
97
|
+
`mount()` 返回 `{ routes, registerTools, prefs? }`。`routes` 挂 `/workbench/api/<路由>`,只许 GET,
|
|
98
|
+
键必须在 `<unit>` 或 `<unit>/…` 之下(否则装配抛错)。错误返回
|
|
99
|
+
`{ ok: false, error: { code, message, hint? } }`(HTTP 200,外壳转发)。`registerTools` 提供模块工具。
|
|
172
100
|
|
|
173
|
-
|
|
174
|
-
|
|
101
|
+
浏览器面:`units.registerUnit({ manifest, messages, card })`。`card` 收到
|
|
102
|
+
`{ t, world, refreshToken, onRefresh, onSummary }`(`world.current.workspace` 为当前项目),返回元素。
|
|
103
|
+
三态用 kit:加载 `kit.components.SkeletonRows`;空 `kit.components.EmptyState`(无按钮);
|
|
104
|
+
错误 `kit.components.BlockerState` + `kit.lib.blockerOfError(error, t)`。
|
|
175
105
|
|
|
176
|
-
|
|
106
|
+
`idle` 是独立态(无工作区或 `enabled: false`),不画成空态。
|
|
107
|
+
卡片在任意输入下不得抛异常或出现 `undefined` / `NaN`(`check --only cards`)。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 文案:先判读者
|
|
2
|
+
|
|
3
|
+
一块模块同时给三种人写字:卡片给人、工具描述给 agent、原始输出给开发者。判错读者,后面全错。
|
|
4
|
+
|
|
5
|
+
## 1 三类读者
|
|
6
|
+
|
|
7
|
+
| 读者 | 读什么 | 判据 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **agent** | 工具描述、参数说明、返回、错误、`reason` / `{blocked}`、中断通知、`context.render` | 读完知道下一次调什么 |
|
|
10
|
+
| **终端用户** | 卡片、空态、阻断、按钮、配置、审批屏、agent 的交代 | 读完知道点哪 / 要不要做,且不写代码 |
|
|
11
|
+
| **开发者** | 日志、`detail`、原始输出 | 排障时能对照、能检索 |
|
|
12
|
+
|
|
13
|
+
审批字段的归属容易混:
|
|
14
|
+
|
|
15
|
+
| 字段 | 读者 |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `needsApproval.question` / `fallback`、`approvalDetail` / `approval.render`、`approval_note` | 终端用户 |
|
|
18
|
+
| `needsApproval.reason`(deny)、`{blocked}` | agent |
|
|
19
|
+
|
|
20
|
+
## 2 两层:主句 + 原文
|
|
21
|
+
|
|
22
|
+
同一处既有用户又有技术细节时,成对写:
|
|
23
|
+
|
|
24
|
+
- **主句**:给人,不含错误码、路径、堆栈、内部词。
|
|
25
|
+
- **原文**:给开发者,原样,放 `detail` / 折叠区 / 日志。
|
|
26
|
+
|
|
27
|
+
两条判据:
|
|
28
|
+
|
|
29
|
+
1. 遮住原文只读主句:用户知道发生了什么、下一步做什么。
|
|
30
|
+
2. 遮住主句只读原文:开发能定位。
|
|
31
|
+
|
|
32
|
+
## 3 人侧
|
|
33
|
+
|
|
34
|
+
1. **可复述**:不写代码的人读完能说出下一步。
|
|
35
|
+
2. **名词看得见**:用界面上的词;`unit` / 套件 / 宿主 / schema / payload / 运行时 不进主句。
|
|
36
|
+
3. **一句一个意思**:不靠括号补解释,一句不放两个动作。
|
|
37
|
+
4. **不露空值**:读不到就说读不到,不写 `0` / `undefined` / `NaN` / 裸键名。
|
|
38
|
+
|
|
39
|
+
状态四义、时间写相对时间、单位跟数字:见 [PANEL.md](PANEL.md) §7。
|
|
40
|
+
审批那一屏的写法:见 [DESIGN.md](DESIGN.md) §3 —— 标题一句,事实进详情,明细一行一个事实。
|
|
41
|
+
中文与英文都写;英文重写,不直译。
|
|
42
|
+
|
|
43
|
+
## 4 agent 侧
|
|
44
|
+
|
|
45
|
+
**agent 自己推得出的,不写。** 工具名、参数名、schema、上一次返回已经说了的,不重复第二遍。
|
|
46
|
+
|
|
47
|
+
- 删:字段清单、枚举释义、参数名复述、理由、输出里已有的话。
|
|
48
|
+
- 留:写与危险、跨工具下一步与字面量、非显然的默认与上限、会误导的坑、安全事实。
|
|
49
|
+
- 描述:一句“是什么” + 一句“什么时候用”;不写“这个工具用于……”。
|
|
50
|
+
- 返回:事实 + 总数 / 分页 + 下一次调用;ID、版本、单位原样。
|
|
51
|
+
- 错误:发生了什么 + 能不能重试 + 下一步;不甩堆栈。
|
|
52
|
+
- 通知:自包含 —— 谁做了什么 + 哪件事变了 + 现在做什么。
|
|
53
|
+
|
|
54
|
+
加字先减字。模块工具的提示词没有字数闸,自己守。
|
|
55
|
+
|
|
56
|
+
## 5 开发侧
|
|
57
|
+
|
|
58
|
+
- **原文**:日志、堆栈、命令不翻译、不美化。
|
|
59
|
+
- **可检索**:保留错误码、路径、ID、时间戳。
|
|
60
|
+
- **不进主句**:这些内容用户能看到时,折叠并标注“原始输出 / 堆栈 / 命令”;主句不依赖它。
|
|
61
|
+
|
|
62
|
+
日志一行:目标 + 发生了什么 + 关键值原样。不写“出错了”这种没有定位信息的句子。
|
|
63
|
+
|
|
64
|
+
## 6 硬规则
|
|
65
|
+
|
|
66
|
+
- 读不到写读不到,不写 0。
|
|
67
|
+
- 主句只放人话;错误码、路径、堆栈、内部词放原文。
|
|
68
|
+
- 事实原样:ID、版本、单位、时间戳不改写、不四舍五入。
|
|
69
|
+
- 不引用不存在的工具、字段、页面、命令。
|
|
70
|
+
- 不写“系统内部错误 / 请稍候”这类没有信息的句子。
|
|
71
|
+
|
|
72
|
+
## 7 验
|
|
73
|
+
|
|
74
|
+
1. 真卡片:五态、两语各看一遍;`dsh-wb-module check --only cards` 抓 `undefined` / `NaN` / 裸键。
|
|
75
|
+
2. 遮住测试两条都过。
|
|
76
|
+
3. 工具:注册后读描述,逐句问“模型推得出来吗”;真调一次读返回;漏参数读报错。
|
|
77
|
+
4. 拿五个用户会说的话试:只凭描述能不能挑中这把工具。
|
|
78
|
+
|
|
79
|
+
## 8 例
|
|
80
|
+
|
|
81
|
+
| 面 | 改前 | 改后 |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| 空态 | 暂无数据 | 这个环境还没有构建记录。换一个环境,或先跑一次构建。 |
|
|
84
|
+
| 阻断 | 系统内部错误,请稍后重试 | 这次没读到发布记录。检查网络后重试。 |
|
|
85
|
+
| 按钮 | 确定 | 发布到生产 |
|
|
86
|
+
| 描述 | 用于查看构建状态的工具 | 看某个环境最近一次构建:状态、耗时、提交;用户说“发布没成功”就先调它。 |
|
|
87
|
+
| 返回 | 操作成功 | 已发布 v1.2.3 到生产;下一步 `_build` 看结果。 |
|
|
88
|
+
| 拒绝 | 不允许 | 这个环境是生产;先在确认条上点「发布到生产」。 |
|
|
89
|
+
| 日志 | 出错了 | `[cmdb] release v1.2.3 failed: HTTP 502 (retryable)` |
|
|
@@ -1,130 +1,102 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 数据源:探一条读,再固化
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
干净机器上你也没有现成的 skill 可抄。所以这一步是**探查**,不是查手册。
|
|
3
|
+
用户只说"我要看 X",不提供接口。干净机器上没有现成 skill。步骤是探查,不是查手册。
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## 判域
|
|
7
6
|
|
|
8
|
-
| 情况 |
|
|
7
|
+
| 情况 | 域 |
|
|
9
8
|
|---|---|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
9
|
+
| 用户已在浏览器登录过该系统 | DIY:用 opencli 复用登录探(下文四步) |
|
|
10
|
+
| 要新凭据(账号 / 密钥 / token) | 工程:停,说清谁配什么 |
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
其余几类(本地文件 / 已有命令 / 不要凭据的 GET → 直接造;新服务 / 新依赖 / 改外壳 → 工程域):[DESIGN.md](DESIGN.md) §0 那张全表。
|
|
12
|
+
判据是"要不要新凭据",不是"要不要接新平台"。其余类型见 [DESIGN.md](DESIGN.md) §0。
|
|
15
13
|
|
|
16
14
|
## 四步
|
|
17
15
|
|
|
18
|
-
1.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
①
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
所以"切格 + 按列序取值"是两个纯函数,能单测。很多公开站点一条 XHR 都不发,就该走这条。
|
|
79
|
-
- **工作台刚重启时桥还没起来**:第一次读会以"命令跑不起来"失败(不是"数据读不到")——
|
|
80
|
-
等一秒再读一次就好。超时**不**重试:那已经等满了。这条与上面两条一样,`browserRead` 里都写好了。
|
|
81
|
-
3. **试一条最小读** —— 先证明"能拿到",再写模块:
|
|
82
|
-
- 拿得到结构化输出 → 记下字段形状
|
|
83
|
-
- 要登录但已过期 → 记下"未登录"长什么样
|
|
84
|
-
- 超时 / 被墙 → 记下这两种失败的形状
|
|
85
|
-
这一步的产出是**一条可重复的命令或请求**,不是"我探过了"。
|
|
86
|
-
4. **固化进模块** —— 先把它照抄进 `design.json`(只有一条读写 `datasource{}`;一条卡有好几条读写 `reads[]`,一条一行;字段一样:哪条读 / 参数 / 凭据 /
|
|
87
|
-
没登录长什么样 / 超时长什么样 / 当初那次调用的原文),再写进模块的**宿主半侧**:用 `@dsh-workbench/shared/host` 导出的
|
|
88
|
-
`browserRead({ shell, script, url, loginUrl })`(**接口读**:探明的那一段脚本原样搬进去就行 ——
|
|
89
|
-
两拍、重试、平台码映射、`message` / `hint` / `detail` / `helpUrl` 四个字段都在它里面;
|
|
90
|
-
会话不传就落在读那条上,想给自己一条专线再传 `session: "<unit>"`)或
|
|
91
|
-
`browserFind({ shell, css, url, session })`(**DOM 读**:它替你问"现在停在哪一页"(`browser get url`),
|
|
92
|
-
不在就 `open` 再读一遍,失败时把 opencli 自己说的原因带出来),或者只读路由里的一个 HTTP GET。之后卡片只依赖**这一条**;渲染怎么改都不动它。
|
|
93
|
-
|
|
94
|
-
这一步只管**读**。要 agent 真去**改**东西(提交 / 发布 / 建单 / 改配置),那半边走 [WRITES.md](WRITES.md)。
|
|
95
|
-
|
|
96
|
-
`opencli` 是**侦察兵,不是日常通道**:探明之后,日常读必须是一条固定的、便宜的、能解释的调用。
|
|
97
|
-
|
|
98
|
-
## 固化时一并写掉三态
|
|
99
|
-
|
|
100
|
-
拿不到数据是常态(第一次读、登录过期、站点改版、接口超时)。卡片**必须**画得出来:
|
|
101
|
-
|
|
102
|
-
| 情况 | 卡片说什么 |
|
|
16
|
+
1. **问入口** —— "你平时在哪个网页 / 软件里看这个?"记下入口。不问"用 argocd 还是 jenkins"。
|
|
17
|
+
把用户的需求翻成站点 / 系统关键词是你的工作。
|
|
18
|
+
|
|
19
|
+
2. **按四层找数据**:
|
|
20
|
+
|
|
21
|
+
① **opencli 自带**:`workbench_diy_recon`(`keyword=<站点 / 系统 / 域名>`,或 `url` 取域名)。
|
|
22
|
+
返回可用能力:站点、命令、`strategy`(PUBLIC / COOKIE / INTERCEPT / UI)、是否需要用户 Chrome 登录、
|
|
23
|
+
可直接复用的命令。**命中即复用,不装不审批。** 官方发现指引:`opencli skills read opencli-usage`。
|
|
24
|
+
一个词可能带出多组(如 `arxiv` 带 `hf`、`semanticscholar`),看全。优先 `public`;`cookie` / `ui` /
|
|
25
|
+
`intercept` 先确认用户登录状态。一张卡可有多条读(可跨站点):`design.json` 用 `reads[]` 一条一行,
|
|
26
|
+
每条写参数 / 凭据 / 未登录 / 超时,卡片按条给失败态。
|
|
27
|
+
|
|
28
|
+
② **这台机器另装的插件**:recon 一并列出(或 `opencli plugin list`)。参数用 `--help`,或
|
|
29
|
+
`workbench_diy_spec`(`command='<site> <name>'`)取 `--help -f json` 原文(插件与内置均可)。
|
|
30
|
+
|
|
31
|
+
③ **官方有、这台机器没有** → 安装:`opencli plugin install github:user/repo`、`opencli external install <名>`,
|
|
32
|
+
或模块清单 `manifest.env.plugins`。安装改这台机器且执行第三方代码 → 需用户审批;优先内网或官方源。
|
|
33
|
+
|
|
34
|
+
④ **都没有** → 开发:一次性探用下文的 eval;长期用写 adapter(`opencli skills read opencli-adapter-author`,
|
|
35
|
+
失效用 `opencli-autofix`)。
|
|
36
|
+
|
|
37
|
+
**探**:`workbench_diy_eval`(`read=<地址>, args=["<回 JSON 的脚本>"]`;无接口给 `css=<选择器>`,
|
|
38
|
+
走固定 DOM 读,返回 `{entries:[{text}]}`)。它复用用户浏览器登录,并把失败归一成阻断屏错误码;
|
|
39
|
+
两拍、重启重试、平台码映射都在工具内。目标:找到承载数据的位置(列表页或其后的 XHR),
|
|
40
|
+
记 URL、参数、凭据来源。
|
|
41
|
+
|
|
42
|
+
- 不知道数据在哪:先跑 `performance.getEntriesByType('resource')`,筛 `/api/`、`.json`。
|
|
43
|
+
有 → 接口读;无但页面有整块表格 → DOM 读。判断一次即可。
|
|
44
|
+
- `opencli` 随工作台预装(隔离运行时 `$DSH_HOME/workbench/opencli/`,启动体检安装,不碰 `~/.opencli`)。
|
|
45
|
+
未装好时工具会报"隔离运行时还没装好";转述原文,不猜 URL。
|
|
46
|
+
- 脚本约定:不在站点 → `{ ok: false, code: "not-on-site" }`;登录过期 → `{ ok: false, code: "not-logged-in" }`
|
|
47
|
+
(工具归一为 `auth-required`);成功 → `{ ok: true, ...数据 }`。
|
|
48
|
+
- 新 session 的 `eval` 会静默开 `about:blank`(后台容器窗,不抢焦点)。脚本第一行判断
|
|
49
|
+
`location.origin`;不在站点则 `open` 该页,等约 1 秒再 eval。两拍都用 shared 的 `backgroundLine`
|
|
50
|
+
拼(前缀 `env OPENCLI_WINDOW=background`),不要直接拼 `opencli browser …`。
|
|
51
|
+
- 读与屏用不同会话:`browserRead` / `browserFind` 默认 `workbench-read`;`workbench` 是看 / 动族的
|
|
52
|
+
屏会话。同名会把用户的页导航走。需要独立标签就传 `session: "<unit>"`。
|
|
53
|
+
- DOM 读不要在 `eval` 里 `querySelectorAll`:宿主产物禁止 `document.`,且无法单测。探时用
|
|
54
|
+
`workbench_diy_find`(`read=…, args=["--css", "<选择器>"]`);固化用 `browserFind`
|
|
55
|
+
(`find --css … --limit N --text-max M`,返回 `{ entries: [{ text, tag, nth }] }`;
|
|
56
|
+
一行单元格按换行拼在 `text` 内,"切格 + 按列取值"做成两个纯函数)。
|
|
57
|
+
- 工作台刚重启时桥未就绪:首次读报"命令跑不起来",等一秒重读;超时不重试。
|
|
58
|
+
|
|
59
|
+
3. **试一条最小读** —— 先证明能拿到,再写模块。记录:结构化字段形状;未登录长什么样;超时 / 被墙的形状。
|
|
60
|
+
产出必须是一条可重复的命令或请求。
|
|
61
|
+
|
|
62
|
+
4. **固化进模块** —— 先写入 `design.json`(单读 `datasource{}`;多读 `reads[]` 一条一行;字段:
|
|
63
|
+
哪条读 / 参数 / 凭据 / 没登录 / 超时 / 当时调用原文),再写宿主半侧:用 `@dsh-workbench/shared/host` 的
|
|
64
|
+
`browserRead({ shell, script, url, loginUrl })`(接口读;两拍、重试、错误码映射、`message` / `hint` /
|
|
65
|
+
`detail` / `helpUrl` 内置;不传 `session` 用读会话)或 `browserFind({ shell, css, url, session })`(DOM 读;
|
|
66
|
+
自动判断当前页、必要时 `open`),或一个只读 HTTP GET。卡片只依赖这一条。
|
|
67
|
+
写操作走 [WRITES.md](WRITES.md)。
|
|
68
|
+
|
|
69
|
+
`opencli` 是**侦察兵,不是日常通道**:探明后,日常读必须是一条固定、便宜、可解释的调用。
|
|
70
|
+
|
|
71
|
+
## 三态
|
|
72
|
+
|
|
73
|
+
拿不到数据是常态。卡片必须画得出:
|
|
74
|
+
|
|
75
|
+
| 情况 | 文案 |
|
|
103
76
|
|---|---|
|
|
104
|
-
| 没登录 / 凭据过期 | "去 XX 登录一下"
|
|
77
|
+
| 没登录 / 凭据过期 | "去 XX 登录一下"(可点,指到用户常用入口) |
|
|
105
78
|
| 超时 / 网络不通 | "这次没读到,稍后重试" + 重试 |
|
|
106
|
-
|
|
|
79
|
+
| 站点改版、字段缺失 | "读到的形状和预期不一样";不显示空白、不显示 0 |
|
|
107
80
|
|
|
108
|
-
|
|
81
|
+
任何输入(待机 / 在读 / 有值 / 空 / 读不到)下抛异常或出现 `undefined` / `NaN` 的卡片都会让用户认为"卡片不出来"。
|
|
82
|
+
`check --only cards` 拦这条([REVIEW.md](REVIEW.md))。
|
|
109
83
|
|
|
110
|
-
##
|
|
84
|
+
## 错误码
|
|
111
85
|
|
|
112
|
-
|
|
113
|
-
(例:`AUTH_UNAUTHORIZED`)**别直接透传**,先映射到这张表里的一个:
|
|
86
|
+
平台码(如 `AUTH_UNAUTHORIZED`)先映射,不透传:
|
|
114
87
|
|
|
115
|
-
|
|
|
88
|
+
| code | 用户看到 |
|
|
116
89
|
|---|---|
|
|
117
|
-
| `auth-required` + `helpUrl` | "需要登录 XX" +
|
|
118
|
-
| `cli-timeout` |
|
|
119
|
-
| `cli-exec-failed` |
|
|
120
|
-
| `permission-denied` + `helpUrl` | "没有权限" +
|
|
121
|
-
|
|
|
90
|
+
| `auth-required` + `helpUrl` | "需要登录 XX" + 去登录 + 重试(无 `helpUrl` 只剩重试) |
|
|
91
|
+
| `cli-timeout` | 细提示(不换整卡)+ 重试 |
|
|
92
|
+
| `cli-exec-failed` | 整卡"读不到" + 重试;`hint` 写清确认什么 |
|
|
93
|
+
| `permission-denied` + `helpUrl` | "没有权限" + 权限页 + 重试 |
|
|
94
|
+
| 其他 | 整卡显示 `message` + 重试 |
|
|
122
95
|
|
|
123
|
-
|
|
124
|
-
输出 —— 写全了用户才知道"这一屏为什么长这样、下一步点哪"。
|
|
96
|
+
字段位置:`message` 标题、`hint` 可照做的一句、`detail` 折叠的原始输出。
|
|
125
97
|
|
|
126
|
-
##
|
|
98
|
+
## 门禁
|
|
127
99
|
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
100
|
+
- 数据源在宿主半侧;卡片只消费返回。
|
|
101
|
+
- 只读路由错误返回 `{ ok: false, error: { code, message, hint? } }`(HTTP 200,外壳转发)。
|
|
102
|
+
- 验收走正常 / 空 / 出错三条路;真机造不出的状态写清如何触发,并在 `plugin.test.ts` 各喂一遍。
|