draftgo-cli 2.0.3 → 2.0.9

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.
@@ -6,11 +6,14 @@ version: 1.0.0
6
6
 
7
7
  # DraftGo 开发助手
8
8
 
9
+ > **【DraftGo 开发追求】**
10
+ > 使用 DraftGo-CLI 开发的项目,默认追求:**开发的急速感、逻辑与实现的完整、迭代性强**。AI 应快速读懂现有资源,优先交付真实落地闭环、高可用的实现,并让后续修改者能继续迭代。若本地 Agent 环境存在前端 UI 相关 Skills,前端界面开发时优先调用。
11
+ >
9
12
  > **【开发分级 — 最高优先级】**
10
13
  > 任何开发对话开始时,**必须先读 `{{SKILL_DIR}}/rules/dev-workflow.md` 判断任务级别**,再执行对应流程:
11
- > - **小修** → 直接定位 → 改 → check → push。优先只读目标资源和最小必要规则;无需 Story 门、需求门、Task 文档;仍需写 changelog。
12
- > - **轻功能** → 范围复述 → 直接做 → 主路径验证 → push。用于简单页面能力、单入口交互、小型数据联动,不强制 Task。
13
- > - **标准功能** → 轻量确认 → TaskTDR。若 `.draftgo/story.yaml` 存在则静默加载;不存在时不阻塞开发,但应在完成后提醒补 Story。
14
+ > - **小修** → 直接定位 → 改 → 轻量证据。优先只读目标资源和最小必要规则;无需 Story 门、需求门、Task 文档;changelog / check / push 按影响选择。
15
+ > - **轻功能** → 范围复述 → 直接做 → 凭证据闭环。用于简单页面能力、单入口交互、小型数据联动,不强制 Task。
16
+ > - **标准功能** → 轻量确认 → 内部短计划执行闭环。若 `.draftgo/story.yaml` 存在则静默加载;不存在时不阻塞开发,但应在完成后提醒补 Story;只有跨资源、多页面协作、并行或高风险时才落 Task
14
17
  > - **高风险** → 完整 Story / 计划 / 验证 / 人工确认。无 `.draftgo/story.yaml` 时必须先构建 Story。
15
18
  >
16
19
  > **开发中发现请求与已有 Story 冲突时,必须显式提示开发者,不可静默执行。** 详见 `{{SKILL_DIR}}/story/SKILL.md` 冲突检测章节。
@@ -18,17 +21,23 @@ version: 1.0.0
18
21
  > **【开发任务硬性流程】**
19
22
  > 任何"开发 / 修改 / 新建 / 修复 / 重构 / 完善 / 优化"指令,**必须先读 `{{SKILL_DIR}}/rules/dev-workflow.md` 做任务分级和用户意图翻译**。标准功能 / 高风险任务追问用户时必须带上 AI 自己的意图推测(推测 + 2-3 个选项 + 推荐项),不能空着问;能合理推断的轻功能不因模板追问拖慢。前端开发按任务规模读取 `{{SKILL_DIR}}/rules/frontend.md` 的相关规则。
20
23
  >
21
- > **【更新日志硬性步骤】**
22
- > 每次完成任何开发 / 修复 / 修改 / 完善 / 删除 / 重构操作后,必须立即写更新日志到 `.draftgo/changelog.md`,格式:`- [HH:MM] [操作类型] 描述`。这是强制步骤,不得跳过。
24
+ > **【真实可用默认原则】**
25
+ > 除非用户明确要求“静态 / 纯页面 / demo / mock / 假数据 / 伪功能 / 先看效果”,任何开发任务都优先考虑真实落地闭环、高可用。禁止用前端假数据、静态卡片、无效按钮或伪交互充当功能完成。若平台能力、外部依赖或通用动态 DB 都无法支撑该功能,不要继续编写伪功能;向用户说明阻塞原因,并在确有复用价值时写入 `.draftgo/lessons/`。
26
+ >
27
+ > **【更新日志记录】**
28
+ > changelog 用于让后续 Agent 接手;影响可见功能、跨资源、已 push、已发布或用户明确要求记录时写入 `.draftgo/changelog.md`,格式:`- [HH:MM] [操作类型] 描述`。纯小修、探索、未形成有效改动时可跳过。
23
29
  >
24
- > **【任务标记硬性步骤】**
25
- > 只有标准功能 / 高风险任务需要 Task 文档。走完计划门后,每完成一个任务必须立即更新 `.draftgo/Task/<file>.md` 中的 ⬜ → ✅ 标记和证据摘要。小修和轻功能无需创建 Task。
30
+ > **【任务标记】**
31
+ > 只有已创建 Task 文档的任务需要维护标记。跨资源、多页面协作、并行开发或高风险任务完成一个阶段后,更新 `.draftgo/Task/<file>.md` 中的 ⬜ → ✅ 标记和证据摘要;小修、轻功能和普通标准功能可用内部短计划,不创建 Task。
26
32
  >
27
33
  > **【双端覆盖提醒】**
28
- > 开发任何业务功能时,必须从完整用户路径链路思考:用户从官网 / 导航 / 首页入口进入,点击到目标页面,完成浏览 / 搜索 / 提交 / 管理等操作,再获得真实反馈。必须主动判断是否需要对应管理侧页面(后台 CRUD / 配置 / 审核)和同一份真实数据,避免只做静态展示页。
34
+ > 开发业务功能时,优先从完整用户路径链路思考:用户从官网 / 导航 / 首页入口进入,点击到目标页面,完成浏览 / 搜索 / 提交 / 管理等操作,再获得真实反馈。围绕真实落地闭环、高可用判断是否需要对应管理侧页面(后台 CRUD / 配置 / 审核)和同一份真实数据,避免只做静态展示页。
29
35
  >
30
36
  > **【页面绑定提醒】**
31
37
  > 新增页面后必须处理入口绑定:导航栏、首页模块、后台菜单、相关页面按钮至少一处可点击进入;若用户明确要求隐藏页 / 草稿页,才可不绑定,但必须说明原因。只创建页面文件、不能从正常路径进入,不算完成。
38
+ >
39
+ > **【CLI 辅助工具】**
40
+ > 资源关系不清、进入陌生项目、多页面任务或用户描述模糊时,优先运行 `draftgo map` 快速读取页面 / 导航 / DB / 脚本 / AIHub / 外部 API / 文档 / 系统配置资源地图;目标文件清楚的小修可跳过。涉及页面、导航、DB 或脚本改动时,可用 `draftgo check` 辅助发现未绑定入口、缺文件、重复路由和疑似 mock 风险。验证优先用文件回读、静态检查、与改动匹配的轻量证据和必要的 push 输出闭环。
32
41
 
33
42
  ## 命令路由
34
43
 
@@ -40,7 +49,9 @@ version: 1.0.0
40
49
  | 从云端拉取 / 刷新本地数据 / `/draftgo pull` | 调用 `/draftgo pull` Skill |
41
50
  | 推送本地修改到云端 / `/draftgo push` | 调用 `/draftgo push` Skill |
42
51
  | 构建 / 查看 / 更新系统 Story / `/draftgo story` | 调用 `{{SKILL_DIR}}/story/SKILL.md` |
43
- | 开发 / 修改 / 新建 / 修复 / 重构页面、导航、API、自定义脚本、AIHub 资产 | **先读 `{{SKILL_DIR}}/rules/dev-workflow.md` 做小修 / 轻功能 / 标准功能 / 高风险分级**。小修优先只读目标资源和最小必要规则;前端任务按需读 `{{SKILL_DIR}}/rules/frontend.md`;页面静默失效或脚本复杂时读 `{{SKILL_DIR}}/rules/debugging-syntax.md`;标准功能 / 高风险任务计划门结束后按需读 `{{SKILL_DIR}}/rules/parallel.md` 判断是否启用并行分发。完成后必须写 changelog、check、push;有 Task 文档时同步打 ✅。 |
52
+ | 查看项目资源地图 / `draftgo map` | 直接运行 CLI,快速读取本地页面、导航、DB、脚本、AIHub、外部 API、文档、系统配置、入口引用 |
53
+ | 闭环体检 / `draftgo check` | 直接运行 CLI,检查本地 route、入口绑定、文件存在性和疑似 mock 风险 |
54
+ | 开发 / 修改 / 新建 / 修复 / 重构页面、导航、API、自定义脚本、AIHub 资产 | **先读 `{{SKILL_DIR}}/rules/dev-workflow.md` 做小修 / 轻功能 / 标准功能 / 高风险分级**。小修优先只读目标资源和最小必要规则;前端任务按需读 `{{SKILL_DIR}}/rules/frontend.md`;页面静默失效或脚本复杂时读 `{{SKILL_DIR}}/rules/debugging-syntax.md`;准备并行、多资源冲突或高风险计划时按需读 `{{SKILL_DIR}}/rules/parallel.md`。收尾按影响选择 changelog / check / push;有 Task 文档时同步打 ✅。 |
44
55
 
45
56
  ## 开发前置检查
46
57
 
@@ -55,21 +66,28 @@ version: 1.0.0
55
66
  开发规范读取顺序(按任务规模渐进读取):
56
67
  1. `{{SKILL_DIR}}/rules/dev-workflow.md` — 开发分级与执行流程(小修 / 轻功能 / 标准功能 / 高风险)
57
68
  2. `{{SKILL_DIR}}/rules/frontend.md` — 前端技术规范(前端任务按需读取;小修可先只读相关段落)
58
- 3. `{{SKILL_DIR}}/rules/data-table.md` — 数据列表/表格 UIUX 参考(涉及列表类页面且需要设计判断时读取)
59
- 4. `{{SKILL_DIR}}/rules/debugging-syntax.md` — 排错指南(页面静默失效、脚本复杂或 console 报错时读取)
60
- 5. `{{SKILL_DIR}}/rules/parallel.md` — 并行开发协议(标准功能 / 高风险且任务数足够、资源互不冲突时读取)
69
+ 3. `{{SKILL_DIR}}/rules/debugging-syntax.md` — 排错指南(页面静默失效、脚本复杂或 console 报错时读取)
70
+ 4. `{{SKILL_DIR}}/rules/parallel.md` — 并行开发协议(标准功能 / 高风险且任务数足够、资源互不冲突时读取)
61
71
 
62
72
  ## 架构认知(必读)
63
73
 
64
74
  **DraftGo 不是传统 SPA,是"数据库驱动的页面资产运行时":**
65
- - 壳层(Vue 3 + Vite)只负责 runtime 编排,源码在 `frontend/src/core/`
75
+ - 壳层(React + Vite)负责编译后的平台前端、runtime 编排和管理界面,源码在 `frontend/src/`
66
76
  - 业务页面 HTML 存在数据库 `page.value.html`,运行在 `iframe.srcdoc`
67
77
  - 导航栏 HTML 存在数据库 `navigation.html`,由壳层按需加载
68
78
  - 页面与壳层通过 `window.parent.App` API 通信
69
79
 
70
80
  **技术栈:**
71
81
  - 后端:FastAPI 0.115.0 + SQLAlchemy 2.0.36 + Pydantic 2.10.0 + MySQL + Redis
72
- - 前端:Vue 3 + Vite + Tailwind CSS(本地 `/assets/tailwindcss.js`)
82
+ - 前端:React + Vite + shadcn/ui + Tailwind CSS(shadcn/Tailwind 已作为平台前端基线引入)
83
+ - 数据库业务页面:仍可使用壳层注入的本地 `/assets/tailwindcss.js`、FontAwesome、GSAP 等静态资源,不需要进入 React 编译链
84
+
85
+ **dg-* = shadcn(硬性认知):**
86
+ - 在 DraftGo 中,看到 `dg-*` 必须理解为 **shadcn/ui 的 DraftGo HTML 协议表达**。
87
+ - `dg-*` 不是自研 UI 组件库,不是 daisyUI / Bootstrap / Ant Design / Element Plus 的别名,也不是任意相似样式的统称。
88
+ - 平台壳层管理界面使用 React/TSX 版 shadcn 组件;数据库页面不能直接写 TSX,因此用 `dg-button`、`dg-card`、`dg-form`、`dg-table` 等 HTML 标签表达同一套 shadcn 组件能力。
89
+ - 如果 shadcn 有对应组件,DraftGo 必须提供对应 `dg-*` 标签;当前 runtime 已按映射表提供基础协议覆盖,后续 shadcn 新组件也必须追加对应 `dg-*`。
90
+ - AI 开发页面时,`dg-*` 只代表可用的 DraftGo HTML 组件协议,不代表视觉风格指令;需要按钮、卡片、表单、表格、弹窗、Tabs、Dropdown、Sheet、Tooltip、Toast、Skeleton 等协议能力时使用对应 `dg-*`。
73
91
 
74
92
  ## 连接信息
75
93
 
@@ -103,6 +121,9 @@ version: 1.0.0
103
121
  - `App.callApi(code, options?)` — 调用已注册的外部 API(Promise→`{status_code, headers, body, duration_ms, error}`)
104
122
  - `App.listApis()` — 列出当前用户可调用的外部 API(含 code / name / method / 各 JSON Schema)
105
123
  - 详见 `{{SKILL_DIR}}/rules/frontend.md` App API 章节
124
+ - ❌ 禁止在业务页面内重复实现全局客服、全局反馈、全局浮窗、统计脚本等站点级能力 → ✅ 使用页面管理「全局」分类维护 `frontend_global_*` 固定槽位
125
+ - 全局层存储在 `sys_config.category=frontend_global`,不是 page 资产,不允许新增槽位
126
+ - Toast 可通过全局项二开样式/位置/默认时长,但页面调用仍使用 `App.toast()` / `App.showSuccess()` 等稳定 API
106
127
  - ⚠️ 尽量避免硬编码颜色 → 优先用 `var(--dg-accent)` 等 token 以适配主题切换
107
128
 
108
129
  ## 主题切换适配
@@ -113,12 +134,14 @@ DraftGo 支持多套配色方案 + 亮暗模式,通过 CSS 变量实现。
113
134
  | 概念 | 存储键 | 取值 |
114
135
  |---|---|---|
115
136
  | 显示模式 | `dg_theme` | `'light'` \| `'dark'` \| `'system'` |
116
- | 配色方案 | `dg_color_scheme` | `'dark-gray-white'` \| `'deep-blue-white'` \| `'orange-white'` \| `'custom'` |
137
+ | 配色方案 | `dg_color_scheme` | `'dark-gray-white'` \| `'deep-blue-white'` \| `'warm-retro'` \| `'mint-blue'` \| `'pine-green'` \| `'custom'` |
117
138
 
118
139
  **内置配色方案:**
119
- - `dark-gray-white` 深灰白(默认)— 冷调深灰,干净利落
120
- - `deep-blue-white` 深蓝白 — 专业稳重,企业风
121
- - `orange-white` 橙白 — 温暖醒目,创意风
140
+ - `dark-gray-white`
141
+ - `deep-blue-white`
142
+ - `warm-retro`
143
+ - `mint-blue`
144
+ - `pine-green`
122
145
 
123
146
  **颜色 Token(优先使用):**
124
147
  - `--dg-bg-base` / `--dg-bg-page` / `--dg-bg-surface` — 背景层级
@@ -179,7 +202,7 @@ App.setColorScheme('custom', customVarsObject); // 应用自定义配色
179
202
  | `navigation` | `id`, `code`, `name`, `html`, `order`, `status` | html 字段存导航栏完整 HTML |
180
203
  | `user` | `id`, `username`, `email`, `role_id`, `status` | role_id 关联 role 表 |
181
204
  | `role` | `id`, `code`, `name`, `permissions` (JSON) | admin 角色拥有所有权限 |
182
- | `db_meta` | `id`, `type`, `label`, `describe`, `schema` (JSON Schema: `{type:"object", properties:{字段名:{type,title,required,searchable,...}}}`), `permission` (JSON: `{public,login,admin,owner_field,roles}`,每项 `{read,create,update,delete}` 取值 `none/owner/all`,public 仅 `none/all`), `schema_validation` (0/1), `extra` (JSON) | 动态表结构定义 |
205
+ | `db_meta` | `id`, `type`, `label`, `describe`, `schema` (JSON Schema: `{type:"object", properties:{字段名:{type,title,required,searchable,...}}}`;`searchable` 取 `false`/`"exact"`/`"fuzzy"`/`"range"`/`"contains"`,旧布尔 `true` 按类型推断), `permission` (JSON: `{public,login,admin,owner_field,roles}`,每项 `{read,create,update,delete}` 取值 `none/owner/all`,public 仅 `none/all`), `schema_validation` (0/1), `extra` (JSON) | 动态表结构定义 |
183
206
  | `db` | `id`, `type`, `data` (JSON), `userid`, `status`, `created_at`, `updated_at` | 动态数据存储(含自动时间戳) |
184
207
  | `aihub` | `id`, `name`, `type`, `config` (JSON), `status` | AI 服务配置 |
185
208
  | `sys_config` | `config_key`, `config_value`, `value_type`, `category`, `is_sensitive` | 系统配置 KV |
@@ -191,28 +214,29 @@ App.setColorScheme('custom', customVarsObject); // 应用自定义配色
191
214
  |---|---|
192
215
  | 认证 | POST /api/auth/login, /register, /logout, /refresh, /forgot-password, /reset-password |
193
216
  | 微信认证 | POST /api/auth/wechat/mp/oauth, /wechat/mini/login, /wechat/mp/qr/create · GET /wechat/mp/qr/poll |
194
- | 用户 | GET/PUT /api/users/me · GET/POST /api/users · GET/PUT/DELETE /users/{id} · POST /users/{id}/ban, /unban |
217
+ | 用户 | GET/PUT /api/users/me · PUT /users/me/password · POST /users/me/contact-verifications, /confirm · POST /users/me/password/send-code/{channel}, /reset-by-phone, /reset-by-email · GET/POST /api/users · GET/PUT/DELETE /users/{id} · POST /users/{id}/ban, /unban |
195
218
  | 角色 | GET/POST /api/roles · GET/PUT/DELETE /roles/{id} · POST /roles/{id}/assign/{uid} · DELETE /roles/{id}/revoke/{uid} |
196
- | 页面 | GET/POST /api/pages/ · GET/PUT/DELETE /pages/{id} · POST /pages/{id}/reset-system · GET /pages/by-route · GET /pages/public/{route} |
219
+ | 页面 | GET/POST /api/pages/ · GET/PUT/DELETE /pages/{id} · POST /pages/{id}/reset-system · GET /pages/trash · DELETE /pages/{id}/trash · GET /pages/by-route · GET /pages/public/{route} · POST /pages/check-permissions · GET /pages/{id}/versions · GET/DELETE /pages/{id}/versions/{vid} · POST /versions/{vid}/restore, /star · PATCH /versions/{vid}/note · GET /versions/{v1}/diff/{v2} · POST /versions/batch-delete |
197
220
  | 导航栏 | GET/POST /api/navigations · GET /navigations/{code} · PUT/DELETE /navigations/{id} · POST /navigations/{id}/reset-system |
198
- | 系统配置 | GET /api/system/config · GET /system/category/{category} · GET/PUT/DELETE /system/{key} · POST /system/ · POST /system/config/ensure |
199
- | 备份恢复(基础) | GET/POST /api/system/backup · POST /system/restore · POST /system/reset · POST /system/backup/selective · POST /system/restore/selective?mode=replace\|merge\|append · GET /system/backup/logs |
200
- | 备份恢复(增强) | GET /system/backup/package, POST /system/backup/selective/package(.dgbak)· POST /system/restore/package, /restore/package/inspect(上传 .dgbak)· POST /system/restore/inspect, /restore/dry-run(只读预览/预演)· GET /system/storage/health, POST /system/storage/cleanup-orphans(存储健康)· POST /system/backup/logs/{id}/restore, /undo(回溯/撤销) |
221
+ | 系统配置 | GET /api/system/config · GET /system/category/{category} · GET/PUT/DELETE /system/{key} · POST /system/ · POST /system/config/ensure · GET/PATCH /system/config/sensitive-fields |
222
+ | 备份恢复(基础) | GET/POST /api/system/backup · POST /system/restore · POST /system/reset · POST /system/backup/selective · POST /system/restore/selective?mode=replace\|merge\|append · POST /system/restore/selective/file?mode=replace\|merge\|append(上传 JSON 文件恢复)· GET /system/backup/logs |
223
+ | 备份恢复(增强) | GET /system/backup/package, POST /system/backup/selective/package(.dgbak)· POST /system/restore/package, /restore/package/inspect(上传 .dgbak)· POST /system/restore/inspect, /restore/dry-run(JSON body 只读预览/预演)· POST /system/restore/inspect/file, /restore/dry-run/file(上传 JSON 文件只读预览/预演)· GET /system/storage/health, POST /system/storage/cleanup-orphans(存储健康)· POST /system/backup/logs/{id}/restore, /undo(回溯/撤销) |
201
224
  | ⚠️ 二次密码 | restore / reset / undo / cleanup-orphans / package restore 都需要先 POST `/auth/reauth { password, scope }` 拿一次性 `confirm_token`,在请求头加 `X-Confirm-Token: <token>` 才能调用。SAT 调用方自动豁免。 |
202
225
  | 通知公告 | GET/POST /api/notices · GET/PUT/DELETE /notices/{id} |
203
226
  | 反馈 | GET/POST /api/feedback · GET /feedback/updates · GET/PUT/DELETE /feedback/{id} · DELETE /feedback/batch |
204
227
  | 日志 | GET /api/logs · GET /logs/{id} |
205
- | 智能体 | GET /api/agents · GET /agents/logs · POST /agents/{id}/chat · POST /agents/{id}/images · POST /agents/{id}/preview-chat |
206
- | 动态DB | GET/POST /api/db/{type}POST 支持对象或数组)· PATCH /db/{type}/batch · GET/PUT/DELETE /db/{type}/{id} |
207
- | DB Meta | GET/POST /api/db-meta(POST 支持对象或数组)· PATCH /db-meta/batch · GET /db-meta/{type} · PUT/DELETE /db-meta/{id} |
228
+ | 智能体 | GET /api/agents · GET/DELETE /agents/logs · GET /agents/{id}/selectable-models · POST /agents/{id}/chat · POST /agents/{id}/images · POST /agents/{id}/preview-chat |
229
+ | 动态DB | GET /api/db/{type}(支持 `filters`/`order_by`/`order` 结构化检索)· POST /db/{type}(对象或数组)· PATCH /db/{type}/batch · GET/PUT/DELETE /db/{type}/{id} |
230
+ | DB Meta | GET/POST /api/db-meta(POST 支持对象或数组)· PATCH /db-meta/batch · GET /db-meta/{type} · POST /db-meta/{type}/reconcile-fields · POST /db-meta/reconcile-orphans · PUT/DELETE /db-meta/{id} |
208
231
  | ⚠️ DB Meta 查询 | GET /db-meta/{type} 用 **type**(如 `patient_profile`),不是 id。用 id 查会返回 "元数据不存在"。PUT/DELETE 才用 id。 |
209
- | AIHub | GET/POST /api/aihub(POST 支持对象或数组)· PATCH /aihub/batch · GET/PUT/DELETE /aihub/{id} · PATCH /aihub/{id}/basic · POST /aihub/{id}/sync · POST /aihub/{id}/test-image-generation · POST /aihub/discover · POST /aihub/mcp/discover-tools · GET /aihub/types |
232
+ | AIHub | GET/POST /api/aihub(POST 支持对象或数组)· PATCH /aihub/batch · GET/PUT/DELETE /aihub/{id} · PATCH /aihub/{id}/basic · POST /aihub/{id}/sync · POST /aihub/{id}/test-image-generation, /test-response-format · POST /aihub/discover · POST /aihub/mcp/discover-tools · GET /aihub/types |
210
233
  | AI推理 | GET /api/v1/models · POST /v1/chat/completions · POST /api/images/generation |
211
234
  | 外部 API(页面调用端) | GET /api/external-apis/available · POST /api/external-apis/call/{code}<br>页面里**优先使用 `App.callApi(code, options)`**,不要直连这两个端点 |
212
235
  | 外部 API(管理端,需 admin) | GET/POST /api/external-apis · GET/PUT/DELETE /external-apis/{id} · PATCH /external-apis/{id}/status · POST /external-apis/{id}/test · GET /external-apis/tags · GET /external-apis/logs · POST /external-apis/logs/cleanup?retention_days=N |
213
236
  | 文件上传 | POST /api/upload |
214
237
  | 通知测试 | POST /api/system/notifications/test-email, /test-sms · GET /system/notifications/logs |
215
238
  | 自定义服务 | POST /api/scripts · GET /api/scripts · GET/PUT/DELETE /scripts/{id} · POST /scripts/{id}/enable, /disable, /execute · GET /scripts/{id}/versions · POST /scripts/{id}/versions/{vid}/restore · GET /scripts/{id}/executions · ANY /api/x/{slug}/{path} |
239
+ | 文档中心 | GET/POST /api/docs/categories · GET /docs/articles, /docs/articles/{key}, /docs/search · GET /docs/admin/articles, /docs/admin/stats · POST /docs/articles · PUT/DELETE /docs/articles/{id} · PUT/DELETE /docs/categories/{id} |
216
240
 
217
241
  ### 统一响应信封
218
242
 
@@ -249,7 +273,7 @@ POST /api/pages/
249
273
  Body: { "title": "新页面", "route": "/new", "permission": {"default": "public"}, "value": {"html": "<html>...</html>"} }
250
274
  说明:permission 是对象,不是字符串。`default` 取值 `public`(任何人) / `login`(登录用户) / `admin`(管理员);可选 `roles: {角色code: "public|login|admin"}` 做角色级覆盖。
251
275
  保留路由(不可被业务页面占用):`/setup`(首次部署引导)。创建/更新时若 route 重复,后端返回 400 "route 已存在"。
252
- 系统页:由后端从 `backend/init/pages/` 初始化,`tag == "系统"` 标识;改动后须 `POST /api/pages/{id}/reset-system` 才会从源文件重新加载。
276
+ 系统页:由后端从 `backend/init/pages/` 初始化,`tag == "系统"` 标识;不会在容器重启时自动对比或更新。官方版本提示页面模板有更新时,管理员需要在页面管理点击「重置系统页面」,或调用 `POST /api/pages/{id}/reset-system` 从当前安装包内置模板覆盖重置。
253
277
  Response: { "code": 200, "data": { "id": 123, "title": "新页面", "route": "/new", ... }, "message": "success" }
254
278
  ```
255
279
 
@@ -273,10 +297,13 @@ Body: { "data": {"name": "张三", "age": 30} }
273
297
  Response: { "code": 200, "data": [{ "id": 456 }], "message": "success" }
274
298
  ```
275
299
 
276
- **查询动态数据(带分页):**
300
+ **查询动态数据(分页 + 结构化检索):**
277
301
  ```
278
302
  GET /api/db/patient?page=1&page_size=20
303
+ GET /api/db/patient?filters=name:like:张&filters=age:gte:18&order_by=age&order=desc
279
304
  Response: { "code": 200, "data": { "items": [...], "total": 100, "page": 1, "page_size": 20 }, "message": "success" }
305
+ 说明:filters 每项为 "字段:操作符:值",可多次传入(AND)。操作符 eq/like/gte/lte/gt/lt/in/contains,省略默认 like。
306
+ 字段须在 db_meta schema 标 searchable,且操作符匹配其检索模式(exact/fuzzy/range/contains),否则 400。
280
307
  ```
281
308
 
282
309
  **批量更新动态数据:**
@@ -412,7 +439,7 @@ const visitId = query.visitId; // "7"
412
439
  | 问题 | 排查步骤 |
413
440
  |---|---|
414
441
  | 同步失败 | 1. 检查 `.draftgo/config.json` token 是否有效<br>2. 检查服务器连接<br>3. 查看 `.draftgo/changelog.md` 最近操作记录 |
415
- | 页面加载空白 | 1. 检查 `permission` 字段与当前用户角色是否匹配<br>2. 检查路由是否正确(`/api/pages/by-route?route=/xxx`)<br>3. 浏览器控制台查看 JS 错误 |
442
+ | 页面加载空白 | 1. 检查 `permission` 字段与当前用户角色是否匹配<br>2. 检查路由是否正确(`/api/pages/by-route?route=/xxx`)<br>3. 检查页面脚本错误、运行日志或最近改动 |
416
443
  | API 返回 401 | Token 过期,前端会自动用 refresh token 刷新,无需手动处理 |
417
444
  | API 返回 403 | 权限不足,检查当前用户角色是否有对应权限 |
418
445
  | DB Meta GET 返回"元数据不存在" | 你用了 id,应该用 **type**(如 `/api/db-meta/patient_profile`)。PUT/DELETE 才用 id。 |
@@ -424,7 +451,7 @@ const visitId = query.visitId; // "7"
424
451
 
425
452
  ## 更新日志规范
426
453
 
427
- 每次对系统进行开发/修复/修改/完善/删除/重构等操作后,**必须**写入 `.draftgo/changelog.md`。
454
+ 更新日志用于让后续 Agent 和开发者快速接手。影响可见功能、跨资源、已 push、已发布或用户明确要求记录时,写入 `.draftgo/changelog.md`;纯小修、探索、未形成有效改动时可跳过。
428
455
 
429
456
  ### 规则
430
457
 
@@ -450,7 +477,7 @@ const visitId = query.visitId; // "7"
450
477
 
451
478
  ### 操作流程
452
479
 
453
- 1. 完成任何系统操作后,获取当前日期和时间
480
+ 1. 判断本次改动是否需要记录
454
481
  2. 检查 `.draftgo/changelog.md` 是否存在
455
482
  3. 查找当天日期标题(`## YYYY-MM-DD`)
456
483
  4. 存在则在该日期段落末尾追加新条目;不存在则在文件顶部新增日期段落
@@ -507,18 +534,18 @@ xxx
507
534
 
508
535
  ---
509
536
 
510
- ## 自动推送规范
537
+ ## 推送规范
511
538
 
512
- > **每次修改页面或导航栏 HTML 后,必须立即自动推送到服务器。这是强制步骤,不得跳过,不得等用户提醒。**
539
+ > 推送用于把本地资源同步到云端。用户明确要求推送、任务目标包含云端生效、资源已形成可交付结果、或修改涉及多资源联动时执行;探索性修改、未完成草稿和目标明确的小修可先保留本地。
513
540
 
514
541
  ### 新建资源(无 id 自动创建)
515
542
 
516
543
  push 脚本按 index.json 条目**有无 `id`** 决定走更新还是创建:
517
544
 
518
545
  - **有 id** → `PUT` 更新(PUT 404 时自动转创建,兼容删了重建 / 跨环境)
519
- - **无 id** → `POST` 创建,成功后**自动回写新 id 到 index.json**,并把内容文件重命名为 pull 约定的 `{prefix}_{id}_{slug}.{ext}`
546
+ - **无 id** → `POST` 创建,成功后**自动回写新 id 到 index.json**;有独立内容文件的资源会同步重命名为 pull 约定的 `{prefix}_{id}_{slug}.{ext}`
520
547
 
521
- 支持创建的类型:**pages / nav / db_meta / docs / custom_scripts**。
548
+ 支持创建的类型:**pages / nav / db_meta / aihub / external_apis / docs / doc_categories / custom_scripts**。
522
549
 
523
550
  **新建页面 = 写 html 文件 + 在 `pages/index.json` 加一条不带 id 的记录 + `push pages`**。脚本回写 id 后即与普通更新无异,无需手动调 `POST /api/pages/`、无需手动维护 id。各类型创建必填字段见 push skill(`{{SKILL_DIR}}/push/SKILL.md`「新建资源」节)。
524
551
 
@@ -571,14 +598,14 @@ roles 和 users 涉及权限与账号安全,虽然脚本已支持,**仍必
571
598
  2. **等待用户明确确认**后,再运行 `draftgo_push.py roles` / `users`
572
599
  3. 用户拒绝则不推送,仅保留本地修改
573
600
 
574
- ### 完整操作顺序(每次开发任务结束时)
601
+ ### 收尾操作顺序(按影响选择)
575
602
 
576
- 1. 写更新日志(静默,强制)
603
+ 1. 判断是否需要写更新日志
577
604
  2. 写经验记录(有阻碍或新经验时写入 lessons/,没有则跳过)
578
- 3. 运行推送脚本 / 调用 API(必须执行,roles/users 需二次确认)
579
- 4. 告知用户推送结果
605
+ 3. 需要云端生效时运行推送脚本 / 调用 APIroles/users 需二次确认)
606
+ 4. 告知用户本次证据与是否已推送
580
607
 
581
- > **Lessons 提醒机制**:当 `.draftgo/config.json` 中 `lessons_on_push` 为 `true` 时,push 脚本执行完毕会输出一段回顾提醒。此提醒仅为兜底安全网——无论是否看到提醒,步骤 2 的 lessons 回顾义务始终生效。
608
+ > **Lessons 提醒机制**:当 `.draftgo/config.json` 中 `lessons_on_push` 为 `true` 时,push 脚本执行完毕会输出一段回顾提醒。此提醒仅为兜底安全网;是否写 lessons 仍按“有阻碍或新经验”判断。
582
609
 
583
610
  ---
584
611
 
@@ -628,7 +655,7 @@ def cleanup(sdk, ctx):
628
655
 
629
656
  ```python
630
657
  # ❌ 这些写法都会报错
631
- from draftgo import route # 'draftgo' 不在白名单
658
+ from draftgo import route # 没有 draftgo 这个内置模块
632
659
  from sdk import route # 可以但多余,直接用就行
633
660
  import sdk # 可以但多余
634
661
 
@@ -656,24 +683,22 @@ def handler(request): # ❌ 签名必须是 (sdk, ctx)
656
683
  - Event: `{event, payload, timestamp}`
657
684
  - Scheduled: `{trigger_type, trigger_name, payload, config}`
658
685
 
659
- ### 可用模块(白名单)
686
+ ### 可导入模块
660
687
 
661
- 只有以下模块可以 import
688
+ 自定义脚本只允许管理员创建和编辑,因此脚本引擎**不再做 import 白名单限制**。脚本可以导入当前后端运行环境中已经安装的 Python 模块;如果目标环境没有安装该依赖,脚本加载或执行时会报 `ImportError`。
662
689
 
663
- ```
664
- json, re, datetime, math, collections, itertools, functools,
665
- typing, dataclasses, enum, uuid, hashlib, base64, urllib.parse
666
- ```
690
+ 建议优先使用标准库和 DraftGo SDK。需要新增第三方依赖时,要确认部署环境的 `requirements.txt` / 镜像中已经包含该包,避免本地可用、线上不可用。
667
691
 
668
- **不能用的 + 替代方案:**
692
+ **推荐做法:**
669
693
 
670
- | 需求 | 禁止 | ✅ 替代 |
671
- |------|---------|---------|
672
- | HTTP 请求 | `import requests` | `sdk.external_api.get/post/put/delete(url, headers, timeout)` |
673
- | 缓存 | `import redis` | `sdk.cache.get/set/delete(key)` |
674
- | 日志 | `print()` / `import logging` | `sdk.log.info/warn/error/debug(msg)` |
675
- | 当前时间 | `import time` | `sdk.now()` `import datetime` |
676
- | 文件操作 | `open()` / `import os` | 不支持(设计如此) |
694
+ | 需求 | 推荐 |
695
+ |------|------|
696
+ | 调外部 HTTP | 优先 `sdk.external_api.get/post/put/delete(url, headers, timeout)`,也可在依赖可用时自行导入 HTTP 客户端 |
697
+ | 缓存 | 优先 `sdk.cache.get/set/delete(key)`,自动按脚本隔离 key |
698
+ | 日志 | `sdk.log.info/warn/error/debug(msg)`,会进入执行记录 |
699
+ | 当前时间 | `sdk.now()` 获取平台时区时间;也可导入 `datetime` / `time` |
700
+ | 系统配置和密钥 | `sdk.config.get(key)`,不要把密钥硬编码在脚本里 |
701
+ | 文件和系统命令 | 尽量避免;脚本与主进程同进程运行,误操作会影响后端服务 |
677
702
 
678
703
  ### SDK 速查表(与基座源码对齐)
679
704
 
@@ -689,18 +714,20 @@ typing, dataclasses, enum, uuid, hashlib, base64, urllib.parse
689
714
  | `update` | `(type: str, id: int, data: dict) -> dict` | 部分更新,`data` 传**裸业务字典**,SDK 内部自动包装 `{"data": data}` 提交,**禁止**手动再包一层 |
690
715
  | `update_many` | `(type: str, items: list[dict]) -> list[dict]` | 批量更新;每项传 `{id, data}`,或 `{id, 字段...}` 让 SDK 自动包装为 `data` |
691
716
  | `delete` | `(type: str, id: int) -> bool` | 失败返回 `False`,不抛异常 |
692
- | `query` | `(type: str, filters: dict = None, page: int = 1, page_size: int = 20) -> dict` | 返回 `{items, total, page, page_size}` |
717
+ | `query` | `(type: str, filters: dict = None, page: int = 1, page_size: int = 20, order_by: str = None, order: str = "desc") -> dict` | 返回 `{items, total, page, page_size}` |
693
718
 
694
- **`filters` 语义(重要)**:内部转换为搜索词喂给 `DBService.get_db_list` `search` 参数,是**字段包含式搜索**,**不是**结构化 WHERE。**不支持** `>=` `<` `in` `or` `between` 等操作符;多字段是 AND 关系。需要复杂条件请在脚本里二次过滤。
719
+ **`filters` 语义(重要)**:结构化条件查询,直接对记录的 `data` JSON 字段做 WHERE,**支持** `eq` / `like` / `gte` / `lte` / `gt` / `lt` / `in` / `contains`。多字段是 AND 关系。字段必须在 db_meta schema 里标 `searchable`,且操作符要匹配字段的检索模式(exact/fuzzy/range/contains),否则后端报 400。
695
720
 
696
- **filters 类型处理规则**:
697
- | 值类型 | 行为 | 示例 |
721
+ **filters 写法**:
722
+ | 形式 | 含义 | 示例 |
698
723
  |--------|------|------|
699
- | `str` | 原样传递 | `{"title": "AI"}` → 搜索 `title:AI` |
700
- | `int` / `float` | 转字符串 | `{"score": 90}` 搜索 `score:90` |
701
- | `bool` | 转小写字符串 | `{"distributed": False}` → 搜索 `distributed:false` |
702
- | `list` | 逗号拼接 | `{"tags": ["a","b"]}` 搜索 `tags:a,b` |
703
- | `None` | **跳过**,不参与搜索 | `{"field": None}` → 该条件被忽略 |
724
+ | `{field: 值}` | 精确等于(eq) | `{"status": "paid"}` |
725
+ | `{field: {"op": 操作符, "value": 值}}` | 指定操作符 | `{"title": {"op": "like", "value": "公告"}}` |
726
+ | `{field: [...]}` | 命中其一(in) | `{"status": ["paid", "pending"]}` |
727
+ | `{field: bool}` | 精确等于(SDK `"true"`/`"false"`) | `{"done": True}` |
728
+ | `{field: None}` | **跳过**,不参与查询 | `{"field": None}` |
729
+
730
+ 范围/排序示例:`sdk.db.query("article", {"views": {"op": "gte", "value": 100}}, order_by="views", order="desc")`
704
731
 
705
732
  **上限**:`page_size` 内部 `min(page_size, 1000)`,超出静默截断。
706
733
 
@@ -957,10 +984,50 @@ def fetch_all(sdk, ctx):
957
984
  | `slug` | ✅ | URL 标识(route 模式的访问路径:`/api/x/{slug}/{path}`) |
958
985
  | `code` | ✅ | 完整 Python 代码 |
959
986
  | `mode` | ✅ | `event` / `route` / `scheduled` |
960
- | `triggers` | 否 | event 模式: `{"events": ["user.registered"]}`;scheduled 模式: `{"cron": "0 2 * * *"}` |
987
+ | `triggers` | 否 | 当前主要作为 event/route 的管理端元数据保留。event 模式可写 `{"events": ["user.registered"]}`,route 模式可写路径/方法;scheduled 模式不需要写 `triggers`,cron 只读取代码中的 `@scheduled(...)` 装饰器,后端会清空 scheduled trigger 元数据 |
961
988
  | `config` | 否 | `{"timeout": 30, "max_retries": 0}`;`timeout` 是**脚本整体超时**(默认 30s),与 `sdk.external_api` 的单次 HTTP timeout(默认 10s、硬上限 30s)是**两层独立机制**。多次外部调用串行场景务必上调到 120–300 |
962
989
  | `permission` | 否 | 仅 route 模式有效:`{"default": "public|login|admin", "roles": [...]}` |
963
990
 
991
+ **Route 配置推荐写法:**
992
+
993
+ ```json
994
+ {
995
+ "config": {
996
+ "timeout": 20,
997
+ "route_security": {
998
+ "auth_required": true,
999
+ "rate_limit_per_minute": 60,
1000
+ "burst_limit": 10,
1001
+ "max_body_size_kb": 256,
1002
+ "timeout_ms": 15000,
1003
+ "ip_allowlist": ["10.0.0.0/8"]
1004
+ }
1005
+ }
1006
+ }
1007
+ ```
1008
+
1009
+ 当前后端兼容把这些字段直接写在 `config` 顶层,但为了避免混乱,文档统一按 `config.route_security` 说明。
1010
+
1011
+ **Route 配置推荐写法:**
1012
+
1013
+ ```json
1014
+ {
1015
+ "config": {
1016
+ "timeout": 20,
1017
+ "route_security": {
1018
+ "auth_required": true,
1019
+ "rate_limit_per_minute": 60,
1020
+ "burst_limit": 10,
1021
+ "max_body_size_kb": 256,
1022
+ "timeout_ms": 15000,
1023
+ "ip_allowlist": ["10.0.0.0/8"]
1024
+ }
1025
+ }
1026
+ }
1027
+ ```
1028
+
1029
+ 当前后端兼容把这些字段直接写在 `config` 顶层,但为了避免混乱,文档统一按 `config.route_security` 说明。
1030
+
964
1031
  ### 权限配置(仅 route 模式)
965
1032
 
966
1033
  | 场景 | permission 值 |
@@ -984,17 +1051,48 @@ def fetch_all(sdk, ctx):
984
1051
  | `page.updated` | `{page_id, changes}` |
985
1052
  | `script.executed` | `{script_id, execution_id, status, duration_ms}` |
986
1053
 
987
- ### 访问路径
1054
+ ### 访问路径与调度来源
988
1055
 
989
1056
  Route 模式脚本通过 `/api/x/{slug}/{path}` 访问:
990
1057
  - slug 为 `order-api`,handler 为 `@route("GET /list")` → 访问 `GET /api/x/order-api/list`
991
1058
  - slug 为 `health-check`,handler 为 `@route("GET /health")` → 访问 `GET /api/x/health-check/health`
992
1059
 
1060
+ Scheduled 模式的 cron **写在代码装饰器里**:
1061
+ - `@scheduled("0 2 * * *")` → 每天凌晨 2 点执行
1062
+ - 当前引擎加载 scheduled handler 时直接读取装饰器里的 cron 表达式
1063
+ - scheduled 模式不需要触发器 cron 元数据;即使旧客户端传入也会被后端清空
1064
+
1065
+ Event 模式同理:
1066
+ - `@on("user.registered")` 才是真正注册事件的来源
1067
+ - `triggers.events` 建议与代码保持一致,作为管理端元数据保存
1068
+
1069
+ **Route 安全机制(必须考虑)**:
1070
+
1071
+ 所有 `/api/x/{slug}/{path}` 请求在进入脚本前会经过平台安全守卫:Route 总开关、HTTP method 白名单、IP 黑白名单、每分钟限流、10 秒突发限流、默认登录要求、请求体大小上限、执行超时上限。全局默认来自系统配置 `script_route_*`,单个脚本可在 `config.route_security` 覆盖。
1072
+
1073
+ 当前后端也兼容把 `auth_required`、`rate_limit_per_minute`、`burst_limit`、`max_body_size_kb`、`timeout_ms`、`ip_allowlist`、`ip_blocklist` 直接写在 `config` 顶层,但**推荐统一写在 `config.route_security` 下**:
1074
+
1075
+ ```json
1076
+ {
1077
+ "timeout": 20,
1078
+ "route_security": {
1079
+ "auth_required": true,
1080
+ "rate_limit_per_minute": 60,
1081
+ "burst_limit": 10,
1082
+ "max_body_size_kb": 256,
1083
+ "timeout_ms": 15000,
1084
+ "ip_allowlist": ["10.0.0.0/8"]
1085
+ }
1086
+ }
1087
+ ```
1088
+
1089
+ 公开 Route 接口不要关闭限流;涉及外部回调、支付、Webhook、AI 调用等高频/高成本接口时,必须显式设置请求体上限、限流和合理超时。
1090
+
993
1091
  ### 开发常见错误速查
994
1092
 
995
1093
  | 症状 | 原因 | 修复 |
996
1094
  |------|------|------|
997
- | 脚本状态 error: "xxx 不在允许的模块列表" | import 了白名单之外的模块 | 只用白名单模块,HTTP sdk.external_api |
1095
+ | 脚本状态 error: `ImportError` / `No module named ...` | 运行环境没有安装该依赖,或模块名写错 | 改用标准库 / SDK,或先把依赖加入后端运行环境 |
998
1096
  | 脚本状态 error: "代码语法错误" | Python 语法问题 | 本地用 `python -c "import ast; ast.parse(open('x.py').read())"` 验证 |
999
1097
  | 访问 /api/x/slug/path 返回 404 | slug 或 path 不匹配 | 确认 slug 和 @route 中的 path 拼写 |
1000
1098
  | 访问 /api/x/slug/path 返回 405 | HTTP 方法不匹配 | GET 请求但装饰器写了 POST |
@@ -1005,8 +1103,8 @@ Route 模式脚本通过 `/api/x/{slug}/{path}` 访问:
1005
1103
  | 拉取公开 HTTP 接口返回 403 | 注册表里配的 UA 不会被脚本复用 | 在脚本里显式 `headers={"User-Agent": "..."}` |
1006
1104
  | 多平台串行调用时脚本整体超时(但单次 HTTP 没报超时) | `config.timeout` 默认 30s,被先触发 | 提交脚本时把 `config.timeout` 调到 120–300 |
1007
1105
  | `AttributeError: 'DBModule' object has no attribute 'list'` | 文档老版本误写 `sdk.db.list` | 用 `sdk.db.query(type, filters, page, page_size)` |
1008
- | `sdk.db.query` 加了 `>=` / `in` 等条件没生效 | filters 是字段包含搜索,不是结构化 WHERE | 在脚本中拉回数据后二次过滤 |
1009
- | `sdk.db.query(filters={"done": True})` 返回 0 条 | boolean/list 类型需与索引格式对齐 | boolean `True`/`False`(SDK 内部转 `"true"`/`"false"`),list 用实际列表;不要传字符串 `"true"` |
1106
+ | `sdk.db.query` 加了 `gte` / `in` 等条件报 400 | 该字段未标 searchable,或操作符与字段的检索模式不匹配 | db_meta schema 给字段标对应 searchable 模式(range 才支持 gte/lte,exact/fuzzy 支持 eq/in) |
1107
+ | `sdk.db.query(filters={"done": True})` 返回 0 条 | 字段未标 searchable,或 boolean 字段未按 exact 模式配置 | 给字段标 `searchable`,boolean `True`/`False`(SDK 内部转 `"true"`/`"false"`) |
1010
1108
  | `sdk.db.update` 报 schema 校验失败或字段丢失 | 第三个参数 `data` 已被 SDK 自动包装,禁止手动再包 `{"data": {...}}` | 直接传裸字典:`sdk.db.update("type", id, {"field": val})` |
1011
1109
  | `sdk.db.update_many` 后部分数据没更新 | 批量更新是一次事务,任意一条失败会整批回滚 | 先确认每项都有 `id`,且 `{id, data}` 中的 `data` 满足 schema |
1012
1110
  | `sdk.external_api.*(timeout=120)` 仍然 30s 超时 | 单次 HTTP timeout 硬上限 30s | 改造业务逻辑,把单次请求拆短;或改用分批拉取 |
@@ -1125,6 +1223,91 @@ Body:
1125
1223
 
1126
1224
  聊天型 Agent 默认 `spec.mode="chat"`,调用 `POST /api/agents/{id}/chat`。
1127
1225
 
1226
+ ### Agent 用户自主选择模型
1227
+
1228
+ Agent 支持把“主模型 + 备用模型”作为用户可选模型白名单暴露给页面。开启方式是在 Agent 的 `data.spec.model_selection` 中写入配置:
1229
+
1230
+ ```json
1231
+ {
1232
+ "data": {
1233
+ "schema_version": "agent.v3",
1234
+ "spec": {
1235
+ "model": "gpt-4.1",
1236
+ "fallback_models": ["gpt-4.1-mini", "claude-3-5-sonnet"],
1237
+ "model_selection": {
1238
+ "user_selectable": true,
1239
+ "on_invalid": "ignore"
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ ```
1245
+
1246
+ 获取该 Agent 允许用户选择的模型列表:
1247
+
1248
+ ```
1249
+ GET /api/agents/{agent_id}/selectable-models
1250
+ ```
1251
+
1252
+ 返回:
1253
+
1254
+ ```json
1255
+ {
1256
+ "user_selectable": true,
1257
+ "models": ["gpt-4.1", "gpt-4.1-mini", "claude-3-5-sonnet"]
1258
+ }
1259
+ ```
1260
+
1261
+ 注意:
1262
+
1263
+ - `models` 是 Agent 白名单,由 `spec.model` + `spec.fallback_models` 去重派生,不是供应商全量模型列表。
1264
+ - 未开启 `user_selectable` 时返回 `{ "user_selectable": false, "models": [] }`。
1265
+ - `on_invalid="ignore"` 表示传入非白名单模型时回落到主模型;`reject` 表示拒绝请求。
1266
+ - 页面端优先使用 `DraftGoAI.getSelectableModels(agentId)`,再把用户选中的模型作为 `options.model` 传给 `DraftGoAI.chat` 或 `DraftGoAI.images`。
1267
+
1268
+ ### Agent 结构化 JSON 输出
1269
+
1270
+ Agent 需要稳定 JSON 返回时,不要只在提示词里口头要求;应配置 `data.spec.output_format`:
1271
+
1272
+ ```json
1273
+ {
1274
+ "data": {
1275
+ "schema_version": "agent.v3",
1276
+ "spec": {
1277
+ "model_id": 1,
1278
+ "model": "gpt-4.1",
1279
+ "output_format": {
1280
+ "mode": "json",
1281
+ "json": {
1282
+ "strategy": "auto",
1283
+ "schema_name": "agent_output",
1284
+ "schema": {
1285
+ "type": "object",
1286
+ "properties": {
1287
+ "answer": { "type": "string" },
1288
+ "confidence": { "type": "number" }
1289
+ },
1290
+ "required": ["answer"],
1291
+ "additionalProperties": false
1292
+ }
1293
+ }
1294
+ }
1295
+ }
1296
+ }
1297
+ }
1298
+ ```
1299
+
1300
+ 字段规则:
1301
+
1302
+ | 字段 | 说明 |
1303
+ |------|------|
1304
+ | `output_format.mode` | `native`(默认)或 `json` |
1305
+ | `output_format.json.strategy` | `auto` / `native` / `prompt`;推荐 `auto` |
1306
+ | `output_format.json.schema` | 可选 JSON Schema;有 schema 时优先尝试 `json_schema` |
1307
+ | `output_format.json.schema_name` | 传给 OpenAI 兼容 `response_format.json_schema.name` |
1308
+
1309
+ 模型资产可调用 `POST /api/aihub/{model_id}/test-response-format` 探测 `response_format` 支持情况,结果写回 `data.supports_response_format` 与 `data.supports_json_schema`。`strategy=auto` 会优先使用原生 `response_format`,遇到上游不支持时降级为提示词约束。流式 JSON 输出结束时会追加 `json.validation` SSE 事件,用于前端判断解析/Schema 校验是否通过。
1310
+
1128
1311
  ### 创建图片生成型 Agent
1129
1312
 
1130
1313
  ```
@@ -1140,7 +1323,7 @@ Body:
1140
1323
  "mode": "image_generation",
1141
1324
  "model_id": 1,
1142
1325
  "model": "gpt-image-1",
1143
- "system_prompt_template": "统一生成商业海报风格,画面干净。",
1326
+ "system_prompt_template": "统一生成商业海报,画面清晰。",
1144
1327
  "image_generation": {
1145
1328
  "n": 1,
1146
1329
  "size": "1024x1024",
@@ -1156,10 +1339,10 @@ Body:
1156
1339
 
1157
1340
  ```
1158
1341
  POST /api/agents/{agent_id}/images
1159
- Body: {"prompt": "生成一张夏季饮品海报", "size": "1024x1024"}
1342
+ Body: {"prompt": "生成一张夏季饮品海报", "size": "1024x1024", "model": "gpt-image-1"}
1160
1343
  ```
1161
1344
 
1162
- 页面里使用 `DraftGoAI.images(agentId, prompt, options)`;不要把图片生成请求发到 `/chat`。
1345
+ 页面里使用 `DraftGoAI.images(agentId, prompt, options)`;不要把图片生成请求发到 `/chat`。`options.model` 同样遵循 Agent 的用户选模型白名单。
1163
1346
 
1164
1347
  ### 给 Agent 绑定工具
1165
1348
 
@@ -18,7 +18,7 @@ allowed-tools: Bash(python:*), Read, Glob
18
18
  > - **有 id** → `PUT /api/{type}/{id}` 更新(PUT 404 时自动转为创建)
19
19
  > - **无 id** → `POST /api/{type}` 创建,成功后**自动回写新 id 到 index.json**,并把对应 .html/.md/代码文件**重命名为 pull 约定的 `{prefix}_{id}_{slug}.{ext}`**
20
20
 
21
- 支持创建的类型:**pages / nav / db_meta / docs / custom_scripts**(其余类型仍只更新)。
21
+ 支持创建的类型:**pages / nav / db_meta / aihub / external_apis / docs / doc_categories / custom_scripts**。
22
22
 
23
23
  ### 新建页面的标准流程
24
24
 
@@ -49,7 +49,10 @@ allowed-tools: Bash(python:*), Read, Glob
49
49
  | pages | `title`, `route` | route 不可重复 |
50
50
  | nav | `name`, `code` | 创建必填 code,更新时不发 |
51
51
  | db_meta | `type`, `label`, `schema` | 无 id 时按 type 创建 |
52
+ | aihub | `type`, `name`, `data` | 支持 model/prompt/agent/mcp/skill 等 AI 资产 |
53
+ | external_apis | `code`, `name`, `base_url` | code 不可包含 `/` 或空格;创建时不发送 status |
52
54
  | docs | `title` | 其余字段有默认值 |
55
+ | doc_categories | `name` | slug 可选;不填由后端生成/处理 |
53
56
  | custom_scripts | `name`, `slug`, `mode`, `triggers` | mode=route/event/scheduled;创建后默认覆盖代码,启停仍走 enable/disable |
54
57
 
55
58
  > **创建后必须以脚本回写的 index 为准**,不要手动猜 id。回写后建议 `git diff` 或重新读 index 确认 `id` 已落地。
@@ -85,7 +88,7 @@ allowed-tools: Bash(python:*), Read, Glob
85
88
  !python {{SKILL_SCRIPTS}}/draftgo_push.py aihub <aihub_id>
86
89
  ```
87
90
 
88
- 读取 `.draftgo/aihub/index.json`,按 `AIHubUpdate` schema 推送。
91
+ 读取 `.draftgo/aihub/index.json`,按 `AIHubUpdate` schema 推送;无 `id` 或 PUT 404 时会 `POST /api/aihub` 创建,成功后回写新 `id`。
89
92
 
90
93
  ## 推送外部 API("推送外部API" / "push external_apis")
91
94
 
@@ -94,7 +97,7 @@ allowed-tools: Bash(python:*), Read, Glob
94
97
  !python {{SKILL_SCRIPTS}}/draftgo_push.py external_apis <api_id>
95
98
  ```
96
99
 
97
- 读取 `.draftgo/external_apis/index.json`(已包含 init 时合并的 detail 字段),按 `ExternalAPIUpdate` schema 推送。
100
+ 读取 `.draftgo/external_apis/index.json`(已包含 init 时合并的 detail 字段),按 `ExternalAPIUpdate` schema 推送;无 `id` 或 PUT 404 时会 `POST /api/external-apis` 创建,成功后回写新 `id`。创建最少需要 `code`、`name`、`base_url`。
98
101
 
99
102
  ## 推送系统配置("推送系统配置" / "push system_config")
100
103
 
@@ -149,7 +152,7 @@ users 涉及账号安全,**强制要求人工确认**:
149
152
  !python {{SKILL_SCRIPTS}}/draftgo_push.py doc_categories <category_id>
150
153
  ```
151
154
 
152
- 读取 `.draftgo/doc_categories/index.json`,按 `CategoryUpdate` schema 推送。
155
+ 读取 `.draftgo/doc_categories/index.json`,按 `CategoryUpdate` schema 推送;无 `id` 或 PUT 404 时会 `POST /api/docs/categories` 创建,成功后回写新 `id`。
153
156
 
154
157
  ## 推送自定义脚本("推送自定义脚本" / "push custom_scripts")⚠️ 需二次确认
155
158