draftgo-cli 4.0.23 → 4.0.24

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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/bin/draftgo.js +8 -8
  3. package/package.json +72 -72
  4. package/resources/custom-service-sdk/auth_test.go +1 -1
  5. package/resources/custom-service-sdk/manifest.json +11 -11
  6. package/resources/custom-service-sdk/platform.go +10 -3
  7. package/resources/custom-service-sdk/resources.go +1 -0
  8. package/resources/custom-service-sdk/resources_scope_test.go +10 -5
  9. package/resources/custom-service-sdk/sdk.go +4 -3
  10. package/resources/skill/SKILL.md +1 -1
  11. package/resources/skill/manifest.json +1 -1
  12. package/resources/skill/references/aihub.md +74 -74
  13. package/resources/skill/references/app-api.md +78 -78
  14. package/resources/skill/references/architecture.md +40 -40
  15. package/resources/skill/references/checkout.md +105 -105
  16. package/resources/skill/references/custom-services.md +4 -4
  17. package/resources/skill/references/data.md +168 -168
  18. package/resources/skill/references/methods.md +3 -0
  19. package/resources/skill/references/modules.md +47 -47
  20. package/resources/skill/references/runtime.md +95 -96
  21. package/resources/skill/story/SKILL.md +264 -264
  22. package/src/commands/help.js +72 -72
  23. package/src/commands/listTargets.js +12 -12
  24. package/src/commands/status.js +2 -2
  25. package/src/commands/uninstall.js +45 -45
  26. package/src/commands/update.js +20 -20
  27. package/src/customServices.js +5 -4
  28. package/src/detect.js +14 -14
  29. package/src/fsx.js +67 -67
  30. package/src/index.js +25 -25
  31. package/src/localRuntime/detect.js +76 -76
  32. package/src/localRuntime/mysqlClient.js +138 -138
  33. package/src/logger.js +37 -37
  34. package/src/mcp/client.js +586 -595
  35. package/src/mcp/hosts.js +520 -520
  36. package/src/mcp/protocol.js +167 -167
  37. package/src/prompt.js +94 -94
  38. package/src/updateCheck.js +16 -16
@@ -1,77 +1,77 @@
1
- ---
2
- read_when: 页面开发时需要查 App API · 忘记某个方法签名时
3
- ---
4
-
5
- # App 对象速查表
6
-
7
- ```javascript
8
- const App = window.parent?.App;
9
- ```
10
-
11
- ## 请求
12
-
13
- | 方法 | 签名 | 说明 |
14
- |---|---|---|
1
+ ---
2
+ read_when: 页面开发时需要查 App API · 忘记某个方法签名时
3
+ ---
4
+
5
+ # App 对象速查表
6
+
7
+ ```javascript
8
+ const App = window.parent?.App;
9
+ ```
10
+
11
+ ## 请求
12
+
13
+ | 方法 | 签名 | 说明 |
14
+ |---|---|---|
15
15
  | `App.get` | `(path, params?, headers?)` | GET,params 为 query 参数 |
16
16
  | `App.post` | `(path, body?, headers?)` | POST |
17
17
  | `App.put` | `(path, body?, headers?)` | PUT |
18
18
  | `App.patch` | `(path, body?, headers?)` | PATCH |
19
19
  | `App.delete` | `(path, params?, headers?)` | DELETE |
20
- | `App.uploadFile` | `(file, onProgress?)` | 文件上传,返回标准信封 |
21
-
22
- **响应格式**:`{ code: 200, data: <载荷>, message: "success" }`
23
- **消费范式**:
24
- ```javascript
25
- const res = await App.get('pages'); // 不传分页参数时全量返回
26
- if (res.code !== 200) { App.showError(res.message); return; }
27
- const items = res.data.items;
28
-
29
- const paged = await App.get('pages', { page: 1, page_size: 20 }); // 显式分页
30
- ```
31
-
32
- ## 反馈
33
-
34
- | 方法 | 签名 | 说明 |
35
- |---|---|---|
20
+ | `App.uploadFile` | `(file, onProgress?)` | 文件上传,返回标准信封 |
21
+
22
+ **响应格式**:`{ code: 200, data: <载荷>, message: "success" }`
23
+ **消费范式**:
24
+ ```javascript
25
+ const res = await App.get('pages'); // 不传分页参数时全量返回
26
+ if (res.code !== 200) { App.showError(res.message); return; }
27
+ const items = res.data.items;
28
+
29
+ const paged = await App.get('pages', { page: 1, page_size: 20 }); // 显式分页
30
+ ```
31
+
32
+ ## 反馈
33
+
34
+ | 方法 | 签名 | 说明 |
35
+ |---|---|---|
36
36
  | `App.showSuccess` | `(msg)` | 成功 Toast |
37
37
  | `App.showError` | `(msg)` | 错误 Toast |
38
38
  | `App.showWarning` | `(msg)` | 警告 Toast |
39
39
  | `App.showInfo` | `(msg)` | 信息 Toast |
40
- | `App.toast` | `(msg, type?)` | 通用 Toast,type: success/error/warning/info |
41
- | `App.confirm` | `(msg, title?)` | 确认弹窗,返回 `Promise<boolean>` |
42
- | `App.showModal` | `(msg, title?)` | 信息模态框(替代 alert) |
43
- | `App.showLoading` | `()` | 全局 loading 蒙层 |
44
- | `App.hideLoading` | `()` | 关闭 loading |
45
-
46
- ## 路由
47
-
48
- | 方法 | 说明 |
49
- |---|---|
50
- | `App.navigate(route)` | 路由跳转(pushState) |
51
- | `App.getCurrentRoute()` | 当前路径字符串 |
52
- | `App.getCurrentRouteContext()` | 完整路由上下文,含 `query` |
53
-
54
- **读取 URL 参数**(必须用此方式,不能用 `window.location.search`):
55
- ```javascript
56
- const routeContext =
57
- window.__DG_ROUTE_CONTEXT__
58
- || window.__DG_GET_ROUTE_CONTEXT__?.()
59
- || window.parent?.App?.getCurrentRouteContext?.()
60
- || { query: {} };
61
- const { patientId, visitId } = routeContext.query;
62
- ```
63
-
64
- ## 状态
65
-
66
- | 属性 | 类型 | 说明 |
67
- |---|---|---|
68
- | `App.currentUser` | object \| null | 当前用户,未登录为 null |
40
+ | `App.toast` | `(msg, type?)` | 通用 Toast,type: success/error/warning/info |
41
+ | `App.confirm` | `(msg, title?)` | 确认弹窗,返回 `Promise<boolean>` |
42
+ | `App.showModal` | `(msg, title?)` | 信息模态框(替代 alert) |
43
+ | `App.showLoading` | `()` | 全局 loading 蒙层 |
44
+ | `App.hideLoading` | `()` | 关闭 loading |
45
+
46
+ ## 路由
47
+
48
+ | 方法 | 说明 |
49
+ |---|---|
50
+ | `App.navigate(route)` | 路由跳转(pushState) |
51
+ | `App.getCurrentRoute()` | 当前路径字符串 |
52
+ | `App.getCurrentRouteContext()` | 完整路由上下文,含 `query` |
53
+
54
+ **读取 URL 参数**(必须用此方式,不能用 `window.location.search`):
55
+ ```javascript
56
+ const routeContext =
57
+ window.__DG_ROUTE_CONTEXT__
58
+ || window.__DG_GET_ROUTE_CONTEXT__?.()
59
+ || window.parent?.App?.getCurrentRouteContext?.()
60
+ || { query: {} };
61
+ const { patientId, visitId } = routeContext.query;
62
+ ```
63
+
64
+ ## 状态
65
+
66
+ | 属性 | 类型 | 说明 |
67
+ |---|---|---|
68
+ | `App.currentUser` | object \| null | 当前用户,未登录为 null |
69
69
  | `App.isAdmin` | boolean | 是否管理员 |
70
70
  | `App.permissions` | string[] | 当前用户有效 permission grant 的展示投影;仅用于页面显隐,不替代服务端授权 |
71
71
  | `App.isAuthenticated` | boolean | 是否已认证 |
72
- | `App.hasToken` | boolean | 是否有 token(含未验证) |
73
- | `App.config` | object | 系统配置 KV |
74
- | `App.theme` | `'light'` \| `'dark'` | 当前显示模式 |
72
+ | `App.hasToken` | boolean | 是否有 token(含未验证) |
73
+ | `App.config` | object | 系统配置 KV |
74
+ | `App.theme` | `'light'` \| `'dark'` | 当前显示模式 |
75
75
  | `App.colorScheme` | string | 当前配色方案 |
76
76
  | `App.scopeContext` | object | null | 当前候选范围;`scope_type` 为 `platform` / `space` |
77
77
  | `App.availableSpaces` | object[] | 当前用户可选择的工作区与递归空间 |
@@ -118,22 +118,22 @@ await App.get('business-items', { page: 1 });
118
118
  const platformHeaders = { 'X-DraftGo-Scope-Type': 'platform' };
119
119
  await App.get('roles', {}, platformHeaders);
120
120
  ```
121
-
122
- ## 主题
123
-
124
- ```javascript
125
- App.applyTheme('dark'); // 切换显示模式
126
- App.setColorScheme('deep-blue-white'); // 切换预设配色
127
- App.setColorScheme('custom', customVarsObject); // 自定义配色
128
- App.getColorScheme(); // 获取方案详情
129
- ```
130
-
131
- ## 其他
132
-
133
- ```javascript
121
+
122
+ ## 主题
123
+
124
+ ```javascript
125
+ App.applyTheme('dark'); // 切换显示模式
126
+ App.setColorScheme('deep-blue-white'); // 切换预设配色
127
+ App.setColorScheme('custom', customVarsObject); // 自定义配色
128
+ App.getColorScheme(); // 获取方案详情
129
+ ```
130
+
131
+ ## 其他
132
+
133
+ ```javascript
134
134
  await App.logout(); // 服务端登出、清理状态并跳转配置的登录页
135
- App.setAuthTokens({ access_token, refresh_token }); // 登录后写入 token
136
- App.reloadGlobalLayer(); // 重载全局层
135
+ App.setAuthTokens({ access_token, refresh_token }); // 登录后写入 token
136
+ App.reloadGlobalLayer(); // 重载全局层
137
137
  App.openGlobalWidget(name); // 触发全局挂件打开
138
138
  App.getScopeContext(); // 读取当前作用域
139
139
  App.setScopeContext({ scope_type: 'space', space_id: '120' });
@@ -141,5 +141,5 @@ App.clearScopeContext(); // 清除手工选择,恢复服务端默
141
141
  App.t(key, fallback?, values?); // 页面级国际化文本,values 保留 ICU 占位符
142
142
  App.formatDateTime(value, options?); // 按 system_timezone 格式化 API 返回的 UTC 时间
143
143
  ```
144
-
144
+
145
145
  Token、刷新、路由上下文和认证事件见 `references/runtime.md`。AI 对话、图片和兼容门面的完整契约见 `references/chat-sdk.md`,Agent 能力见 `references/aihub.md`。
@@ -1,46 +1,46 @@
1
- ---
2
- read_when: 进入陌生 DraftGo 项目时 · 理解平台机制时(3 分钟速读)
3
- ---
4
-
5
- # DraftGo 架构认知
6
-
7
- ## 核心概念(30 秒)
8
-
9
- DraftGo **不是传统 SPA**,是「数据库驱动的页面资产运行时」:
10
-
11
- - 壳层(React + Vite)负责平台前端、runtime 编排、管理界面
12
- - 业务页面 HTML 存在数据库 `page.value.html`,运行在 `iframe.srcdoc`
13
- - 导航栏 HTML 存在数据库 `navigation.html`,壳层按需加载
14
- - 页面与壳层通过 `window.parent.App` 通信
15
-
16
- ---
17
-
18
- ## 为什么这样设计(1 分钟)
19
-
20
- | 问题 | 传统 SPA | DraftGo 方案 |
21
- |---|---|---|
22
- | 页面内容更新 | 需要重新编译部署 | 直接改数据库 HTML,秒级生效 |
23
- | 多租户/多项目 | 需要多套工程 | 一套壳层,数据库隔离内容 |
24
- | 权限控制 | 路由守卫 + 条件渲染 | `page.permission` 字段控制,壳层统一拦截 |
1
+ ---
2
+ read_when: 进入陌生 DraftGo 项目时 · 理解平台机制时(3 分钟速读)
3
+ ---
4
+
5
+ # DraftGo 架构认知
6
+
7
+ ## 核心概念(30 秒)
8
+
9
+ DraftGo **不是传统 SPA**,是「数据库驱动的页面资产运行时」:
10
+
11
+ - 壳层(React + Vite)负责平台前端、runtime 编排、管理界面
12
+ - 业务页面 HTML 存在数据库 `page.value.html`,运行在 `iframe.srcdoc`
13
+ - 导航栏 HTML 存在数据库 `navigation.html`,壳层按需加载
14
+ - 页面与壳层通过 `window.parent.App` 通信
15
+
16
+ ---
17
+
18
+ ## 为什么这样设计(1 分钟)
19
+
20
+ | 问题 | 传统 SPA | DraftGo 方案 |
21
+ |---|---|---|
22
+ | 页面内容更新 | 需要重新编译部署 | 直接改数据库 HTML,秒级生效 |
23
+ | 多租户/多项目 | 需要多套工程 | 一套壳层,数据库隔离内容 |
24
+ | 权限控制 | 路由守卫 + 条件渲染 | `page.permission` 字段控制,壳层统一拦截 |
25
25
  | 导航定制 | 改代码重部署 | MCP 定位后 checkout navigation.html,commit 即生效 |
26
-
27
- **核心推论**:在 DraftGo 里「修改页面」= 修改数据库里的 HTML 字符串,不是修改 `.tsx` 文件。
28
-
29
- ---
30
-
26
+
27
+ **核心推论**:在 DraftGo 里「修改页面」= 修改数据库里的 HTML 字符串,不是修改 `.tsx` 文件。
28
+
29
+ ---
30
+
31
31
  ## App 对象是什么(1 分钟)
32
32
 
33
33
  壳层将能力对象赋值给 `window.App`,数据库页面通过 `window.parent.App` 使用带认证的请求、反馈、路由、当前用户和主题能力。方法签名见 `references/app-api.md`;iframe 注入、认证事件和路由参数见 `references/runtime.md`。
34
-
35
- ---
36
-
37
- ## 技术栈(30 秒)
38
-
39
- | 层 | 技术 |
40
- |---|---|
41
- | 后端 | Go 1.26 · 标准库 `net/http` · `database/sql`(go-sql-driver/mysql)· MySQL · Redis |
42
- | 壳层前端 | React · Vite |
34
+
35
+ ---
36
+
37
+ ## 技术栈(30 秒)
38
+
39
+ | 层 | 技术 |
40
+ |---|---|
41
+ | 后端 | Go 1.26 · 标准库 `net/http` · `database/sql`(go-sql-driver/mysql)· MySQL · Redis |
42
+ | 壳层前端 | React · Vite |
43
43
  | 数据库页面 | HTML 文档运行时 · Tailwind CSS 4 · Basecoat UI(默认)· Oat UI(可选)· Font Awesome · GSAP · 内置 SVG 图标库 |
44
- | CLI | Node.js(draftgo-cli) |
45
-
44
+ | CLI | Node.js(draftgo-cli) |
45
+
46
46
  数据库页面以完整 HTML 文档运行,直接加载平台提供的本地资源;壳层源码按 React + Vite 工程构建。开发时按目标所属层使用上表对应技术栈。
@@ -1,7 +1,7 @@
1
- ---
2
- read_when: 编辑 pages、navigation 或 docs 正文时 · 查看 checkout manifest 时 · 处理 409/412 冲突时
3
- ---
4
-
1
+ ---
2
+ read_when: 编辑 pages、navigation 或 docs 正文时 · 查看 checkout manifest 时 · 处理 409/412 冲突时
3
+ ---
4
+
5
5
  # Checkout / Commit
6
6
 
7
7
  ## 页面与内容最短流程
@@ -21,120 +21,120 @@ draftgo commit pages 42
21
21
  The checkout set includes `pages`, `navigations`, `docs/articles`, and `custom_services`. A custom-service checkout contains exactly `service.go` and `service.json`, plus one complete `.base` directory. Runner-managed `go.mod/go.sum` never participate in diff or commit. `draftgo commit custom-services <id>` performs the complete delivery (`commit -> validate -> publish`) and prints an explicit published confirmation. Use `draftgo test custom-services <id>` for server Runner validation/execution; `draftgo publish custom-services <id>` remains as a direct compatibility entry point. DB Meta remains a live MCP/API resource and is never checked out.
22
22
 
23
23
  `draftgo refresh <type> <id...>` is a safe checkout shortcut. It updates only a clean local worktree; local changes stop it. A successful commit keeps current local files and one base only. Cloud version storage is the sole history source.
24
-
25
- > 根 `SKILL.md` 在 Skill 触发时会自动加载。使用本文件前,先完成根 Skill 的“强制预读:Reference 优先于 MCP”任务路由。本文件只说明长正文的传输、版本和冲突规则,不能替代页面、前端、运行时或安全资料。
26
-
27
- ## 适用范围
28
-
29
- 只有长正文使用 worktree:
30
-
31
- | 输入类型 | 规范类型 | 本地目录 | 文件前缀 |
32
- |---|---|---|---|
33
- | `page` / `pages` | `pages` | `.draftgo/worktree/pages/` | `page_` |
34
- | `nav` / `navigation` / `navigations` | `navigations` | `.draftgo/worktree/navigations/` | `nav_` |
35
- | `doc` / `docs` / `article(s)` / `docs/articles` | `docs` | `.draftgo/worktree/docs/` | `article_` |
36
-
24
+
25
+ > 根 `SKILL.md` 在 Skill 触发时会自动加载。使用本文件前,先完成根 Skill 的“强制预读:Reference 优先于 MCP”任务路由。本文件只说明长正文的传输、版本和冲突规则,不能替代页面、前端、运行时或安全资料。
26
+
27
+ ## 适用范围
28
+
29
+ 只有长正文使用 worktree:
30
+
31
+ | 输入类型 | 规范类型 | 本地目录 | 文件前缀 |
32
+ |---|---|---|---|
33
+ | `page` / `pages` | `pages` | `.draftgo/worktree/pages/` | `page_` |
34
+ | `nav` / `navigation` / `navigations` | `navigations` | `.draftgo/worktree/navigations/` | `nav_` |
35
+ | `doc` / `docs` / `article(s)` / `docs/articles` | `docs` | `.draftgo/worktree/docs/` | `article_` |
36
+
37
37
  db_meta、AIHub、system_config、roles、users、doc_categories 和普通配置使用 MCP 实时 API,不 checkout。
38
38
 
39
39
  Checkout 只为已存在且已确认 ID 的资源建立本地正文与 base,不创建页面、导航或文档。新增资源先按 MCP 实时 API 契约创建并取得 ID;需要编辑完整正文时再 checkout。只需元数据或正文片段即可完成判断时,不必 checkout。
40
-
41
- ## 命令
42
-
43
- ```bash
44
- draftgo checkout <pages|nav|docs> <id...>
40
+
41
+ ## 命令
42
+
43
+ ```bash
44
+ draftgo checkout <pages|nav|docs> <id...>
45
45
  draftgo commit <pages|nav|docs> <id...>
46
46
  draftgo reconcile <pages|nav|docs> <id...>
47
47
  draftgo diff <pages|nav|docs|custom-services> <id> [--stat|--summary]
48
- draftgo conflicts
49
- draftgo conflict show <pages|nav|docs> <id>
50
- draftgo conflict resolve <pages|nav|docs> <id>
51
- ```
52
-
48
+ draftgo conflicts
49
+ draftgo conflict show <pages|nav|docs> <id>
50
+ draftgo conflict resolve <pages|nav|docs> <id>
51
+ ```
52
+
53
53
  `checkout --force` 只用于用户明确允许丢弃未提交本地修改的情况。默认 checkout 检测到 worktree 文件相对
54
54
  base 已变化时必须拒绝覆盖。
55
55
 
56
56
  `diff --stat` 只显示文件与增删行数;`diff --summary` 显示资源、基线版本和变化概要。两者均不输出正文 diff,先用它们确认范围,再按需展开完整 `diff`。`--output json` 时 stdout 只包含 UTF-8 JSON,诊断走 stderr。
57
-
58
- ## Checkout 流程
59
-
60
- 1. CLI 通过 MCP `draftgo_resource_get_metadata` 取得规范类型、content_type、SHA-256、大小、版本/revision、
61
- ETag 和受信任的下载/提交 URL。
57
+
58
+ ## Checkout 流程
59
+
60
+ 1. CLI 通过 MCP `draftgo_resource_get_metadata` 取得规范类型、content_type、SHA-256、大小、版本/revision、
61
+ ETag 和受信任的下载/提交 URL。
62
62
  2. CLI 使用 `.draftgo/config.json` 中的用户 API Key 通过专用 HTTP 下载完整正文;API Key 不进入 MCP 参数或日志。
63
- 3. 响应体直接流式写入同目录临时文件,校验 content_type、字节数和 SHA-256。
64
- 4. 校验成功后原子重命名到 worktree 文件,并保存相同字节的 `.base` 文件。
65
- 5. 最后原子更新 `.draftgo/worktree/manifest.json`。失败时不得留下半截正式文件或推进 manifest。
66
-
67
- 正文不做 HTML/Markdown 转换,也不改变编码。扩展名规则:
68
-
69
- - `text/html`、`application/xhtml+xml` -> `.html`
70
- - `text/markdown`、`text/x-markdown` -> `.md`
71
- - `text/plain` -> `.txt`
72
- - 其他类型只接受底座返回的安全扩展名;缺失或不安全时拒绝 checkout
73
-
74
- ## Manifest
75
-
76
- 路径固定为 `.draftgo/worktree/manifest.json`,schema version 当前为 `1`。条目键使用规范类型和 id:
77
-
78
- ```json
79
- {
80
- "schema_version": 1,
81
- "updated_at": "2026-07-30T12:00:00.000Z",
82
- "entries": {
83
- "pages:42": {
84
- "server": "https://draftgo.example",
85
- "resource_type": "pages",
86
- "resource_id": "42",
87
- "title": "Example",
88
- "route": "/example",
89
- "code": null,
90
- "slug": null,
91
- "local_path": ".draftgo/worktree/pages/page_42.html",
92
- "content_type": "text/html",
93
- "file_extension": ".html",
94
- "content_size": 123,
95
- "base_path": ".draftgo/worktree/.base/pages/page_42.html",
96
- "base_version": "7",
97
- "base_revision": null,
98
- "base_etag": null,
99
- "base_hash": "<sha256>",
100
- "checked_out_at": "2026-07-30T12:00:00.000Z"
101
- }
102
- }
103
- }
104
- ```
105
-
106
- `server` 必须和当前连接一致。`local_path`、`base_path` 必须是项目内相对路径。manifest、worktree `.base`
107
- 和冲突目录必须 gitignore;不要手工伪造版本或哈希。
108
-
109
- ## Commit 流程
110
-
111
- 1. 读取 manifest 指向的 worktree 文件,计算当前字节数和 SHA-256;未变化时返回 `unchanged`。
63
+ 3. 响应体直接流式写入同目录临时文件,校验 content_type、字节数和 SHA-256。
64
+ 4. 校验成功后原子重命名到 worktree 文件,并保存相同字节的 `.base` 文件。
65
+ 5. 最后原子更新 `.draftgo/worktree/manifest.json`。失败时不得留下半截正式文件或推进 manifest。
66
+
67
+ 正文不做 HTML/Markdown 转换,也不改变编码。扩展名规则:
68
+
69
+ - `text/html`、`application/xhtml+xml` -> `.html`
70
+ - `text/markdown`、`text/x-markdown` -> `.md`
71
+ - `text/plain` -> `.txt`
72
+ - 其他类型只接受底座返回的安全扩展名;缺失或不安全时拒绝 checkout
73
+
74
+ ## Manifest
75
+
76
+ 路径固定为 `.draftgo/worktree/manifest.json`,schema version 当前为 `1`。条目键使用规范类型和 id:
77
+
78
+ ```json
79
+ {
80
+ "schema_version": 1,
81
+ "updated_at": "2026-07-30T12:00:00.000Z",
82
+ "entries": {
83
+ "pages:42": {
84
+ "server": "https://draftgo.example",
85
+ "resource_type": "pages",
86
+ "resource_id": "42",
87
+ "title": "Example",
88
+ "route": "/example",
89
+ "code": null,
90
+ "slug": null,
91
+ "local_path": ".draftgo/worktree/pages/page_42.html",
92
+ "content_type": "text/html",
93
+ "file_extension": ".html",
94
+ "content_size": 123,
95
+ "base_path": ".draftgo/worktree/.base/pages/page_42.html",
96
+ "base_version": "7",
97
+ "base_revision": null,
98
+ "base_etag": null,
99
+ "base_hash": "<sha256>",
100
+ "checked_out_at": "2026-07-30T12:00:00.000Z"
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
106
+ `server` 必须和当前连接一致。`local_path`、`base_path` 必须是项目内相对路径。manifest、worktree `.base`
107
+ 和冲突目录必须 gitignore;不要手工伪造版本或哈希。
108
+
109
+ ## Commit 流程
110
+
111
+ 1. 读取 manifest 指向的 worktree 文件,计算当前字节数和 SHA-256;未变化时返回 `unchanged`。
112
112
  2. 按 content_type 执行本地结构和内联脚本检查。
113
- 3. 通过专用 HTTP 流式上传原始文件,携带 `If-Match`、base version/revision、content_type、长度和 SHA-256。
114
- 4. 完整正文不得作为 MCP tool 参数发送。
115
- 5. 底座确认 hash 和新版本后,CLI 原子更新 `.base` 与 manifest。返回 hash 不一致时不得推进基线。
116
-
113
+ 3. 通过专用 HTTP 流式上传原始文件,携带 `If-Match`、base version/revision、content_type、长度和 SHA-256。
114
+ 4. 完整正文不得作为 MCP tool 参数发送。
115
+ 5. 底座确认 hash 和新版本后,CLI 原子更新 `.base` 与 manifest。返回 hash 不一致时不得推进基线。
116
+
117
117
  本地正文已经等于远端、但 base/manifest 落后时,先用 `draftgo check --remote` 确认 `committed_unrecorded`,再运行 `draftgo reconcile`;不要手改 manifest。
118
118
 
119
119
  单个 commit 成功不自动完成 worklog 项。只有整个事项统一验证且全部 commit/MCP 交付成功后,主 Agent 才执行 `draftgo work complete <编号> --note "<完成结果>"`。任何检查失败、409/412 或交付失败都不得标记为完成。
120
-
121
- ## 409 / 412 冲突
122
-
123
- 版本冲突时 CLI 返回非零,不自动重试、不 force、不覆盖 worktree local,并写入:
124
-
125
- ```text
126
- .draftgo/conflicts/<pages|navigations|docs>/<id>/
127
- ├── conflict.json
128
- ├── base.<ext>
129
- ├── local.<ext>
130
- └── remote.<ext>
131
- ```
132
-
133
- - `base` 是 checkout 时的内容;`local` 是发生冲突时的本地快照;`remote` 是重新下载并校验的当前远端内容。
134
- - `conflict.json` 记录三份路径、版本、ETag 和哈希,不嵌入完整正文。
135
- - Agent 或用户在 worktree local 文件中完成合并;不要手写 HTML 自动合并器,也不要改动保存的三份证据。
120
+
121
+ ## 409 / 412 冲突
122
+
123
+ 版本冲突时 CLI 返回非零,不自动重试、不 force、不覆盖 worktree local,并写入:
124
+
125
+ ```text
126
+ .draftgo/conflicts/<pages|navigations|docs>/<id>/
127
+ ├── conflict.json
128
+ ├── base.<ext>
129
+ ├── local.<ext>
130
+ └── remote.<ext>
131
+ ```
132
+
133
+ - `base` 是 checkout 时的内容;`local` 是发生冲突时的本地快照;`remote` 是重新下载并校验的当前远端内容。
134
+ - `conflict.json` 记录三份路径、版本、ETag 和哈希,不嵌入完整正文。
135
+ - Agent 或用户在 worktree local 文件中完成合并;不要手写 HTML 自动合并器,也不要改动保存的三份证据。
136
136
  - 合并完成后运行 `draftgo verify`,再执行 `draftgo conflict resolve <type> <id>`。
137
- - resolve 校验 worktree 与 remote,采用 remote 版本作为新的 base,但保留合并后的 worktree;随后运行
138
- `draftgo diff` 并 `draftgo commit`。
139
-
140
- 存在 unresolved conflict 时 commit、deploy 或 auto-commit 必须停止。
137
+ - resolve 校验 worktree 与 remote,采用 remote 版本作为新的 base,但保留合并后的 worktree;随后运行
138
+ `draftgo diff` 并 `draftgo commit`。
139
+
140
+ 存在 unresolved conflict 时 commit、deploy 或 auto-commit 必须停止。
@@ -62,7 +62,7 @@ func health(ctx *sdk.Context) (any, error) {
62
62
 
63
63
  输入字段为 `method`、`headers`、`body`、`query_params`、`path_params`。身份由 `ctx.Auth.CurrentUser()` 获取。普通 `ctx.DB`、`ctx.Users`、`ctx.Billing` 等调用继承请求调用者权限;管理员创建服务、持有服务凭据或 `RequireAdmin` 都不会自动提升 SDK 调用。
64
64
 
65
- `ctx.Admin.*` 是自定义服务显式选择的单次系统内部授权:该次 RPC `*:*:all` 全权限执行,可跨用户/空间,不检查调用者或服务主体 AccessGrant;服务主体仅保留用于 ownership 与审计。每次调用都会进入审计,内部授权不会泄漏到后续普通调用。服务代码仍拿不到服务凭据、数据库连接或管理员凭据。公开 Route 不应无条件调用 Admin;先完成业务鉴权、参数校验和幂等设计,返回值仍需自行脱敏。
65
+ `ctx.Admin.*` 是自定义服务显式选择的单次系统内部授权:该次 RPC 使用 system actor_id=0,按 `*:*:all` 全权限执行,可跨用户/空间,不检查 AccessGrant;每次调用进入审计,内部授权不会泄漏到后续普通调用。Event/Scheduled 没有调用者,普通 `ctx.xxx` 返回 403。服务代码仍拿不到服务凭据、数据库连接或管理员凭据。公开 Route 不应无条件调用 Admin;先完成业务鉴权、参数校验和幂等设计,返回值仍需自行脱敏。
66
66
 
67
67
  ### Event
68
68
 
@@ -70,7 +70,7 @@ func health(ctx *sdk.Context) (any, error) {
70
70
 
71
71
  ### Scheduled
72
72
 
73
- `app.Schedule("0 2 * * *", cleanup)` 使用五字段 cron,也支持 `interval:5m`。定时任务没有用户调用者;执行空间来自服务持久化的 ResourceOwnership,服务 principal 还必须有覆盖目标资源的 AccessGrant。运行时不会猜测用户或回退到 platform。
73
+ `app.Schedule("0 2 * * *", cleanup)` 使用五字段 cron,也支持 `interval:5m`。定时任务没有用户调用者;执行空间来自服务持久化的 ResourceOwnership,普通 SDK 调用必须改用 `ctx.Admin.*`,运行时不会猜测用户或回退到 platform。
74
74
 
75
75
  ## 试运行
76
76
 
@@ -180,8 +180,8 @@ typed helper 未覆盖的新端点可使用 `ctx.AIHub.Request`,但 path 只
180
180
  - platform Grant 可跨空间但只能使用显式权限;space Grant 不能跨根。请求中的范围不能覆盖服务已保存的归属。
181
181
  - 普通服务调用访问动态 DB 时仍受目标 DB 的 DataRange(`none` / `own` / `all`)限制;`ctx.Admin.*` 只应用于源码明确选择的单次可信管理操作,不能从请求参数隐式开启。
182
182
  - Route 不接受服务级 `permission`;入口由宿主认证、服务 ownership 和调用者 `scripts:execute` AccessGrant 控制,业务级公开/登录规则在 handler 内显式实现。
183
- - 定时任务和无可解析用户的事件默认使用服务主体及其持久化空间;需要平台级或跨归属管理时必须在对应单次调用显式使用 `ctx.Admin.*`。
184
- - 服务 principal 由平台运行时注入;不要在 `service.json`、源码、测试参数或日志中保存/模拟服务身份凭据。Route/Event 取调用者与服务 Grant 的权限交集,Scheduled 使用服务持久化 ownership,而不是假造用户 API Key
183
+ - 定时任务和事件没有调用者,普通 `ctx.xxx` 直接返回 403;必须在对应单次调用显式使用 `ctx.Admin.*`。服务归属仍由持久化 ResourceOwnership 提供,不创建服务主体授权。
184
+ - 平台运行时注入真实 user 或 system principal;不要在 `service.json`、源码、测试参数或日志中保存/模拟服务身份凭据。Route 普通调用按用户 AccessGrant,Event/Scheduled 仅允许 Admin RPC
185
185
  - `config.timeout`、`max_concurrency`、`queue_timeout_ms` 控制执行;Route 饱和返回 429。
186
186
  - 超时会终止独立子进程。运行器不是不可信多租户安全沙箱,只授予可信编辑者服务权限。
187
187
  - 构建键覆盖源码、依赖、SDK 和 Runner 协议;有效验证凭证绑定该键,任一部分变化都必须重新验证。