dsh-wkgeo-ops 0.2.0 → 0.2.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wkgeo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,185 +1,224 @@
1
- # dsh-wkgeo-ops · DSH 插件「网站运营」
2
-
3
- 把本机 `wkgeo` CLI(`F:\cloudbase\cli\wkgeo.mjs`)的站点运营能力接进 DeepSeek Harness:
4
- **agent 7 个工具** + **左侧栏「网站运营」入口(点开是中央面板)** + **右侧边栏「网站运营」面板(含三步建站向导)**。不新增任何 npm 依赖,只用 Node 内置模块。
5
-
6
- - key / 包名 / cordis 插件名:`dsh-wkgeo-ops`
7
- - 版本:0.1.0 v4(private,不发布到 npm)
8
- - 宿主:DSH Web 客户端(`dsh.client.platform = "web"`)+ cordis 服务端插件
9
- - v2 口径(老板 2026-10-30):**三步向导**(①站点名 ②语言 ③内容来源)+ **模板 / 网址 / 文件 三选或多选**
10
- - v4 口径(老板 2026-10-30):「初始化先选**站点根目录** → **站点卡片**一个一个显示(能点进去)→ 详情里有**本地文件夹**」;不再用一张表铺列表
11
-
12
- ---
13
-
14
- ## 1. 目录结构
15
-
16
- ```
17
- integrations/dsh-wkgeo-ops/
18
- ├─ package.json # manifest:main / dsh.bundle.patch / dsh.client / exports / peerDeps
19
- ├─ cordis.patch.yml # 装载层:- insert: id/name = dsh-wkgeo-ops
20
- ├─ lib/
21
- │ ├─ wizard.js # 纯函数:三步向导状态机 + 来源归一 + 能力探测 + 命令构造 + 建站闸门
22
- │ └─ index.js # 服务端 cordis 插件:7 个工具 + 6 条路由 + 执行层(可单测)
23
- ├─ client/
24
- │ └─ client.js # Web 客户端:左侧栏入口 + 面板(初始化/卡片网格/详情/向导/日志 五态)
25
- └─ README.md # 本文件
26
- ```
27
-
28
- ## 2. 三步向导(面板主流程)
29
-
30
- | 步 | 内容 | 规则 |
31
- | --- | --- | --- |
32
- | ① | **站点名** | 必填,≤60 字;回车即下一步 |
33
- | ② | **语言** | 默认**中文**(`zh-CN`),可选**英文**(`en`) |
34
- | ③ | **内容来源:模板 / 网址 / 文件** | 三个入口**同时摆出来**,复选框式**可单选可多选**;勾谁展开谁的输入 |
35
-
36
- 入口位置(老板口径):
37
-
38
- - **没有站点时**:空态第一行就是整宽主按钮「**+ 创建网站(第一个)**」。
39
- - **有站点时**:列表最末尾固定一行虚线「**+ 创建新网站(最后一个)**」,顶部也有「+ 创建网站」小按钮。
40
-
41
- 第三步的三种来源:
42
-
43
- | 来源 | 角色 | 勾选后展开的输入 |
44
- | --- | --- | --- |
45
- | 模板 | **设计骨架** | 模板中心下拉(默认模板 + `WKGEO_TEMPLATE_KEYS` 配置的名字)+ 手填模板名/uuid |
46
- | 网址 | **结构与内容来源** | 老站 URL(`http(s)://…`) |
47
- | 文件 | **结构与内容来源** | 本地目录(逐个 `*.html`)或 zip 路径 |
48
-
49
- **合并语义**(UI 文案与文档同一份 `MERGE_SEMANTICS`,非装饰):
50
-
51
- 1. 模板 = 设计骨架;网址 / 文件 = 结构与内容来源。
52
- 2. 只选网址或文件 → 先用它生成模板(`template init`),再建站。
53
- 3. 模板 + 网址/文件 → 模板做骨架,内容按栏目灌入。
54
- 4. **做不到的组合必须明确标「未填充」**(`unfilled[]` + 面板黄色提示),**不许静默忽略**。
55
-
56
- 深层链接(截图 / 复现用):`?wizard=1&step=3&name=…&lang=zh-CN&sources=template,url&templateKey=…&url=…`。
57
-
58
- ## 3. 工具清单(agent 侧,7 个)
59
-
60
- | 工具 | 参数 | 底层命令 | 返回要点 |
61
- | --- | --- | --- | --- |
62
- | `wkgeo_sites_list` | 无 | `sites list --json` | `{ ok, count, sites[] }`,每站 `id/name/status/shortHost/shortUrl/domain/domainVerification/provisionState/relation/cmsOnly` |
63
- | `wkgeo_site_status` | `site`* | `sites get <id> --json` | `{ ok, site }`:状态、短域、域名验证(含 `verifiedAt`)、证书、备案、开通状态 |
64
- | `wkgeo_site_preview` | `site`*、`path` | `preview <path> --site <id> --json` | `{ ok, url, siteId, path }` → `https://wkgeo.com/cms/preview/<站点ID>/about/` |
65
- | `wkgeo_publish` | `site`*、`confirm`*、`wait` | `publish --site <id> --confirm [--no-wait] --json` | 缺 `confirm:true` **不执行**,回 `{executed:false,needsConfirm:true,reason,risk}` |
66
- | `wkgeo_site_create` | `name`*、`lang`、`source`、`templateKey`、`url`、`dir`、`zipPath`、`confirm`* | 计划式执行:`sites create` →(`lang save`)→(`templates save`×N)→(`import --apply`) | 缺 `confirm:true` 只回 `{executed:false,needsConfirm:true,plan[],risk}`;有 confirm 才逐步执行,回 `{ok,siteId,steps[],unfilled[],gaps[],warnings[]}` |
67
- | `wkgeo_template_from_url` | `url`*、`out`、`site`、`confirm` | `template init --from-url <url> [--out <dir>] --json`(长任务 300s) | CLI 没这个子命令时 → `{supported:false,executed:false,gap,command,fallback}`,**0 次 CLI 调用**,不假装成功 |
68
- | `wkgeo_template_from_dir` | `dir`、`zipPath`、`out`、`site`、`confirm` | `template init --from-dir <dir> [--out <dir>] --json`(300s);没有该子命令时降级 `templates save` | 只给 zip → `{supported:false,gap:{code:'ZIP_UNSUPPORTED'}}`;无 `site`/`confirm` → `needsConfirm:true` + 将执行的命令清单 |
69
-
70
- `*` = 必填。实现细节:
71
-
72
- - **`parameters` 必须是 raw JSON Schema**(`{ type:'object', properties, required }`),由 `paramsSchema(properties, required)` 统一生成,`required` 恒输出;传「属性名 → 属性 schema」的映射会被模型 API 判 `schema must be a JSON Schema of 'type': "object", got 'type: null'`(`INVALID_REQUEST`,整轮被打回)。回归脚本:`verify/tool-schema.mjs`(用 DSH 随包的 ajv 编译 7 个 schema,期望 `RESULT: ALL PASS`、exit 0)。
73
- - 一律 `spawn(node, [cli, ...args])`(`shell:false`、`windowsHide:true`);**普通命令硬超时 30s**,**长任务(模板生成 / 建站 / 导入)300s**(`LONG_TIMEOUT_MS`),超时 `SIGKILL` 回 `code:"TIMEOUT"`;stdout 上限 4MB。
74
- - 只解析 CLI 的 JSON 信封;失败只回 `error.code/message/hint`,**不打印任何 token、不落日志**。
75
- - node 解析顺序:`WKGEO_NODE_BIN` → `process.execPath`(仅当是 `node.exe`)→ `node`;CLI 路径可用 `WKGEO_CLI` 覆盖,默认 `F:\cloudbase\cli\wkgeo.mjs`。
76
- - **能力探测**(10 分钟缓存):读 `template init --help` / `sites create --help` / `import --help` 文本,判断 `--from-url`、`--from-dir`、`--lang`、`--url` 是否真的存在;**探测不到就不硬塞参数**,改走降级路径并在返回里标 `capabilities`。
77
- - 模板中心下拉的选项来自 `WKGEO_TEMPLATE_KEYS`(逗号分隔的内置模板名/uuid)+ 一个「默认模板(不指定)」+ 手填框;CLI 没有「列模板」的命令,缺口见 §7。
78
-
79
- ## 4. 面板(Web 客户端侧)
80
-
81
- - **左侧栏入口(v3)**:`sidebar.panellist`(`id: wkgeo-ops`、`order: 30`、图标 `OpsIcon`)+ `main`(`key: wkgeo-ops`,渲染同一个 `PanelBody`)。两条的 id/key 必须相同:①只注册就有点不开、②只注册就先没图标;**装插件本身不会自动往左栏加菜单,这两条必须自己写**。停用插件时若该面板正激活,先 `layout.selectPanel(null)` 把界面交还对话。
82
- - 注册 `sidebarRightTabs`(`id: wkgeo-ops`、`priority: fallback`)+ `sidebar.right.pane.tab` / `.title` 两个 slot,与 `dsh-bili-panel` 范式一致;右栏页签与左栏入口渲染的是**同一个** `PanelBody`。
83
- - **五态(v4)**:
84
- 1. **初始化**(本机还没设站点根目录时):一张居中卡片「第一步:先选站点根目录」——手输绝对路径,或点「选择根目录」进目录浏览器一层层点。**强制第一步、不能跳过**:根目录没设就只显示这张卡,不给卡片列表(唯一例外是服务端 config 接口不可用 `cfgDown`,那时退回列表并在顶栏提示「接口不可用」)。
85
- 2. **卡片网格**:一个站一张卡(站名 + 状态 pill + 主机链接 + 类型/验证/短码 tags + 底部「📁 本地文件夹」行),末尾虚线「+ 新建站点」卡;整张卡可点。
86
- 3. **详情**:点卡进去 → 返回按钮 + 站点信息 7 行 + 本地文件夹面板(绝对路径 / 相对根目录 / HTML 文件清单 / 换文件夹 / 重新扫描 / 打开站点)。
87
- 4. **向导**(三步,见 §2)、5. **执行日志**。
88
- - **目录选择器是固定浮层弹窗(v4.1)**:`position: fixed` 全屏遮罩 + 720px 白卡(`role="dialog"`、`aria-modal`),点遮罩即取消;顶部手输路径 + `↑ 上一级` + `→` 跳转,中间子目录列表、底部「取消 / 用这个目录」。**不要改回普通区块**:面板容器高度有限,内联区块会被挤成 0 高(老板 2026-10-30 报「点了没弹出选择器」,根因见下一条)。
89
- - **真 React 两个坑(v4.1 修)**:① 目录取数的 `useEffect` **必须带依赖数组 `[path]`**——无依赖写法每轮渲染都重跑,React 先执行上一轮清理把 `alive` 置 false,首个 fetch 响应被丢弃且守卫拦住重试,表现就是永远停在「读取目录中…」(自写的轻量垫片不复现,真 React 必现);② 受控输入框用 `onChange`,不要用 `onInput`(真 React 里等价只读)。
90
- - **页签 guide 的 title / description 必须是函数(v4.2 修,事故级)**:`sidebarRightTabs.register({ guide: [{ title: '站点状态' }] })` 这种**字符串写法**会让 DSH 在渲染右栏 guide 时抛 `TypeError: entry.title is not a function`;它发生在右栏同一个 React 树里 → **整个右侧栏被卸载成一片空白**(连 DSH 自带的「开始」页签内容也没了),左侧栏 / 中央面板不受影响。正确写法 `title: () => '…'`(dsh-image-gen 即如此),本仓库再用 `asLabel()` 兜底。回归:`node integrations/dsh-wkgeo-ops/verify/panel-registration.mjs` → `RESULT: ALL PASS`。
91
- - **v5(2026-10-30):面板根节点自己撑满宽度** —— `STYLE.wrap` 必须带 `width:'100%'; minWidth:0; flex:'1 1 auto'`,顶栏 `STYLE.bar` 带 `flexWrap:'wrap'`、标题/计数 `whiteSpace:'nowrap'`;否则在右栏(flex 项、宽度窄)里整块会收缩成一竖条。
92
- - **v5:右栏与中央共用同一个入口组件 `OpsPanel`**(apply 内定义:`h(PanelBoundary, null, h(PanelBody, ctx))`),两处槽位都直接传这个组件(和 `dsh-bili-panel` 同形),**不要再套 PaneShell 之类的额外外壳**;面板标题后有一个 `v5` 小标记,用来确认客户端加载的是哪一版。
93
-
94
- - 取数路由(服务端自注册、与 DSH GUI 同源):
95
- - `GET /wkgeo-ops/sites`(`?refresh=1` 强刷,默认 15s 缓存)
96
- - `GET /wkgeo-ops/templates` → `{ok, choices[], manual, note, gap, mergeSemantics[]}`
97
- - `POST /wkgeo-ops/site-create` → `application/x-ndjson` 逐步流(先 `{type:'plan'}` 再 `{type:'step'}`…最后 `{type:'done'}`);**缺 `confirm:true` 一律 200 + `needsConfirm:true`,不执行**;非 POST → 405。
98
- - **`GET|POST /wkgeo-ops/config`**(v4):读/写本机站点根目录。GET → `{ok, rootDir, exists}`;POST `{rootDir}` 保存(相对路径 400 `ROOT_NOT_ABSOLUTE`、不存在 400 `ROOT_MISSING`、不是目录 400 `ROOT_NOT_DIR`);`{rootDir:''}` 清空。
99
- - **`GET /wkgeo-ops/browse?path=`**(v4):目录选择器数据源,回 `{ok, path, parent, dirs[], files[], htmlCount, insideRoot}`。**设了根目录后只允许浏览根目录之内**(越界 403 `OUTSIDE_ROOT`);没设根目录时可从家目录/盘符起随便翻。
100
- - **`GET|POST|DELETE /wkgeo-ops/site-folder`**(v4):站点 ↔ 本地文件夹映射。GET 无参回全部 `folders{}`、`?site=<id>` 回单站(含 `rel`/`exists`/`htmlCount`/`files[]`,未绑定 `bound:false`);POST `{siteId, dir}` 绑定(要求已有根目录 `ROOT_UNSET`、绝对路径 `DIR_NOT_ABSOLUTE`、在根目录内 `OUTSIDE_ROOT`、目录真实存在 `DIR_MISSING`/`DIR_NOT_DIR`);DELETE `?site=<id>` 解绑。
101
- - **存储位置**:`<DSH_PROFILE_DIR 或 ~/.dsh>/wkgeo-ops/config.json`,形状 `{ rootDir, folders: { [siteId]: dir }, updatedAt }`。**只存本机**,不上传、不写进站点、不进 CLI。
102
- - 样式只用 `--dsw-alias-*` CSS 变量,不引外部资源、不引 CSS 文件。
103
- - 拿不到 `webServer` 服务时只跳过路由(面板顶栏提示「接口不可用」并退回列表),**7 个工具照常可用**。
104
-
105
- ## 5. 安装(老板侧)
106
-
107
- 前置:`wkgeo` CLI 已登录(`node F:\cloudbase\cli\wkgeo.mjs login`),DSH 已在运行。
108
-
109
- **A. 拷贝插件(必做)**
110
-
111
- ```powershell
112
- Copy-Item -Recurse -Force 'F:\cloudbase\integrations\dsh-wkgeo-ops' "$env:USERPROFILE\.dsh\plugins-src\dsh-wkgeo-ops"
113
- ```
114
-
115
- **B. 让 DSH 装载它(二选一)**
116
-
117
- 1. **有插件安装 UI**(DSH 设置里的插件页,「从本地目录安装」):选到 `%USERPROFILE%\.dsh\plugins-src\dsh-wkgeo-ops`,装上后重启 DSH。
118
- 2. **手动挂 profile**(只需两处,**不用**改 `cordis.patch.yml`):
119
- - `%USERPROFILE%\.dsh\profiles\desktop\package.json` 的 `dependencies` 加
120
- `"dsh-wkgeo-ops": "file:C:/Users/Administrator/.dsh/plugins-src/dsh-wkgeo-ops"`
121
- - 同文件 `dsh.profile.bundles` 数组加一项 `dsh-wkgeo-ops`
122
- - 重启 DSH。
123
-
124
- > `cordis.patch.yml` 里**不需要**再写 `- id: dsh-wkgeo-ops`:`dsh.profile.bundles` 里的包是按 bundle 层挂载的,包内自带的 `cordis.patch.yml` 会自动应用(本机 profile 的 patch 文件里就没有该条目,插件照样正常加载)。
125
-
126
- **C. 验证(装完应同时出现两处入口 + 7 个工具)**
127
-
128
- | 位置 | 现象 |
129
- |---|---|
130
- | **左侧栏** | 第 5 个图标(显示器+双卡片,`order: 30`,排在「工作台」后面);点它 → 中央区渲染「网站运营」面板 |
131
- | 右侧栏 | 「网站运营」页签(与左栏入口并存,两条路都能进) |
132
- | 对话 | agent 调 `wkgeo_sites_list` 返回站点数组 |
133
-
134
- 左栏入口是插件自己注册的(`client/client.js` 的 `sidebar.panellist` + `main` 两个槽位、共用 id `wkgeo-ops`),**装插件本身不会自动往左栏塞菜单**——所以看到图标就说明这版代码已生效;只看到右侧栏页签则说明装的是旧版(左栏入口是 2026-10-30 加的)。
135
-
136
- **D. 别人(另一台机器 / 另一个 profile)首次安装的差异**
137
-
138
- - 左栏 / 右栏入口与工具都随包走,**换机器一样出现**;`client` 侧不依赖本机路径。
139
- - ⚠️ **唯一本机相关的东西是 wkgeo CLI 路径**:`lib/index.js` 的 `DEFAULT_CLI_PATH` 默认 `F:\cloudbase\cli\wkgeo.mjs`。别人机器上 CLI 调不通时,设环境变量 `WKGEO_CLI=<其仓库>/cli/wkgeo.mjs`(`WKGEO_NODE_BIN` 可指定 node)后重启 DSH 即可——左栏图标仍然在,只是面板取数会报 `SPAWN_FAILED`。
140
- - 插件中心卡片**没有图标和本地化描述**(包内暂无 `locale/` 与 `icon` 导出),不影响功能。
141
- - 已发布公共 npm:`npm i dsh-wkgeo-ops` / 插件中心填 `dsh-wkgeo-ops`(v0.2.0 起可装);本仓库目录安装仍可拷目录 / `file:` 依赖。
142
-
143
- ## 6. 自测
144
-
145
- ```powershell
146
- node --check integrations\dsh-wkgeo-ops\lib\index.js # exit 0
147
- node --check integrations\dsh-wkgeo-ops\lib\wizard.js # exit 0
148
- node --check integrations\dsh-wkgeo-ops\client\client.js # exit 0
149
- node .tmp-build\t159\smoke.mjs # SMOKE OK — 40 checks passed / exit 0
150
- node .tmp-build\t161\api-test.mjs # 23/23 PASS · RESULT: ALL PASS / exit 0(v4 三条新路由)
151
- node .tmp-build\t161\shoot.mjs # SHOTS=5 / exit 0(v4 五张截图)
152
- node .tmp-build\t162\repro.mjs # 真 React 18 全流程:init → 浮层选根目录 → 卡片 → 详情 → 绑定文件夹,打印 CLICK_*/AFTER_* 文本、ERRS=[]
153
- node integrations/dsh-wkgeo-ops/verify/panel-registration.mjs # 页签/面板注册静态断言(guide 必须函数、左栏两条、错误边界)· RESULT: ALL PASS
154
- ```
155
-
156
- smoke 的 40 项断言分组(v2 起的回归基线):①三文件 `node --check` ②manifest + exports + peerDeps ③`cordis.patch.yml` ④v1/v2 纯函数导出 ⑤列表/详情/预览解析 ⑥publish 闸门(缺 confirm 0 次 CLI)⑦**七个工具齐 + `name/confirm` 必填内联** ⑧**建站闸门:缺 confirm → `needsConfirm:true` + 0 次 CLI** ⑨**四种来源组合的命令序列**(模板 / 网址 / 文件 / 模板+网址,含 from-url 先于 create)⑩**三步向导状态机**(不能跳步 / 语言默认中文 / 多选至少一项 / 三步走完)⑪zip 与 from-url 缺口 ⑫三条面板路由 ⑬文件扫描 ⑭客户端 `__internal` 纯函数与深链还原。
157
-
158
- `t161/api-test.mjs`(v4)用临时 store 文件起本地 HTTP 服务,断言:config 初始空 / 相对路径 `ROOT_NOT_ABSOLUTE` / 不存在 `ROOT_MISSING` / 保存落盘;browse 默认落根目录且 `insideRoot` / 越界 403 `OUTSIDE_ROOT`;folder 绑定 `rel`/`htmlCount`/`index.html` 排最前 / `?site=` 单站 / 全量映射 / 未绑定 / 越界 403 / `DIR_MISSING` / `DIR_NOT_ABSOLUTE` / DELETE 解绑;`isPathInside`/`relToRoot` 四个纯函数。
159
-
160
- 面板渲染证据(不装进真 DSH,用本地静态服务 + Chrome headless 截真实 `client.js`):
161
-
162
- ```powershell
163
- node .tmp-build\t161\preview.mjs # 打印 PANEL_URL / BROWSE_URL / CARDS_URL / DETAIL_URL / REAL_URL
164
- node .tmp-build\t161\shoot.mjs # 自动起服务 + Chrome headless(CDP) 1440×900 截五张
165
- ```
166
-
167
- - 初始化(先选站点根目录):`.tmp-build\t161\shot-init.png`
168
- - 目录选择器(一层层点):`.tmp-build\t161\shot-browse.png`
169
- - 卡片网格(状态 pill / tags / 本地文件夹行 / +新建卡):`.tmp-build\t161\shot-cards.png`
170
- - 站点详情(站点信息 + 本地文件夹清单):`.tmp-build\t161\shot-detail.png`
171
- - 真实 CLI 的 23 个站点渲染成卡片:`.tmp-build\t161\shot-real.png`
172
-
173
- `t162/repro.mjs` 是**真 React 18**(unpkg UMD,存在 `.tmp-build\t162\vendor`)的端到端复现:自起 http 服务发真实 `client.js` + mock 五条路由,Chrome headless 里按文字点按钮、取 innerText、收集 `window.onerror`,跑完打印 `AFTER_USE_TEXT`(=卡片)`AFTER_CARD_TEXT`(=详情)`BOUND_TEXT`(=已绑定文件夹)与 `ERRS3=[]`;产物 `after.png`(浮层)/`after-use.png`/`after-card.png`/`after-bind-modal.png`/`after-bound.png`。**改面板交互后先跑这个**,轻量垫片(t161/t159)复现不了 effect 时序类 bug。
174
-
175
- ## 7. 已知限制 / 未验证项(如实记录)
176
-
177
- 1. **CLI 没有 `template init`**:`wkgeo template init --from-url/--from-dir`(task-42)**尚未合入** `cli/`,`template init` 现报 `usage` 错误。所以「网址/文件 → 生成模板」这一步现在**只能降级**:网址 → `import --url … --apply`(灌内容,骨架仍是默认模板,返回 `unfilled:[{kind:'template'}]`);文件 → 逐个 `templates save`。CLI 支持后会自动切换(能力探测),插件代码不用改。
178
- 2. **`sites create` 没有 `--lang`**:真实 CLI 会报「不认识的参数:--lang」。所以英文站走「建站后 `lang save --code en --name 英文 --site <id> --confirm`」;默认中文不额外调用。探测到 CLI 将来支持 `--lang` 会自动加。
179
- 3. **zip 不支持**:Node 内置无解压、CLI 也无 `templates import --zip`;插件明确回 `ZIP_UNSUPPORTED`,不假装成功。
180
- 4. **模板中心列表拿不到**:CLI 没有列内置模板的命令(`templates list` 列的是**某站点自己的 html**)。下拉只能给「默认模板 + `WKGEO_TEMPLATE_KEYS` + 手填」;`--template` 真正匹配的是 DB `public.cms_template`(`source='builtin'`)的 **name 或 id**。
181
- 5. **`import` 需运维权限**:它走 service_role 直连库(本机 `tencent/cloudbase/import-site.mjs`),客户/AI 账号不该用;插件只用它做「网址来源」的降级,且会同时标未填充项。
182
- 6. **已装进真实 DSH**(`%USERPROFILE%\.dsh\plugins-src\dsh-wkgeo-ops`,profile 里是 `file:` 依赖 → pnpm **link**,别删/挪该目录):左栏「网站运营」入口已实测出现在运行中的 GUI 里。但 **服务端 `lib/index.js` 的改动必须重启 DSH 才生效**(路由在 `apply()` 里注册,进程内模块已缓存):v4 的三条新路由在未重启的实例上会 404,面板顶栏会提示「接口不可用」。`client/client.js` 的改动可以热重载、不用重启。
183
- 7. **`wkgeo_publish` / `wkgeo_site_create` 未在真实环境执行过**(破坏性写操作,只做注入假 CLI 的链路测试)。
184
- 8. 面板截图里的列表用**真实 23 个站点**(`shot-real.png`),不是造的数据;卡片网格/详情/初始化三张用固定的 5 个假站点。
185
- 9. **站点 ↔ 本地文件夹是本机私有映射**:wkgeo 站点模型里没有「本地目录」字段,映射只存在 `<DSH_PROFILE_DIR>/wkgeo-ops/config.json`(根目录只能设一个、文件夹必须在根目录之内)。换机器 / 换 profile 要重新设根目录并重绑;不会随站点同步。
1
+ # dsh-wkgeo-ops · DSH 插件「网站运营」
2
+
3
+ 把 `wkgeo` CLI(仓库内 `cli/wkgeo.mjs`,或用 `WKGEO_CLI` 指到你本机 CLI)的站点运营能力接进 DeepSeek Harness:
4
+ **agent 16 个工具** + **左侧栏「悟空GEO-网站运营」入口(点开是中央面板)** + **右侧边栏「网站运营」面板(含三步建站向导)**。不新增任何 npm 依赖,插件内**不重写**任何 HTTP / SQL 业务逻辑——工具只负责 `spawn node cli/wkgeo.mjs … --json` 再解析信封。
5
+
6
+ - key / 包名 / cordis 插件名:`dsh-wkgeo-ops`
7
+ - 版本:**0.2.2**(2026-10-30 发布到公共 npm,可直接 `npm i dsh-wkgeo-ops`;0.2.2 内容:左栏入口文字改为老板拍定的「悟空GEO-网站运营」、补 `LICENSE`、README 去掉本机绝对路径。历史:0.2.0 被 npm 判 staged 冲突,首个正式版是 0.2.1)
8
+ - 宿主:DSH Web 客户端(`dsh.client.platform = "web"`)+ cordis 服务端插件
9
+ - v2 口径(老板 2026-10-30):**三步向导**(①站点名 ②语言 ③内容来源)+ **模板 / 网址 / 文件 三选或多选**
10
+ - v4 口径(老板 2026-10-30):「初始化先选**站点根目录** → **站点卡片**一个一个显示(能点进去)→ 详情里有**本地文件夹**」;不再用一张表铺列表
11
+ - v0.2.0 口径(老板 2026-10-30「接收 dsh,完成 cli 的后续能力对接,然后发布一个包」):CLI 的 `content apply / fill / check`、`templates list`、`template init / validate / pack / publish`、`domain records` 全部接成工具,破坏性操作一律 **dry-run 默认 + 显式 `confirm:true`**
12
+
13
+ ---
14
+
15
+ ## 1. 目录结构
16
+
17
+ ```
18
+ integrations/dsh-wkgeo-ops/
19
+ ├─ package.json # manifest:main / files / dsh.bundle.patch / dsh.client / exports / engines / publishConfig
20
+ ├─ cordis.patch.yml # 装载层:- insert: id/name = dsh-wkgeo-ops
21
+ ├─ lib/
22
+ │ ├─ wizard.js # 纯函数:三步向导状态机 + 来源归一 + 能力探测 + 命令构造 + 建站闸门
23
+ │ └─ index.js # 服务端 cordis 插件:16 个工具 + 6 条路由 + 执行层(可单测)
24
+ ├─ client/
25
+ │ └─ client.js # Web 客户端:左侧栏入口 + 面板(初始化/卡片网格/详情/向导/日志 五态)
26
+ ├─ skill/ # agent 技能:wkgeo-site-content(建站清单契约 + 示例 manifest)
27
+ ├─ skills/ # agent 技能:wkgeo-site-build(旧指针,已并入 skill/wkgeo-site-content)
28
+ ├─ verify/ # 离线回归:tool-schema / panel-registration / dsh-compat(+ schema-subset 校验器)
29
+ └─ README.md # 本文件
30
+ ```
31
+
32
+ ## 2. 三步向导(面板主流程)
33
+
34
+ | 步 | 内容 | 规则 |
35
+ | --- | --- | --- |
36
+ | ① | **站点名** | 必填,≤60 字;回车即下一步 |
37
+ | ② | **语言** | 默认**中文**(`zh-CN`),可选**英文**(`en`) |
38
+ | ③ | **内容来源:模板 / 网址 / 文件** | 三个入口**同时摆出来**,复选框式**可单选可多选**;勾谁展开谁的输入 |
39
+
40
+ 入口位置(老板口径):
41
+
42
+ - **没有站点时**:空态第一行就是整宽主按钮「**+ 创建网站(第一个)**」。
43
+ - **有站点时**:列表最末尾固定一行虚线「**+ 创建新网站(最后一个)**」,顶部也有「+ 创建网站」小按钮。
44
+
45
+ 第三步的三种来源:
46
+
47
+ | 来源 | 角色 | 勾选后展开的输入 |
48
+ | --- | --- | --- |
49
+ | 模板 | **设计骨架** | 模板中心下拉(默认模板 + `WKGEO_TEMPLATE_KEYS` 配置的名字)+ 手填模板名/uuid |
50
+ | 网址 | **结构与内容来源** | 老站 URL(`http(s)://…`) |
51
+ | 文件 | **结构与内容来源** | 本地目录(逐个 `*.html`)或 zip 路径 |
52
+
53
+ **合并语义**(UI 文案与文档同一份 `MERGE_SEMANTICS`,非装饰):
54
+
55
+ 1. 模板 = 设计骨架;网址 / 文件 = 结构与内容来源。
56
+ 2. 只选网址或文件 → 先用它生成模板(`template init`),再建站。
57
+ 3. 模板 + 网址/文件 → 模板做骨架,内容按栏目灌入。
58
+ 4. **做不到的组合必须明确标「未填充」**(`unfilled[]` + 面板黄色提示),**不许静默忽略**。
59
+
60
+ 深层链接(截图 / 复现用):`?wizard=1&step=3&name=…&lang=zh-CN&sources=template,url&templateKey=…&url=…`。
61
+
62
+ ## 3. 工具清单(agent 侧,16 个)
63
+
64
+ v0.2.0 新增的 9 个工具都是**薄封装**:`spawn node cli/wkgeo.mjs … --json` → 解析 JSON 信封 → 统一返回
65
+ `{ ok, code, command, executed, needsConfirm, reason?, data?, stderr?, error?, errorCode?, errorHint? }`。
66
+ `code` 是 CLI 退出码(0 成功 / 1 校验失败 / 2 用法或环境错误 / 3 需要 `wkgeo login` / 4 被限流;插件内部错误用 -1),
67
+ `errorHint` 给出中文处置建议。**破坏性操作(建站 / 发布 / 内容落库 / 模板发布)默认 dry-run,必须 `confirm:true` 才真写**。
68
+
69
+ | 工具 | 参数 | 底层命令 | 返回要点 |
70
+ | --- | --- | --- | --- |
71
+ | `wkgeo_sites_list` | 无 | `sites list --json` | `{ ok, count, sites[] }`,每站 `id/name/status/shortHost/shortUrl/domain/domainVerification/provisionState/relation/cmsOnly` |
72
+ | `wkgeo_site_status` | `site`* | `sites get <id> --json` | `{ ok, site }`:状态、短域、域名验证(含 `verifiedAt`)、证书、备案、开通状态 |
73
+ | `wkgeo_site_preview` | `site`*、`path` | `preview <path> --site <id> --json` | `{ ok, url, siteId, path }` → `https://wkgeo.com/cms/preview/<站点ID>/about/` |
74
+ | `wkgeo_publish` | `site`*、`confirm`*、`wait` | `publish --site <id> --confirm [--no-wait] --json` | 缺 `confirm:true` **不执行**,回 `{executed:false,needsConfirm:true,reason,risk}` |
75
+ | `wkgeo_site_create` | `name`*、`lang`、`source`、`templateKey`、`url`、`dir`、`zipPath`、`confirm`* | 计划式执行:`sites create` →(`lang save`)→(`templates save`×N)→(`import --apply`) | 缺 `confirm:true` 只回 `{executed:false,needsConfirm:true,plan[],risk}`;有 confirm 才逐步执行,回 `{ok,siteId,steps[],unfilled[],gaps[],warnings[]}` |
76
+ | `wkgeo_template_from_url` | `url`*、`out`、`site`、`confirm` | `template init --from-url <url> [--out <dir>] --json`(长任务 300s) | CLI 没这个子命令时 → `{supported:false,executed:false,gap,command,fallback}`,**0 次 CLI 调用**,不假装成功 |
77
+ | `wkgeo_template_from_dir` | `dir`、`zipPath`、`out`、`site`、`confirm` | `template init --from-dir <dir> [--out <dir>] --json`(300s);没有该子命令时降级 `templates save` | 只给 zip → `{supported:false,gap:{code:'ZIP_UNSUPPORTED'}}`;无 `site`/`confirm` → `needsConfirm:true` + 将执行的命令清单 |
78
+ | `wkgeo_content_apply` | `file`*、`site`、`confirm`、`publish`、`wait`、`allowNameMismatch` | `content apply --file <site-manifest.json> [--site] [--publish [--no-wait]] [--allow-name-mismatch] [--confirm] --json`(300s) | **整站落库**:无 `confirm:true` = CLI dry-run,回 `{executed:false,needsConfirm:true,reason}`;有 confirm 才写库,`publish:true` 顺带发布 |
79
+ | `wkgeo_content_fill` | `site`、`manifest` \| `manifestJson`、`lang`、`confirm` | `content fill --manifest <文件> [--site] [--lang] [--confirm] --json`(300s) | **模板内容填充**:`manifestJson` 会落到临时目录再调用、调用后清理;默认 dry-run |
80
+ | `wkgeo_content_check` | `site`、`template`、`lang`、`maxDetails`(默认 50) | `content check … --max-details <n> --json` | **只读一致性自检**;四类硬问题 → CLI exit 1 → `ok:false` + `errorHint` |
81
+ | `wkgeo_templates_list` | `site` | `templates list [--site <id>] --json` | 站点自己的模板列表(`count` + `data.templates[]`:`name/source/isActive/isDefault/summary/files[]`);首次调用 CLI 会自动 seed |
82
+ | `wkgeo_template_init` | `mode`(blank\|url\|dir)、`url`、`dir`、`key`*、`out`、`version`、`registry`、`offline`、`force`、`chrome`、`maxPages`、`noDesign` | `template init [--to] [--version] [--registry] [--offline] [--force] --json`;生成模式 `--from-url` / `--from-dir` + `--key`(300s) | 空白骨架(不带 `mode`);网址 / 目录生成必须给 `key`,缺了回 `MISSING_KEY`;网址模式很慢,`maxPages` 可限页 |
83
+ | `wkgeo_template_validate` | `dir`*、`base`、`noDesign` | `template validate --dir <目录> [--base] [--no-design] --json`(300s) | 9 关校验,不合格 → CLI exit 1 → `ok:false` |
84
+ | `wkgeo_template_pack` | `dir`*、`out`、`base` | `template pack --dir <目录> [--out <zip>] [--base] --json`(300s) | 打包结果(`data.zip` / 文件数) |
85
+ | `wkgeo_template_publish` | `dir`*、`key`、`confirm`、`noPrune`、`archive`、`noPreview` | `template publish --dir <目录> [--key] [--dry-run] [--confirm] [--no-prune] [--archive] [--no-preview] --json`(300s) | **发布到模板市场**:默认 dry-run,`confirm:true` 才写 |
86
+ | `wkgeo_domains_list` | `site` | 有 `site` → `domain records --site <id> --json`;无 `site` → `sites list --json` 聚合 | 有 site:`data.domains[]`(TXT/CNAME 记录、`verify_status`、证书 `not_after`、ESA 状态);无 site:逐站域名汇总。**CLI 没有 `domain list` 子命令**,聚合路径会在 `data.source` 里写明 |
87
+
88
+ `*` = 必填。实现细节:
89
+
90
+ - **`parameters` 必须是 raw JSON Schema**(`{ type:'object', properties, required }`),由 `paramsSchema(properties, required)` 统一生成,`required` 恒输出;传「属性名 → 属性 schema」的映射会被模型 API 判 `schema must be a JSON Schema of 'type': "object", got 'type: null'`(`INVALID_REQUEST`,整轮被打回)。回归脚本:`verify/tool-schema.mjs`(纯 node,不需 Electron/ajv,期望 `RESULT: ALL PASS(16 个工具)`、exit 0)。
91
+ - **DSH schema 子集(v0.2.0 硬约束)**:宿主 `dsh-tools/lib/types/json-schema.js` 只认 `type / oneOf / properties / required / additionalProperties / items / enum / const` + 注解 `description/title/default/examples`;其它键(`format/pattern/minLength/minItems/anyOf/allOf/$ref/...`)一律报 `JsonSchemaError: unsupported JSON schema`。**属性级 `required: true` 只能出现在 `type:'object'` 的属性上**——加在 boolean/integer/array/string 上就是老板 2026-10-30 装插件失败的那条错(`schema.properties.ok.required is not supported on type "boolean"`);必填一律用顶层 `required: [...]`。`verify/schema-subset.mjs` 复刻了这套校验器,`verify/dsh-compat.mjs` 会拦住违规写法。
92
+ - 一律 `spawn(node, [cli, ...args])`(`shell:false`、`windowsHide:true`);**普通命令硬超时 30s**,**长任务(模板生成 / 建站 / 导入)300s**(`LONG_TIMEOUT_MS`),超时 `SIGKILL` 回 `code:"TIMEOUT"`;stdout 上限 4MB。
93
+ - 只解析 CLI 的 JSON 信封;失败只回 `error.code/message/hint`,**不打印任何 token、不落日志**。
94
+ - node 解析顺序:`WKGEO_NODE_BIN` → `process.execPath`(仅当是 `node.exe`)→ `node`;CLI 路径可用 `WKGEO_CLI` 覆盖,默认取仓库内 `cli/wkgeo.mjs`。
95
+ - **能力探测**(10 分钟缓存):读 `template init --help` / `sites create --help` / `import --help` 文本,判断 `--from-url`、`--from-dir`、`--lang`、`--url` 是否真的存在;**探测不到就不硬塞参数**,改走降级路径并在返回里标 `capabilities`。
96
+ - 模板中心下拉的选项来自 `WKGEO_TEMPLATE_KEYS`(逗号分隔的内置模板名/uuid)+ 一个「默认模板(不指定)」+ 手填框;CLI 没有「列模板」的命令,缺口见 §7。
97
+
98
+ ## 4. 面板(Web 客户端侧)
99
+
100
+ - **左侧栏入口(v3)**:`sidebar.panellist`(`id: wkgeo-ops`、`order: 30`、图标 `OpsIcon`)+ `main`(`key: wkgeo-ops`,渲染同一个 `PanelBody`)。两条的 id/key 必须相同:①只注册就有点不开、②只注册就先没图标;**装插件本身不会自动往左栏加菜单,这两条必须自己写**。停用插件时若该面板正激活,先 `layout.selectPanel(null)` 把界面交还对话。
101
+ - 注册 `sidebarRightTabs`(`id: wkgeo-ops`、`priority: fallback`)+ `sidebar.right.pane.tab` / `.title` 两个 slot,与 `dsh-bili-panel` 范式一致;右栏页签与左栏入口渲染的是**同一个** `PanelBody`。
102
+ - **五态(v4)**:
103
+ 1. **初始化**(本机还没设站点根目录时):一张居中卡片「第一步:先选站点根目录」——手输绝对路径,或点「选择根目录」进目录浏览器一层层点。**强制第一步、不能跳过**:根目录没设就只显示这张卡,不给卡片列表(唯一例外是服务端 config 接口不可用 `cfgDown`,那时退回列表并在顶栏提示「接口不可用」)。
104
+ 2. **卡片网格**:一个站一张卡(站名 + 状态 pill + 主机链接 + 类型/验证/短码 tags + 底部「📁 本地文件夹」行),末尾虚线「+ 新建站点」卡;整张卡可点。
105
+ 3. **详情**:点卡进去 → 返回按钮 + 站点信息 7 行 + 本地文件夹面板(绝对路径 / 相对根目录 / HTML 文件清单 / 换文件夹 / 重新扫描 / 打开站点)。
106
+ 4. **向导**(三步,见 §2)、5. **执行日志**。
107
+ - **目录选择器是固定浮层弹窗(v4.1)**:`position: fixed` 全屏遮罩 + 720px 白卡(`role="dialog"`、`aria-modal`),点遮罩即取消;顶部手输路径 + `↑ 上一级` + `→` 跳转,中间子目录列表、底部「取消 / 用这个目录」。**不要改回普通区块**:面板容器高度有限,内联区块会被挤成 0 高(老板 2026-10-30 报「点了没弹出选择器」,根因见下一条)。
108
+ - **真 React 两个坑(v4.1 修)**:① 目录取数的 `useEffect` **必须带依赖数组 `[path]`**——无依赖写法每轮渲染都重跑,React 先执行上一轮清理把 `alive` 置 false,首个 fetch 响应被丢弃且守卫拦住重试,表现就是永远停在「读取目录中…」(自写的轻量垫片不复现,真 React 必现);② 受控输入框用 `onChange`,不要用 `onInput`(真 React 里等价只读)。
109
+ - **页签 guide 的 title / description 必须是函数(v4.2 修,事故级)**:`sidebarRightTabs.register({ guide: [{ title: '站点状态' }] })` 这种**字符串写法**会让 DSH 在渲染右栏 guide 时抛 `TypeError: entry.title is not a function`;它发生在右栏同一个 React 树里 → **整个右侧栏被卸载成一片空白**(连 DSH 自带的「开始」页签内容也没了),左侧栏 / 中央面板不受影响。正确写法 `title: () => '…'`(dsh-image-gen 即如此),本仓库再用 `asLabel()` 兜底。回归:`node integrations/dsh-wkgeo-ops/verify/panel-registration.mjs` → `RESULT: ALL PASS`。
110
+ - **v5(2026-10-30):面板根节点自己撑满宽度** —— `STYLE.wrap` 必须带 `width:'100%'; minWidth:0; flex:'1 1 auto'`,顶栏 `STYLE.bar` 带 `flexWrap:'wrap'`、标题/计数 `whiteSpace:'nowrap'`;否则在右栏(flex 项、宽度窄)里整块会收缩成一竖条。
111
+ - **v5:右栏与中央共用同一个入口组件 `OpsPanel`**(apply 内定义:`h(PanelBoundary, null, h(PanelBody, ctx))`),两处槽位都直接传这个组件(和 `dsh-bili-panel` 同形),**不要再套 PaneShell 之类的额外外壳**;面板标题后有一个 `v5` 小标记,用来确认客户端加载的是哪一版。
112
+
113
+ - 取数路由(服务端自注册、与 DSH GUI 同源):
114
+ - `GET /wkgeo-ops/sites`(`?refresh=1` 强刷,默认 15s 缓存)
115
+ - `GET /wkgeo-ops/templates` → `{ok, choices[], manual, note, gap, mergeSemantics[]}`
116
+ - `POST /wkgeo-ops/site-create` → `application/x-ndjson` 逐步流(先 `{type:'plan'}` 再 `{type:'step'}`…最后 `{type:'done'}`);**缺 `confirm:true` 一律 200 + `needsConfirm:true`,不执行**;非 POST → 405。
117
+ - **`GET|POST /wkgeo-ops/config`**(v4):读/写本机站点根目录。GET → `{ok, rootDir, exists}`;POST `{rootDir}` 保存(相对路径 400 `ROOT_NOT_ABSOLUTE`、不存在 400 `ROOT_MISSING`、不是目录 400 `ROOT_NOT_DIR`);`{rootDir:''}` 清空。
118
+ - **`GET /wkgeo-ops/browse?path=`**(v4):目录选择器数据源,回 `{ok, path, parent, dirs[], files[], htmlCount, insideRoot}`。**设了根目录后只允许浏览根目录之内**(越界 403 `OUTSIDE_ROOT`);没设根目录时可从家目录/盘符起随便翻。
119
+ - **`GET|POST|DELETE /wkgeo-ops/site-folder`**(v4):站点 ↔ 本地文件夹映射。GET 无参回全部 `folders{}`、`?site=<id>` 回单站(含 `rel`/`exists`/`htmlCount`/`files[]`,未绑定 `bound:false`);POST `{siteId, dir}` 绑定(要求已有根目录 `ROOT_UNSET`、绝对路径 `DIR_NOT_ABSOLUTE`、在根目录内 `OUTSIDE_ROOT`、目录真实存在 `DIR_MISSING`/`DIR_NOT_DIR`);DELETE `?site=<id>` 解绑。
120
+ - **存储位置**:`<DSH_PROFILE_DIR 或 ~/.dsh>/wkgeo-ops/config.json`,形状 `{ rootDir, folders: { [siteId]: dir }, updatedAt }`。**只存本机**,不上传、不写进站点、不进 CLI。
121
+ - 样式只用 `--dsw-alias-*` CSS 变量,不引外部资源、不引 CSS 文件。
122
+ - 拿不到 `webServer` 服务时只跳过路由(面板顶栏提示「接口不可用」并退回列表),**7 个工具照常可用**。
123
+
124
+ ## 5. 安装(老板侧)
125
+
126
+ 前置:`wkgeo` CLI 已登录(`node <仓库>\cli\wkgeo.mjs login`,或直接用 `WKGEO_CLI` 指到你本机的 CLI),DSH 已在运行。
127
+
128
+ **A. 从 npm 安装(v0.2.0 起,推荐 —— 老板要的就是这条路)**
129
+
130
+ DSH 设置里的插件页 →「**添加插件**」→ 填包名:
131
+
132
+ ```
133
+ dsh-wkgeo-ops
134
+ ```
135
+
136
+ 装上后重启 DSH 即生效(左侧栏图标 + 右侧栏页签 + 16 个工具一起出现)。等价命令行:
137
+
138
+ ```powershell
139
+ npm i dsh-wkgeo-ops # 或 npm i -g / 在 DSH profile 目录里装
140
+ npm view dsh-wkgeo-ops version # 0.2.2
141
+ ```
142
+
143
+ > **唯一本机相关项**:工具要调 `wkgeo` CLI。插件默认取仓库内 `cli/wkgeo.mjs`;装到别的机器 / 别的仓库时,设环境变量 `WKGEO_CLI=<你那边的仓库>\cli\wkgeo.mjs`(可选 `WKGEO_NODE_BIN`)后重启 DSH。没配也能装、能激活,只是工具调用会回 `SPAWN_FAILED` 并给出提示。
144
+
145
+ **B. 从本地目录安装(开发期改动还没发包时用)**
146
+
147
+ 1. 拷目录(在仓库根目录执行):
148
+ ```powershell
149
+ Copy-Item -Recurse -Force '.\integrations\dsh-wkgeo-ops' "$env:USERPROFILE\.dsh\plugins-src\dsh-wkgeo-ops"
150
+ ```
151
+ 2. 二选一:
152
+ - 插件安装 UI「从本地目录安装」选到 `%USERPROFILE%\.dsh\plugins-src\dsh-wkgeo-ops`;
153
+ - 手动挂 profile(两处,**不用**改 `cordis.patch.yml`):`%USERPROFILE%\.dsh\profiles\desktop\package.json` 的 `dependencies` 加
154
+ `"dsh-wkgeo-ops": "file:C:/Users/Administrator/.dsh/plugins-src/dsh-wkgeo-ops"`,同文件 `dsh.profile.bundles` 数组加一项 `dsh-wkgeo-ops`,重启 DSH。
155
+
156
+ > `cordis.patch.yml` 里**不需要**再写 `- id: dsh-wkgeo-ops`:`dsh.profile.bundles` 里的包按 bundle 层挂载,包内自带的 patch 会自动应用。
157
+
158
+ **C. 验证(装完应同时出现两处入口 + 16 个工具)**
159
+
160
+ | 位置 | 现象 |
161
+ |---|---|
162
+ | **左侧栏** | 第 5 个图标(显示器+双卡片,`order: 30`,排在「工作台」后面),文字为 **「悟空GEO-网站运营」**(`client/client.js` 的 `SIDEBAR_ROW_LABEL`);点它 → 中央区渲染「网站运营」面板 |
163
+ | 右侧栏 | 「网站运营」页签(与左栏入口并存,两条路都能进) |
164
+ | 对话 | 让 agent「列一下我的站点」→ 调 `wkgeo_sites_list`,回 `{ok:true,count:25,sites:[…]}` |
165
+
166
+ 三个**只读**工具可以立刻验通(不写任何东西):`wkgeo_sites_list`(站点列表)、`wkgeo_domains_list`(域名/证书状态)、`wkgeo_templates_list`(站点模板)。
167
+
168
+ 左栏入口是插件自己注册的(`client/client.js` 的 `sidebar.panellist` + `main` 两个槽位、共用 id `wkgeo-ops`),**装插件本身不会自动往左栏塞菜单**——所以看到图标就说明这版代码已生效;只看到右侧栏页签则说明装的是旧版(左栏入口是 2026-10-30 加的)。
169
+
170
+ **D. 别人(另一台机器 / 另一个 profile)首次安装的差异**
171
+
172
+ - 左栏 / 右栏入口与工具都随包走,**换机器一样出现**;`client` 侧不依赖本机路径。
173
+ - ⚠️ **唯一本机相关的东西是 wkgeo CLI 路径**:`lib/index.js` 的 `DEFAULT_CLI_PATH` 默认指向仓库内 `cli/wkgeo.mjs`。别人机器上 CLI 调不通时,设环境变量 `WKGEO_CLI=<其仓库>/cli/wkgeo.mjs`(`WKGEO_NODE_BIN` 可指定 node)后重启 DSH 即可——左栏图标仍然在,只是面板取数会报 `SPAWN_FAILED`。
174
+ - 插件中心卡片**没有图标和本地化描述**(包内暂无 `locale/` 与 `icon` 导出),不影响功能。
175
+ - 已发布公共 npm:`npm i dsh-wkgeo-ops` / 插件中心填 `dsh-wkgeo-ops`(v0.2.0 起可装);本仓库目录安装仍可拷目录 / `file:` 依赖。
176
+
177
+ ## 6. 自测
178
+
179
+ ```powershell
180
+ node --check integrations\dsh-wkgeo-ops\lib\index.js # exit 0
181
+ node --check integrations\dsh-wkgeo-ops\lib\wizard.js # exit 0
182
+ node --check integrations\dsh-wkgeo-ops\client\client.js # exit 0
183
+ node integrations\dsh-wkgeo-ops\verify\tool-schema.mjs # 16 个工具 schema 全过 · RESULT: ALL PASS / exit 0(纯 node,不需 Electron/ajv)
184
+ node integrations\dsh-wkgeo-ops\verify\panel-registration.mjs # 页签/面板注册静态断言(guide 必须函数、左栏两条、错误边界)· RESULT: ALL PASS / exit 0
185
+ node integrations\dsh-wkgeo-ops\verify\dsh-compat.mjs # 离线兼容自检:node --check ×3 + schema 子集 + 属性级 required 扫描 + 包形态 · PASSED=39 FAILED=0 / exit 0
186
+ node .tmp-build\t183\tool-smoke.mjs # 16 工具:离线参数映射断言 7/7 PASS + 4 个真实只读调用(sites/domains/templates/聚合成 25 站)· REAL_READONLY_OK=true
187
+ node .tmp-build\t159\smoke.mjs # SMOKE OK — 40 checks passed / exit 0
188
+ node .tmp-build\t161\api-test.mjs # 23/23 PASS · RESULT: ALL PASS / exit 0(v4 三条新路由)
189
+ node .tmp-build\t161\shoot.mjs # SHOTS=5 / exit 0(v4 五张截图)
190
+ node .tmp-build\t162\repro.mjs # 真 React 18 全流程:init → 浮层选根目录 → 卡片 → 详情 → 绑定文件夹,打印 CLICK_*/AFTER_* 文本、ERRS=[]
191
+ ```
192
+
193
+ smoke 的 40 项断言分组(v2 起的回归基线):①三文件 `node --check` ②manifest + exports + peerDeps ③`cordis.patch.yml` ④v1/v2 纯函数导出 ⑤列表/详情/预览解析 ⑥publish 闸门(缺 confirm 0 次 CLI)⑦**七个工具齐 + `name/confirm` 必填内联** ⑧**建站闸门:缺 confirm → `needsConfirm:true` + 0 次 CLI** ⑨**四种来源组合的命令序列**(模板 / 网址 / 文件 / 模板+网址,含 from-url 先于 create)⑩**三步向导状态机**(不能跳步 / 语言默认中文 / 多选至少一项 / 三步走完)⑪zip 与 from-url 缺口 ⑫三条面板路由 ⑬文件扫描 ⑭客户端 `__internal` 纯函数与深链还原。
194
+
195
+ `t161/api-test.mjs`(v4)用临时 store 文件起本地 HTTP 服务,断言:config 初始空 / 相对路径 `ROOT_NOT_ABSOLUTE` / 不存在 `ROOT_MISSING` / 保存落盘;browse 默认落根目录且 `insideRoot` / 越界 403 `OUTSIDE_ROOT`;folder 绑定 `rel`/`htmlCount`/`index.html` 排最前 / `?site=` 单站 / 全量映射 / 未绑定 / 越界 403 / `DIR_MISSING` / `DIR_NOT_ABSOLUTE` / DELETE 解绑;`isPathInside`/`relToRoot` 四个纯函数。
196
+
197
+ 面板渲染证据(不装进真 DSH,用本地静态服务 + Chrome headless 截真实 `client.js`):
198
+
199
+ ```powershell
200
+ node .tmp-build\t161\preview.mjs # 打印 PANEL_URL / BROWSE_URL / CARDS_URL / DETAIL_URL / REAL_URL
201
+ node .tmp-build\t161\shoot.mjs # 自动起服务 + Chrome headless(CDP) 1440×900 截五张
202
+ ```
203
+
204
+ - 初始化(先选站点根目录):`.tmp-build\t161\shot-init.png`
205
+ - 目录选择器(一层层点):`.tmp-build\t161\shot-browse.png`
206
+ - 卡片网格(状态 pill / tags / 本地文件夹行 / +新建卡):`.tmp-build\t161\shot-cards.png`
207
+ - 站点详情(站点信息 + 本地文件夹清单):`.tmp-build\t161\shot-detail.png`
208
+ - 真实 CLI 的 23 个站点渲染成卡片:`.tmp-build\t161\shot-real.png`
209
+
210
+ `t162/repro.mjs` 是**真 React 18**(unpkg UMD,存在 `.tmp-build\t162\vendor`)的端到端复现:自起 http 服务发真实 `client.js` + mock 五条路由,Chrome headless 里按文字点按钮、取 innerText、收集 `window.onerror`,跑完打印 `AFTER_USE_TEXT`(=卡片)`AFTER_CARD_TEXT`(=详情)`BOUND_TEXT`(=已绑定文件夹)与 `ERRS3=[]`;产物 `after.png`(浮层)/`after-use.png`/`after-card.png`/`after-bind-modal.png`/`after-bound.png`。**改面板交互后先跑这个**,轻量垫片(t161/t159)复现不了 effect 时序类 bug。
211
+
212
+ ## 7. 已知限制 / 未验证项(如实记录)
213
+
214
+ 1. **`template init --from-url` 很慢且必须先定 `key`**:CLI 已支持(`template init --from-url/--from-dir`),但抓站 + 生成要几分钟,插件给的是 300s 超时;缺 `key` 直接回 `MISSING_KEY`。实测跑通的是「空白骨架」模式(`template init --to <dir>` → 从 npm 拉 `@wkgeo/base-template@1.0.0`,61 个文件、2064484 字节、self_check PASS、289ms 命中缓存)。**网址/目录生成模式本轮没有真跑**(会写模板目录 + 耗时长),失败时会回 `ok:false` + `errorHint`,不会假装成功。
215
+ 2. **`sites create` 的 `--lang` 双通道**:CLI 现在支持 `--lang zh-CN|en`,但面板向导仍走「建站后 `lang save`」(兼容未升级的 CLI),能力探测到 `--lang` 时直接用。`wkgeo_site_create` 工具本身走向导链路,不改。
216
+ 3. **zip 不支持**:Node 内置无解压、CLI 也无 `templates import --zip`;插件明确回 `ZIP_UNSUPPORTED`,不假装成功。
217
+ 4. **模板「市场/内置」全量目录仍拿不到**:`templates list` 列的是**某站点自己的模板**(v0.2.0 已接成 `wkgeo_templates_list`);CLI 没有「列全平台模板市场」的命令,所以面板下拉仍只能给「默认模板 + `WKGEO_TEMPLATE_KEYS` + 手填」。`--template` 匹配的是 DB `public.cms_template` 的 **name 或 id**。
218
+ 5. **`import` 需运维权限**:它走 service_role 直连库(本机 `tencent/cloudbase/import-site.mjs`),客户/AI 账号不该用;插件只用它做「网址来源」的降级,且会同时标未填充项。
219
+ 6. **已装进真实 DSH**(`%USERPROFILE%\.dsh\plugins-src\dsh-wkgeo-ops`,profile 里是 `file:` 依赖 → pnpm **link**,别删/挪该目录):左栏「网站运营」入口已实测出现在运行中的 GUI 里。但 **服务端 `lib/index.js` 的改动必须重启 DSH 才生效**(工具与路由都在 `apply()` 里注册,进程内模块已缓存)。`client/client.js` 的改动可以热重载、不用重启。
220
+ 7. **写操作工具本轮没在真实后端执行**:`wkgeo_content_apply` / `wkgeo_content_fill` / `wkgeo_site_create` / `wkgeo_publish` / `wkgeo_template_publish` 只做了「闸门 + 参数映射」离线断言(stub runCli)与 CLI dry-run 语义核对,**没有真写库**(按项目门禁:破坏性写要老板点头)。真实只读验证过的是 `wkgeo_sites_list`(25 站)、`wkgeo_domains_list`(2 条域名 + 证书)、`wkgeo_templates_list`(2 个模板)、`wkgeo_content_check`(score 80/94)、`wkgeo_template_init`(骨架下载)。
221
+ 8. 面板截图里的列表用**真实站点**(`shot-real.png`),不是造的数据;卡片网格/详情/初始化三张用固定的 5 个假站点。
222
+ 9. **站点 ↔ 本地文件夹是本机私有映射**:wkgeo 站点模型里没有「本地目录」字段,映射只存在 `<DSH_PROFILE_DIR>/wkgeo-ops/config.json`(根目录只能设一个、文件夹必须在根目录之内)。换机器 / 换 profile 要重新设根目录并重绑;不会随站点同步。
223
+ 10. **发布到 npm 要用官方 registry**:本机 `~/.npmrc` 默认 `registry=https://registry.npmmirror.com`(镜像无凭证),发布必须 `npm publish --registry https://registry.npmjs.org`(账号 `wkgeocn`);`npm view` 同理。
224
+ 11. **`wkgeo_domains_list` 无 `site` 时走聚合**:CLI 没有 `domain list` 子命令,聚合用 `sites list` 的 `domain/domains[]` 字段,拿不到 TXT/CNAME/证书明细(要明细就传 `site`)。
package/client/client.js CHANGED
@@ -58,6 +58,10 @@ window.__ModuleLoader__.load({
58
58
  { key: 'file', label: '文件', role: '结构与内容来源', hint: '本地目录里的 *.html,或 zip 路径' },
59
59
  ]
60
60
 
61
+ // 左栏入口那一行显示的文字(老板 2026-10-30 定「悟空GEO-网站运营」)。
62
+ // 与 MESSAGES.zh.title 分开:title 仍给右侧面板页签用(「网站运营」),改这里不影响页签。
63
+ const SIDEBAR_ROW_LABEL = '悟空GEO-网站运营'
64
+
61
65
  const MESSAGES = {
62
66
  zh: {
63
67
  title: '网站运营',
@@ -2065,7 +2069,7 @@ window.__ModuleLoader__.load({
2065
2069
  name: 'sidebar.panellist',
2066
2070
  id: PANEL_ID,
2067
2071
  order: 30,
2068
- label: () => MESSAGES.zh.title,
2072
+ label: () => SIDEBAR_ROW_LABEL,
2069
2073
  locale: NS,
2070
2074
  },
2071
2075
  (owner = {}) => h(OpsIcon, { size: owner.size }),
@@ -2121,8 +2125,8 @@ window.__ModuleLoader__.load({
2121
2125
  })
2122
2126
 
2123
2127
 
2124
-
2125
-
2126
-
2127
-
2128
-
2128
+
2129
+
2130
+
2131
+
2132
+
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dsh-wkgeo-ops · DSH 插件「网站运营」——服务端 cordis 插件
3
3
  *
4
- * 作用:把本机 wkgeo CLI(F:\cloudbase\cli\wkgeo.mjs)的站点运营能力,
4
+ * 作用:把 wkgeo CLI(仓库内 cli/wkgeo.mjs,默认值见 DEFAULT_CLI_PATH)的站点运营能力,
5
5
  * 以 16 个 DSH agent 工具 + 6 条同源 HTTP 路由(面板取数 / 建站向导 / 模板选项 / 根目录 / 浏览 / 站点文件夹)暴露给 DSH。
6
6
  *
7
7
  * v0.2.0(2026-10-30 老板「接收 dsh,完成 cli 后续能力对接,然后发布一个包」):
package/package.json CHANGED
@@ -1,41 +1,42 @@
1
- {
2
- "name": "dsh-wkgeo-ops",
3
- "version": "0.2.0",
4
- "description": "DSH 插件「网站运营」:把 wkgeo CLI 的站点运营能力接进 DSH(16 个 agent 工具 + 右侧面板)",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "files": [
8
- "lib",
9
- "client",
10
- "skill",
11
- "skills",
12
- "cordis.patch.yml",
13
- "README.md"
14
- ],
15
- "publishConfig": {
16
- "access": "public",
17
- "registry": "https://registry.npmjs.org"
18
- },
19
- "engines": {
20
- "node": ">=20"
21
- },
22
- "license": "MIT",
23
- "keywords": [
24
- "dsh",
25
- "dsh-plugin",
26
- "wkgeo",
27
- "网站运营",
28
- "建站"
29
- ],
30
- "dsh": {
31
- "bundle": { "patch": "./cordis.patch.yml" },
32
- "client": { "platform": "web", "inject": [] }
33
- },
34
- "exports": {
35
- ".": { "default": "./lib/index.js" },
36
- "./client": "./client/client.js",
37
- "./cordis.patch.yml": "./cordis.patch.yml",
38
- "./package.json": "./package.json"
39
- },
40
- "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1" }
41
- }
1
+ {
2
+ "name": "dsh-wkgeo-ops",
3
+ "version": "0.2.2",
4
+ "description": "DSH 插件「网站运营」:把 wkgeo CLI 的站点运营能力接进 DSH(16 个 agent 工具 + 右侧面板)",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "files": [
8
+ "lib",
9
+ "client",
10
+ "skill",
11
+ "skills",
12
+ "cordis.patch.yml",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "registry": "https://registry.npmjs.org"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "license": "MIT",
24
+ "keywords": [
25
+ "dsh",
26
+ "dsh-plugin",
27
+ "wkgeo",
28
+ "网站运营",
29
+ "建站"
30
+ ],
31
+ "dsh": {
32
+ "bundle": { "patch": "./cordis.patch.yml" },
33
+ "client": { "platform": "web", "inject": [] }
34
+ },
35
+ "exports": {
36
+ ".": { "default": "./lib/index.js" },
37
+ "./client": "./client/client.js",
38
+ "./cordis.patch.yml": "./cordis.patch.yml",
39
+ "./package.json": "./package.json"
40
+ },
41
+ "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1" }
42
+ }
package/skill/README.md CHANGED
@@ -24,7 +24,7 @@ skill/
24
24
 
25
25
  ## 怎么被消费
26
26
 
27
- - **本机 DSH 桌面**:用 `install-dsh-wkgeo-ops` skill 把 `integrations/dsh-wkgeo-ops/` 整个拷到 `~/.dsh/plugins-src/`,插件加载后本目录的 `*/SKILL.md` 即成为可用 skill。
27
+ - **本机 DSH 桌面**:`npm i dsh-wkgeo-ops` 或 DSH「添加插件」填 `dsh-wkgeo-ops`(v0.2.0 起);开发期用 `install-dsh-wkgeo-ops` skill 把 `integrations/dsh-wkgeo-ops/` 拷到 `~/.dsh/plugins-src/`。插件加载后本目录的 `*/SKILL.md` 即成为可用 skill;配套工具是 `wkgeo_sites_list` / `wkgeo_site_create` / `wkgeo_content_apply` / `wkgeo_content_fill` / `wkgeo_content_check` / `wkgeo_publish`(破坏性操作默认 dry-run)。
28
28
  - **手工加载(不装插件)**:直接把 `wkgeo-site-content/SKILL.md` 的内容贴给 agent,或让 agent 读仓库里的 `docs/规范-建站三来源-20261031.md`(内容等价、面向人读)。
29
29
  - **配套文档**:`docs/规范-建站内容契约.md`(字段与上限)、`docs/规范-建站三来源-20261031.md`(作业流程)。
30
30
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: wkgeo-site-content
3
- description: "Use this skill when an agent must turn customer material (a theme/template, an existing website, or files such as Word/PDF/Excel/images/zip) into a live wkgeo site's content. Two write commands: `wkgeo content apply --file site-manifest.json` (whole-site manifest: categories, fields, assets, contents, singles, menus) and `wkgeo content fill --manifest` (template content contract: sections[]/pages[] filled against template columns[]), both dry-run-first and idempotent, verified with `wkgeo content check`. Covers the three-source workflow (template = structure, website = category skeleton, files = copy and media), dry-run → confirm → verify, exit codes 0/1/2/3, and the hard rules: never touch the database directly, never upload to OSS bypassing `wkgeo assets`, never skip the manifest with hand-written SQL. Do NOT use for platform development, admin/ops actions, or dumping raw scraped HTML."
4
- description_zh: 建站三来源 → 清单 → 落库的作业说明(task-178 合并版):模板定结构、网址定栏目骨架、文件定文案素材;整站走 `wkgeo content apply --file site-manifest.json`,按模板栏目填内容走 `wkgeo content fill --manifest`,都用 `wkgeo content check` 验收。
3
+ description: "Use this skill when an agent must turn customer material (a theme/template, an existing website, or files such as Word/PDF/Excel/images/zip) into a live wkgeo site's content. In the DSH plugin `dsh-wkgeo-ops` call the tools `wkgeo_site_create` / `wkgeo_content_apply` / `wkgeo_content_fill` / `wkgeo_content_check` / `wkgeo_publish` (all destructive ones dry-run first, explicit confirm:true to write); without the plugin the same commands are `wkgeo content apply --file site-manifest.json` (whole-site manifest: categories, fields, assets, contents, singles, menus) and `wkgeo content fill --manifest` (template content contract: sections[]/pages[] filled against template columns[]), both dry-run-first and idempotent, verified with `wkgeo content check`. Covers the three-source workflow (template = structure, website = category skeleton, files = copy and media), dry-run → confirm → verify, exit codes 0/1/2/3, and the hard rules: never touch the database directly, never upload to OSS bypassing `wkgeo assets`, never skip the manifest with hand-written SQL. Do NOT use for platform development, admin/ops actions, or dumping raw scraped HTML."
4
+ description_zh: 建站三来源 → 清单 → 落库的作业说明(task-178 合并版 + v0.2.0 插件工具):DSH 里用 `wkgeo_sites_list` / `wkgeo_site_create` / `wkgeo_content_apply` / `wkgeo_content_fill` / `wkgeo_content_check` / `wkgeo_publish` 对话式建站(破坏性操作默认 dry-run);模板定结构、网址定栏目骨架、文件定文案素材,最后必须 `content check` 验收。
5
5
  description_en: Three-source site content workflow for wkgeo — turn templates, existing sites, and documents into a validated manifest, apply it idempotently with content apply / content fill, then verify with content check.
6
6
  version: 0.2.0
7
7
  ---
@@ -14,7 +14,41 @@ version: 0.2.0
14
14
  完整字段口径:`docs/规范-建站内容契约.md`(`content apply --file`)· `docs/规范-模板内容契约.md` §5(`content fill --manifest`)。
15
15
  作业流程原文:`docs/规范-建站三来源-20261031.md`(唯一一份)。Schema 唯一真源:`wkgeo content apply --schema`。
16
16
 
17
- ## 0. 先认两条写命令(task-178 拆开,别混)
17
+ ## 0. 入口选择:装了 DSH 插件就调工具,没装才拼 CLI
18
+
19
+ 装了 `dsh-wkgeo-ops` 插件(v0.2.0+)时,**优先调插件工具**(前缀 `wkgeo_`,中文描述、参数有 JSON Schema、破坏性操作默认 dry-run),不要把命令拼成 shell 字符串——工具内部就是 `node <repo>/cli/wkgeo.mjs <cmd> … --json` + 解析信封,逻辑同一份。
20
+
21
+ | 工具 | 干什么 | 关键参数 | 破坏性 |
22
+ | --- | --- | --- | --- |
23
+ | `wkgeo_sites_list` | 站点列表 | — | 只读 |
24
+ | `wkgeo_site_status` | 单站详情(域名验证/证书/开通状态) | `site`* | 只读 |
25
+ | `wkgeo_site_preview` | 预览 URL | `site`*、`path` | 只读 |
26
+ | `wkgeo_site_create` | 建站(三步向导链路) | `name`*、`lang`、`source`、`templateKey`、`url`、`dir`、`zipPath`、`confirm` | **写**:无 `confirm:true` 只回计划 |
27
+ | `wkgeo_publish` | 发布站点 | `site`*、`confirm`、`wait` | **写**:无 `confirm:true` 不执行 |
28
+ | `wkgeo_content_apply` | 整站落库(A 线) | `file`*、`site`、`confirm`、`publish`、`wait`、`allowNameMismatch` | **写**:默认 dry-run |
29
+ | `wkgeo_content_fill` | 模板栏目填充(B 线) | `site`、`manifest` 或 `manifestJson`、`lang`、`confirm` | **写**:默认 dry-run |
30
+ | `wkgeo_content_check` | 一致性自检(验收) | `site`、`template`、`lang`、`maxDetails` | 只读 |
31
+ | `wkgeo_templates_list` | 站点模板列表 | `site` | 只读 |
32
+ | `wkgeo_template_init` | 模板骨架 / 网址·目录生成 | `mode`、`url`/`dir`、`key`*、`out`、`version`、`maxPages`… | 写磁盘 |
33
+ | `wkgeo_template_validate` | 模板 9 关校验 | `dir`*、`base`、`noDesign` | 只读 |
34
+ | `wkgeo_template_pack` | 打包模板 zip | `dir`*、`out`、`base` | 写磁盘 |
35
+ | `wkgeo_template_publish` | 发布模板到市场 | `dir`*、`key`、`confirm`、`noPrune`、`archive`、`noPreview` | **写**:默认 dry-run |
36
+ | `wkgeo_domains_list` | 域名 / DNS / 证书 / ESA | `site`(不给=逐站聚合) | 只读 |
37
+ | `wkgeo_template_from_url` / `wkgeo_template_from_dir` | 旧版模板导入(能力探测降级) | `url`*/`dir`、`out`、`site`、`confirm` | 按 `confirm` |
38
+
39
+ **对话式建站的标准顺序(每一步都把结果贴给老板)**:
40
+
41
+ 1. `wkgeo_sites_list` —— 先看已有站点,避免重名。
42
+ 2. `wkgeo_site_create { name }` —— 不带 `confirm` = 只回 `{executed:false,needsConfirm:true,plan[]}`;老板点头后带 `confirm:true`。
43
+ 3. `wkgeo_content_apply { file }` —— 先 dry-run,把 `rejected[]` / `unfilled[]` 念给老板;点头后 `confirm:true`(可带 `publish:true`)。
44
+ 4. `wkgeo_content_check { site }` —— 必须 `ok:true`(score 里 warn 逐条说明)才算做完。
45
+ 5. `wkgeo_publish { site, confirm:true }` —— 线上生效。
46
+
47
+ **返回结构统一**:`{ ok, code, command, executed, needsConfirm, reason?, data?, stderr?, error?, errorCode?, errorHint? }`。
48
+ `code` = CLI 退出码:`0` 成功 / `1` 校验或执行失败 / `2` 用法或环境错误 / `3` 需要重新授权(让老板跑 `node <repo>/cli/wkgeo.mjs login`)/ `4` 被限流;`-1` 是插件内部错误(如 `MISSING_KEY`、`MISSING_DIR`、`SPAWN_FAILED`、`TIMEOUT`)。`errorHint` 是中文处置建议,`stderr` 只留首行。
49
+ 没装插件时,用 `wkgeo <cmd> … --json` 得到同一份 `{ok, code, data, error}` 信封,语义等价。
50
+
51
+ ## 0.1 两条写命令(task-178 拆开,别混)
18
52
 
19
53
  | 场景 | 命令 | 清单形状 |
20
54
  | --- | --- | --- |
@@ -18,7 +18,7 @@ author: 云站(wkgeo)
18
18
  - 规范原文:`docs/规范-建站三来源-20261031.md`(三来源作业流程,唯一一份)
19
19
  - 契约:`docs/规范-建站内容契约.md`(`content apply --file`)· `docs/规范-模板内容契约.md` §5(`content fill --manifest`)
20
20
 
21
- 命令名在 task-178 拆开,本文旧版里的 `wkgeo content apply --manifest` **已失效**(会 exit 2):
21
+ 命令名在 task-178 拆开,本文旧版里的 `wkgeo content apply --manifest` **已失效**(会 exit 2);v0.2.0 起装了 `dsh-wkgeo-ops` 插件的话,**优先调工具**(`wkgeo_content_apply` / `wkgeo_content_fill` / `wkgeo_content_check` / `wkgeo_publish`,破坏性操作默认 dry-run、`confirm:true` 才写),等价 CLI 命令是:
22
22
 
23
23
  ```bash
24
24
  wkgeo content apply --file site-manifest.json --site <站ID> # 整站清单:dry-run