draftgo-cli 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/bin/draftgo.js +1 -1
- package/package.json +1 -1
- package/resources/skill/SKILL.md +385 -8
- package/resources/skill/init/SKILL.md +7 -4
- package/resources/skill/rules/frontend.md +19 -0
- package/resources/skill/scripts/draftgo_init.py +12 -10
- package/resources/skill/scripts/draftgo_sync.py +112 -12
- package/resources/skill/sync/SKILL.md +83 -14
- package/src/commands/doctor.js +16 -1
- package/src/commands/help.js +14 -8
- package/src/commands/update.js +68 -1
- package/src/index.js +1 -1
- package/src/updateCheck.js +58 -0
- package/resources/skill/CLAUDE.md +0 -239
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ draftgo init all # 所有支持的工具
|
|
|
35
35
|
| 命令 | 说明 |
|
|
36
36
|
|---|---|
|
|
37
37
|
| `draftgo init [target]...` | 安装 skill。不传 target 时自动识别;`all` 表示全部。 |
|
|
38
|
-
| `draftgo update [target]...` | 覆盖 skill 内容到 CLI 内置版本,同时刷新已安装的入口;保留本地 `config / iteration / bugs / pages`
|
|
38
|
+
| `draftgo update [target]...` | 覆盖 skill 内容到 CLI 内置版本,同时刷新已安装的入口;保留本地 `config / iteration / bugs / pages` 等运行时数据。**若 npm 上存在更新的 CLI 版本,会先自动升级 CLI 再重跑自己**。 |
|
|
39
39
|
| `draftgo uninstall [target]...` | 移除指定 AI 工具的入口文件;全量卸载时同时删除 `.draftgo/skill/`。加 `--purge` 会连 `.draftgo/` 整个删掉。 |
|
|
40
40
|
| `draftgo status` | 查看当前项目装了哪些 AI 工具入口、skill 版本。 |
|
|
41
41
|
| `draftgo doctor` | 诊断:Python 是否可用、检测到哪些 AI 工具、各入口状态。 |
|
|
@@ -47,8 +47,25 @@ draftgo init all # 所有支持的工具
|
|
|
47
47
|
|
|
48
48
|
- `--project <dir>`:对指定目录操作(默认当前工作目录)。
|
|
49
49
|
- `--force`:`init/update` 时覆盖已存在的 `.draftgo/skill/`。
|
|
50
|
+
- `--skip-update-check`:`update` 时不去 npm 查最新版,直接用当前 CLI 执行。
|
|
50
51
|
- `--purge`:`uninstall` 时连 `.draftgo/`(含 config / 日志 / 本地缓存)一起删。
|
|
51
52
|
|
|
53
|
+
可以通过环境变量 `DRAFTGO_NO_UPDATE_CHECK=1` 全局关闭自动升级检查(离线、CI 等场景)。
|
|
54
|
+
|
|
55
|
+
## 更新
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd <your-project>
|
|
59
|
+
draftgo update
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
就这一条命令。`update` 会自己去 npm 查最新版:
|
|
63
|
+
|
|
64
|
+
- 如果 CLI 已是最新 → 直接把内置 skill 资源覆盖到 `.draftgo/skill/`
|
|
65
|
+
- 如果 CLI 过时 → 自动执行 `npm install -g draftgo-cli@latest`,然后用新 CLI 重跑一遍 `update`
|
|
66
|
+
|
|
67
|
+
整个过程中,你本地的 `.draftgo/config.json`、`iteration/`、`bugs/`、`pages/` 等运行时数据都不会被动到。
|
|
68
|
+
|
|
52
69
|
## 自动识别的依据
|
|
53
70
|
|
|
54
71
|
| AI 工具 | 探测信号(任一命中即视为在用) |
|
|
@@ -73,7 +90,6 @@ draftgo init all # 所有支持的工具
|
|
|
73
90
|
├── .draftgo/
|
|
74
91
|
│ ├── skill/ # CLI 管理,不要手动改
|
|
75
92
|
│ │ ├── SKILL.md
|
|
76
|
-
│ │ ├── CLAUDE.md
|
|
77
93
|
│ │ ├── init/SKILL.md
|
|
78
94
|
│ │ ├── sync/SKILL.md
|
|
79
95
|
│ │ ├── bug/SKILL.md
|
package/bin/draftgo.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "draftgo-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro).",
|
|
5
5
|
"bin": {
|
|
6
6
|
"draftgo": "bin/draftgo.js"
|
package/resources/skill/SKILL.md
CHANGED
|
@@ -15,21 +15,337 @@ version: 1.0.0
|
|
|
15
15
|
| 用户意图 | 执行 |
|
|
16
16
|
|---|---|
|
|
17
17
|
| 初始化项目 / 连接服务器 / `/draftgo init` | 调用 `/draftgo init` Skill |
|
|
18
|
-
|
|
|
19
|
-
| 开发页面 / 修改页面 / 新建导航 |
|
|
18
|
+
| 同步页面、导航或数据库 / `/draftgo sync` | 调用 `/draftgo sync` Skill |
|
|
19
|
+
| 开发页面 / 修改页面 / 新建导航 | 读取本文件下方"架构认知"及 `.draftgo/rules/frontend.md` + `.draftgo/rules/debugging-syntax.md` 后开始开发,**完成后必须:1) 写迭代记录 2) 自动同步到服务器**(见下方规范) |
|
|
20
20
|
|
|
21
21
|
## 开发前置检查
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
进行任何开发任务前,先确认 `.draftgo/config.json` 存在:
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
!test -f
|
|
26
|
+
!test -f .draftgo/config.json && echo "OK" || echo "请先运行 /draftgo init"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
存在后,从 `.draftgo/config.json` 读取服务器地址和 token。
|
|
30
30
|
|
|
31
31
|
开发规范读取:`.draftgo/rules/frontend.md`
|
|
32
32
|
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 架构认知(必读)
|
|
36
|
+
|
|
37
|
+
**DraftGo 不是传统 SPA,是"数据库驱动的页面资产运行时":**
|
|
38
|
+
- 壳层(Vue 3 + Vite)只负责 runtime 编排,源码在 `frontend/src/core/`
|
|
39
|
+
- 业务页面 HTML 存在数据库 `page.value.html`,运行在 `iframe.srcdoc`
|
|
40
|
+
- 导航栏 HTML 存在数据库 `navigation.html`,由壳层按需加载
|
|
41
|
+
- 页面与壳层通过 `window.parent.App` API 通信
|
|
42
|
+
|
|
43
|
+
**技术栈:**
|
|
44
|
+
- 后端:FastAPI 0.115.0 + SQLAlchemy 2.0.36 + Pydantic 2.10.0 + MySQL + Redis
|
|
45
|
+
- 前端:Vue 3 + Vite + Tailwind CSS(本地 `/assets/tailwindcss.js`)
|
|
46
|
+
|
|
47
|
+
## 连接信息
|
|
48
|
+
|
|
49
|
+
- 服务器地址与 Token:存储于 `.draftgo/config.json`(已加入 .gitignore)
|
|
50
|
+
- 运行 `/draftgo init` 时写入
|
|
51
|
+
|
|
52
|
+
## 开发规范
|
|
53
|
+
|
|
54
|
+
→ 前端页面开发:读取 `.draftgo/rules/frontend.md`
|
|
55
|
+
→ 页面排错指南:读取 `.draftgo/rules/debugging-syntax.md`(页面功能不执行时必读!)
|
|
56
|
+
- 任何开发,必读!
|
|
57
|
+
|
|
58
|
+
## 开发禁区(违反必报错)
|
|
59
|
+
|
|
60
|
+
- ❌ 禁止境外 CDN(googleapis/jsdelivr/cdnjs/unpkg)→ ✅ 允许国内镜像(npmmirror.com/staticfile.net)
|
|
61
|
+
- ❌ 禁止 `App()` 写法(App 是对象不是函数)→ ✅ 正确:`const App = window.parent?.App`
|
|
62
|
+
- ❌ 禁止 `const App = () => window.parent?.App` → ✅ 去掉箭头函数
|
|
63
|
+
- ❌ 禁止 `window.location.search` 读参数 → ✅ 用 `window.__DG_ROUTE_CONTEXT__.query`
|
|
64
|
+
- ❌ 禁止 `App?.user?.role` 判断管理员 → `App.user` 不存在!✅ 用 `App.isAdmin` 或 `App.currentUser?.role_code`
|
|
65
|
+
- ❌ 禁止 `navigate('/login')` 退出 → ✅ 用 `window.location.href = '/login'`
|
|
66
|
+
- ❌ 禁止 `window.alert/confirm/prompt` → ✅ 用 `App.toast()` / `App.confirm()` / `App.showModal()`
|
|
67
|
+
- `App.showSuccess(msg)` 成功提示(绿色 3s)
|
|
68
|
+
- `App.showError(msg)` 错误提示(红色 4s)
|
|
69
|
+
- `App.showWarning(msg)` 警告提示(黄色 3s)
|
|
70
|
+
- `App.showInfo(msg)` 信息提示(蓝色 3s)
|
|
71
|
+
- `App.toast(msg, type)` 通用方法 — type: success/error/warning/info
|
|
72
|
+
- `App.confirm(msg, title?)` 确认弹窗 — 返回 Promise\<boolean\>
|
|
73
|
+
- `App.showModal(msg, title?)` 信息模态框 — 替代 alert()
|
|
74
|
+
- `App.showLoading()` / `App.hideLoading()` — 全局 loading
|
|
75
|
+
- `App.callApi(code, options?)` — 调用已注册的外部 API(Promise→`{status_code, headers, body, duration_ms, error}`)
|
|
76
|
+
- `App.listApis()` — 列出当前用户可调用的外部 API(含 code / name / method / 各 JSON Schema)
|
|
77
|
+
- 详见 `.draftgo/rules/frontend.md` App API 章节
|
|
78
|
+
- ⚠️ 尽量避免硬编码颜色 → 优先用 `var(--dg-accent)` 等 token 以适配主题切换
|
|
79
|
+
|
|
80
|
+
## 主题切换适配
|
|
81
|
+
|
|
82
|
+
DraftGo 支持多套配色方案 + 亮暗模式,通过 CSS 变量实现。
|
|
83
|
+
|
|
84
|
+
**概念分层:**
|
|
85
|
+
| 概念 | 存储键 | 取值 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 显示模式 | `dg_theme` | `'light'` \| `'dark'` \| `'system'` |
|
|
88
|
+
| 配色方案 | `dg_color_scheme` | `'dark-gray-white'` \| `'deep-blue-white'` \| `'orange-white'` \| `'custom'` |
|
|
89
|
+
|
|
90
|
+
**内置配色方案:**
|
|
91
|
+
- `dark-gray-white` 深灰白(默认)— 冷调深灰,干净利落
|
|
92
|
+
- `deep-blue-white` 深蓝白 — 专业稳重,企业风
|
|
93
|
+
- `orange-white` 橙白 — 温暖醒目,创意风
|
|
94
|
+
|
|
95
|
+
**颜色 Token(优先使用):**
|
|
96
|
+
- `--dg-bg-base` / `--dg-bg-page` / `--dg-bg-surface` — 背景层级
|
|
97
|
+
- `--dg-text-primary` / `--dg-text-secondary` / `--dg-text-muted` — 文字层级
|
|
98
|
+
- `--dg-accent` / `--dg-accent-hover` / `--dg-accent-subtle` — 主题色
|
|
99
|
+
- `--dg-border` / `--dg-success` / `--dg-error` / `--dg-warning` — 功能色
|
|
100
|
+
|
|
101
|
+
**App API 切换配色:**
|
|
102
|
+
```javascript
|
|
103
|
+
App.setColorScheme('deep-blue-white'); // 切换为预设方案
|
|
104
|
+
App.setColorScheme('custom', customVarsObject); // 应用自定义配色
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**硬编码颜色的场景(允许但需注意):**
|
|
108
|
+
- 品牌色(如 Logo 固定色)
|
|
109
|
+
- 数据可视化图表(需保证对比度)
|
|
110
|
+
- 第三方组件库强制要求
|
|
111
|
+
|
|
112
|
+
**硬编码时的要求:**
|
|
113
|
+
- 同时提供亮色/暗色两套值,通过 `[data-theme="dark"]` 选择器切换
|
|
114
|
+
- 确保对比度符合 WCAG AA 标准(文字至少 4.5:1)
|
|
115
|
+
|
|
116
|
+
## 本地开发流程
|
|
117
|
+
|
|
118
|
+
**原则:有功能修改就读本地数据,自主决策是否调用 API 完成高质量修改。**
|
|
119
|
+
|
|
120
|
+
1. **开发时**:读写 `.draftgo/` 本地文件
|
|
121
|
+
- 查元数据:读 `index.json`
|
|
122
|
+
- 查/改 HTML:按 `html_file` 字段路径读对应 `.html` 文件
|
|
123
|
+
2. **修改后**:运行同步脚本推送到服务器(见下方"同步规范")
|
|
124
|
+
3. **同步成功**:本地与服务器数据一致
|
|
125
|
+
|
|
126
|
+
**本地数据目录(均在 `.draftgo/` 下):**
|
|
127
|
+
|
|
128
|
+
| 目录 | 内容 |
|
|
129
|
+
|---|---|
|
|
130
|
+
| pages/ | index.json(无 html)+ 若干 .html |
|
|
131
|
+
| navigations/ | index.json(无 html)+ 若干 .html |
|
|
132
|
+
| roles/ | index.json |
|
|
133
|
+
| users/ | index.json |
|
|
134
|
+
| db_meta/ | index.json |
|
|
135
|
+
| aihub/ | index.json |
|
|
136
|
+
| external_apis/ | index.json(已注册的外部 API 元信息,`auth_config` 已脱敏) |
|
|
137
|
+
| system_config/ | index.json |
|
|
138
|
+
| iteration/ | YYYY-MM-DD.txt(迭代日志) |
|
|
139
|
+
| error/ | YYYY-MM-DD.txt(错误日志) |
|
|
140
|
+
|
|
141
|
+
## 数据库 Schema(关键字段)
|
|
142
|
+
|
|
143
|
+
| 表 | 关键字段 | 说明 |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `page` | `id`, `title`, `route`, `permission`, `value` (JSON: `{html: "..."}`) | value.html 存页面完整 HTML |
|
|
146
|
+
| `navigation` | `id`, `code`, `name`, `html`, `order`, `status` | html 字段存导航栏完整 HTML |
|
|
147
|
+
| `user` | `id`, `username`, `email`, `role_id`, `status` | role_id 关联 role 表 |
|
|
148
|
+
| `role` | `id`, `code`, `name`, `permissions` (JSON) | admin 角色拥有所有权限 |
|
|
149
|
+
| `db_meta` | `id`, `type`, `fields` (JSON: `[{name, type, required}]`) | 动态表结构定义 |
|
|
150
|
+
| `db` | `id`, `type`, `data` (JSON), `userid`, `status`, `created_at`, `updated_at` | 动态数据存储(含自动时间戳) |
|
|
151
|
+
| `aihub` | `id`, `name`, `type`, `config` (JSON), `status` | AI 服务配置 |
|
|
152
|
+
| `sys_config` | `config_key`, `config_value`, `value_type`, `category`, `is_sensitive` | 系统配置 KV |
|
|
153
|
+
|
|
154
|
+
## API 速查与示例
|
|
155
|
+
|
|
156
|
+
| 模块 | 端点 |
|
|
157
|
+
|---|---|
|
|
158
|
+
| 认证 | POST /api/auth/login, /register, /logout, /refresh, /forgot-password, /reset-password |
|
|
159
|
+
| 微信认证 | POST /api/auth/wechat/mp/oauth, /wechat/mini/login, /wechat/mp/qr/create · GET /wechat/mp/qr/poll |
|
|
160
|
+
| 用户 | GET/PUT /api/users/me · GET/POST /api/users · GET/PUT/DELETE /users/{id} · POST /users/{id}/ban, /unban |
|
|
161
|
+
| 角色 | GET/POST /api/roles · GET/PUT/DELETE /roles/{id} · POST /roles/{id}/assign/{uid} · DELETE /roles/{id}/revoke/{uid} |
|
|
162
|
+
| 页面 | GET/POST /api/pages/ · GET/PUT/DELETE /pages/{id} · POST /pages/{id}/reset-system · GET /pages/by-route · GET /pages/public/{route} |
|
|
163
|
+
| 导航栏 | GET/POST /api/navigations · GET /navigations/{code} · PUT/DELETE /navigations/{id} · POST /navigations/{id}/reset-system |
|
|
164
|
+
| 系统配置 | GET /api/system/config · GET /system/category/{category} · GET/PUT/DELETE /system/{key} · POST /system/ · POST /system/config/ensure |
|
|
165
|
+
| 备份恢复(基础) | 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 |
|
|
166
|
+
| 备份恢复(增强) | 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(回溯/撤销) |
|
|
167
|
+
| ⚠️ 二次密码 | restore / reset / undo / cleanup-orphans / package restore 都需要先 POST `/auth/reauth { password, scope }` 拿一次性 `confirm_token`,在请求头加 `X-Confirm-Token: <token>` 才能调用。SAT 调用方自动豁免。 |
|
|
168
|
+
| 通知公告 | GET/POST /api/notices · GET/PUT/DELETE /notices/{id} |
|
|
169
|
+
| 反馈 | GET/POST /api/feedback · GET /feedback/updates · GET/PUT/DELETE /feedback/{id} · DELETE /feedback/batch |
|
|
170
|
+
| 日志 | GET /api/logs · GET /logs/{id} |
|
|
171
|
+
| 智能体 | GET /api/agents · GET /agents/logs · POST /agents/{id}/chat · POST /agents/{id}/preview-chat |
|
|
172
|
+
| 动态DB | GET/POST /api/db/{type} · GET/PUT/DELETE /db/{type}/{id} |
|
|
173
|
+
| DB Meta | GET/POST /api/db-meta · GET /db-meta/{type} · PUT/DELETE /db-meta/{id} |
|
|
174
|
+
| ⚠️ DB Meta 查询 | GET /db-meta/{type} 用 **type**(如 `patient_profile`),不是 id。用 id 查会返回 "元数据不存在"。PUT/DELETE 才用 id。 |
|
|
175
|
+
| AIHub | GET/POST /api/aihub · GET/PUT/DELETE /aihub/{id} · PATCH /aihub/{id}/basic · POST /aihub/{id}/sync · POST /aihub/discover · GET /aihub/types |
|
|
176
|
+
| AI推理 | GET /api/v1/models · POST /v1/chat/completions |
|
|
177
|
+
| 外部 API(页面调用端) | GET /api/external-apis/available · POST /api/external-apis/call/{code}<br>页面里**优先使用 `App.callApi(code, options)`**,不要直连这两个端点 |
|
|
178
|
+
| 外部 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 |
|
|
179
|
+
| 文件上传 | POST /api/upload |
|
|
180
|
+
| 通知测试 | POST /api/system/notifications/test-email, /test-sms · GET /system/notifications/logs |
|
|
181
|
+
|
|
182
|
+
### 高频 API 示例
|
|
183
|
+
|
|
184
|
+
**创建页面(需 admin):**
|
|
185
|
+
```
|
|
186
|
+
POST /api/pages/
|
|
187
|
+
Body: { "title": "新页面", "route": "/new", "permission": "public", "value": {"html": "<html>...</html>"} }
|
|
188
|
+
Response: { "id": 123, "title": "新页面", "route": "/new", ... }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**更新页面 HTML(需 admin):**
|
|
192
|
+
```
|
|
193
|
+
PUT /api/pages/123
|
|
194
|
+
Body: { "value": {"html": "<html>更新后...</html>"} }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**重置系统页面(需 admin):**
|
|
198
|
+
```
|
|
199
|
+
POST /api/pages/123/reset-system
|
|
200
|
+
Body: 空
|
|
201
|
+
说明:从 backend/init/pages/ 重新加载该页面
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**创建动态数据(普通用户自动绑定 userid):**
|
|
205
|
+
```
|
|
206
|
+
POST /api/db/patient
|
|
207
|
+
Body: { "data": {"name": "张三", "age": 30} }
|
|
208
|
+
Response: { "id": 456, "type": "patient", "data": {...}, "userid": 当前用户id, "status": 1, "created_at": "...", "updated_at": "..." }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**查询动态数据(带分页):**
|
|
212
|
+
```
|
|
213
|
+
GET /api/db/patient?page=1&page_size=20
|
|
214
|
+
Response: { "items": [...], "total": 100, "page": 1, "page_size": 20 }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**调用外部 API(页面里):**
|
|
218
|
+
```javascript
|
|
219
|
+
// 在页面 HTML 里:壳层会自动注入认证、做参数校验、写日志
|
|
220
|
+
const r = await App.callApi('weather-now', {
|
|
221
|
+
path_params: { city: 'beijing' }, // 替换 path 模板里的 {city}
|
|
222
|
+
query_params: { unit: 'metric' }, // ?unit=metric
|
|
223
|
+
// body: { ... }, // POST/PUT/PATCH 才生效
|
|
224
|
+
// headers: { 'X-Trace': 't1' }, // 不会覆盖后端注入的认证头
|
|
225
|
+
});
|
|
226
|
+
if (r.status_code >= 200 && r.status_code < 300) {
|
|
227
|
+
console.log(r.body); // JSON 自动解析
|
|
228
|
+
} else {
|
|
229
|
+
App.showError(`上游返回 ${r.status_code}`);
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
- 不要在页面里写 API Key / Bearer Token,由管理员在「外部 API」管理页注册即可
|
|
233
|
+
- API code 不知道时先 `await App.listApis()` 查询
|
|
234
|
+
- `r.error` 仅在代理层错误时(超时 / 网络 / 校验失败)非空;上游业务错误请看 `r.status_code` + `r.body`
|
|
235
|
+
|
|
236
|
+
## 用自然语言注册/管理外部 API(SAT 直连管理端)
|
|
237
|
+
|
|
238
|
+
> 当用户说"帮我接入和风天气"、"注册一个 OpenAI 兼容接口"、"把这个 API 加进来"等,**走管理端 API 注册**,不要让用户去后台手动配置。
|
|
239
|
+
|
|
240
|
+
### 1. 先查本地缓存
|
|
241
|
+
|
|
242
|
+
读 `.draftgo/external_apis/index.json` 确认是否已注册过相同 `code`。
|
|
243
|
+
|
|
244
|
+
### 2. 调用管理端 API(用 SAT,需 admin)
|
|
245
|
+
|
|
246
|
+
读 `.draftgo/config.json` 拿到 `server` + `token`,按下面字段构造 payload,调用 `POST /api/external-apis`:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"code": "weather-now",
|
|
251
|
+
"name": "和风天气-实时",
|
|
252
|
+
"base_url": "https://devapi.qweather.com",
|
|
253
|
+
"method": "GET",
|
|
254
|
+
"path": "/v7/weather/now",
|
|
255
|
+
"headers": {},
|
|
256
|
+
"auth_type": "api_key_query",
|
|
257
|
+
"auth_config": { "name": "key", "value": "用户提供的 KEY" },
|
|
258
|
+
"timeout_ms": 30000,
|
|
259
|
+
"permission": { "default": "login", "roles": [] },
|
|
260
|
+
"param_schema": {
|
|
261
|
+
"query": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"properties": { "location": { "type": "string" } },
|
|
264
|
+
"required": ["location"]
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"tags": ["weather"],
|
|
268
|
+
"description": "实时天气查询"
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**字段约定:**
|
|
273
|
+
- `auth_type`:`none` / `api_key_header` / `api_key_query` / `bearer` / `basic`
|
|
274
|
+
- `api_key_header` / `api_key_query`:`auth_config = {name, value}`
|
|
275
|
+
- `bearer`:`auth_config = {token}`
|
|
276
|
+
- `basic`:`auth_config = {username, password}`
|
|
277
|
+
- `permission.default`:`public`(匿名)/ `login`(登录用户)/ `deny`(仅指定角色)
|
|
278
|
+
- `param_schema.{path,query,body}`:均为 JSON Schema Draft 2020-12,可省略
|
|
279
|
+
- `path` 支持 `{var}` 模板,对应 `param_schema.path` 中字段
|
|
280
|
+
- 认证字段由后端 AES-256-GCM 加密落库;后续 GET 详情会返回脱敏值
|
|
281
|
+
|
|
282
|
+
### 3. 注册后立即测试 + 写迭代记录
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
POST /api/external-apis/{id}/test
|
|
286
|
+
Body: { "path_params": {...}, "query_params": {...}, "body": ... }
|
|
287
|
+
返回: { ok, status_code, duration_ms, error }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
测试通过后写迭代记录:`[新增] 注册外部 API <code>`
|
|
291
|
+
|
|
292
|
+
### 4. 常见管理操作
|
|
293
|
+
|
|
294
|
+
| 用户意图 | API 调用 |
|
|
295
|
+
|---|---|
|
|
296
|
+
| 列出所有 API | `GET /api/external-apis?page=1&page_size=50` |
|
|
297
|
+
| 改名/换地址/换 KEY | `PUT /api/external-apis/{id}`(仅传需要改的字段;不传 `auth_config` 则保留原 KEY) |
|
|
298
|
+
| 启用/禁用 | `PATCH /api/external-apis/{id}/status` body `{status:"active|disabled"}` |
|
|
299
|
+
| 删除 | `DELETE /api/external-apis/{id}` |
|
|
300
|
+
| 查看调用日志 | `GET /api/external-apis/logs?api_code=<code>&page=1&page_size=50` |
|
|
301
|
+
| 清理日志 | `POST /api/external-apis/logs/cleanup?retention_days=30` |
|
|
302
|
+
| 列出已用标签 | `GET /api/external-apis/tags` |
|
|
303
|
+
|
|
304
|
+
### 5. 用户场景对话样例
|
|
305
|
+
|
|
306
|
+
- 用户:"给我接一个查天气的 API,KEY 是 xxx"
|
|
307
|
+
→ AI 询问:服务商?请求方法?需要哪些参数?
|
|
308
|
+
→ AI 调用 `POST /api/external-apis` 注册 → 调用 `/test` 验证 → 告知 code
|
|
309
|
+
→ 用户在页面里直接 `App.callApi('weather-now', { query_params: { location: 'beijing' } })`
|
|
310
|
+
|
|
311
|
+
- 用户:"禁用 weather-now 这个接口"
|
|
312
|
+
→ AI 先 `GET /api/external-apis?keyword=weather-now` 拿 id → `PATCH /{id}/status` → 写迭代记录
|
|
313
|
+
|
|
314
|
+
## URL 参数读取
|
|
315
|
+
|
|
316
|
+
页面运行在 `iframe.srcdoc` 中,`window.location.search` 不可靠。框架通过 `<script data-dg-route-bridge>` 注入路由上下文。
|
|
317
|
+
|
|
318
|
+
```javascript
|
|
319
|
+
// 标准三阶回落(推荐)
|
|
320
|
+
const routeContext =
|
|
321
|
+
window.__DG_ROUTE_CONTEXT__
|
|
322
|
+
|| window.__DG_GET_ROUTE_CONTEXT__?.()
|
|
323
|
+
|| window.parent?.App?.getCurrentRouteContext?.()
|
|
324
|
+
|| { query: {} };
|
|
325
|
+
const query = routeContext.query || {};
|
|
326
|
+
|
|
327
|
+
// 使用示例
|
|
328
|
+
const patientId = query.patientId; // "42"
|
|
329
|
+
const visitId = query.visitId; // "7"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
禁止:`new URLSearchParams(window.location.search)` — iframe 中取不到壳层 URL。
|
|
333
|
+
|
|
334
|
+
## 常见问题排查
|
|
335
|
+
|
|
336
|
+
| 问题 | 排查步骤 |
|
|
337
|
+
|---|---|
|
|
338
|
+
| 同步失败 | 1. 检查 `.draftgo/config.json` token 是否有效<br>2. 检查服务器连接<br>3. 查看 `.draftgo/error/YYYY-MM-DD.txt` 错误日志 |
|
|
339
|
+
| 页面加载空白 | 1. 检查 `permission` 字段与当前用户角色是否匹配<br>2. 检查路由是否正确(`/api/pages/by-route?route=/xxx`)<br>3. 浏览器控制台查看 JS 错误 |
|
|
340
|
+
| API 返回 401 | Token 过期,前端会自动用 refresh token 刷新,无需手动处理 |
|
|
341
|
+
| API 返回 403 | 权限不足,检查当前用户角色是否有对应权限 |
|
|
342
|
+
| DB Meta GET 返回"元数据不存在" | 你用了 id,应该用 **type**(如 `/api/db-meta/patient_profile`)。PUT/DELETE 才用 id。 |
|
|
343
|
+
| 颜色不生效 | 检查是否用了 `var(--dg-*)` 而非硬编码 hex/rgb |
|
|
344
|
+
| URL 参数读取失败 | 检查是否用了 `window.__DG_ROUTE_CONTEXT__.query` 而非 `window.location.search` |
|
|
345
|
+
| 退出登录后导航栏未更新 | 检查是否用了 `window.location.href = '/login'` 而非 `navigate('/login')` |
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
33
349
|
## 迭代记录规范
|
|
34
350
|
|
|
35
351
|
每次对系统进行开发/修复/修改/完善/删除/重构等操作后,**必须**在 `.draftgo/iteration/` 目录下按日期写入迭代记录。
|
|
@@ -57,19 +373,80 @@ version: 1.0.0
|
|
|
57
373
|
3. 存在则 Read 后追加新行;不存在则直接 Write
|
|
58
374
|
4. 无需告知用户(静默执行)
|
|
59
375
|
|
|
376
|
+
## 错误日志规范
|
|
377
|
+
|
|
378
|
+
**遇到任何开发/运行时错误,必须自动追加到 `.draftgo/error/YYYY-MM-DD.txt`:**
|
|
379
|
+
|
|
380
|
+
格式:`[HH:MM:SS] [错误类型] 描述`
|
|
381
|
+
|
|
382
|
+
示例:
|
|
383
|
+
```
|
|
384
|
+
[14:23:45] [同步失败] pages/123 同步到服务器失败:401 Unauthorized
|
|
385
|
+
[14:30:12] [API错误] GET /api/db/patient 返回 500:数据库连接超时
|
|
386
|
+
[15:10:33] [页面错误] admin-users.html 第 42 行:App is not a function
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
操作流程:
|
|
390
|
+
1. 捕获错误后,获取当前日期和时间
|
|
391
|
+
2. 检查 `.draftgo/error/YYYY-MM-DD.txt` 是否存在
|
|
392
|
+
3. 存在则 Read 后追加新行;不存在则直接 Write
|
|
393
|
+
4. 无需告知用户(静默执行)
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
60
397
|
## 自动同步规范
|
|
61
398
|
|
|
62
399
|
> **每次修改页面或导航栏 HTML 后,必须立即自动同步到服务器。这是强制步骤,不得跳过,不得等用户提醒。**
|
|
63
400
|
|
|
401
|
+
### 同步支持的类型
|
|
402
|
+
|
|
403
|
+
同步脚本覆盖 init 拉取的全部 8 种类型:
|
|
404
|
+
|
|
405
|
+
| 类型 | 命令 | 说明 |
|
|
406
|
+
|---|---|---|
|
|
407
|
+
| pages | `python <skill_scripts>/draftgo_sync.py pages [page_id]` | 页面同步 |
|
|
408
|
+
| nav | `python <skill_scripts>/draftgo_sync.py nav [nav_id]` | 导航栏同步 |
|
|
409
|
+
| db_meta | `python <skill_scripts>/draftgo_sync.py db_meta [id]` | 数据库元数据同步 |
|
|
410
|
+
| aihub | `python <skill_scripts>/draftgo_sync.py aihub [id]` | AI 资产同步 |
|
|
411
|
+
| external_apis | `python <skill_scripts>/draftgo_sync.py external_apis [id]` | 外部 API 注册同步 |
|
|
412
|
+
| system_config | `python <skill_scripts>/draftgo_sync.py system_config [config_key]` | 系统配置同步 |
|
|
413
|
+
| roles | `python <skill_scripts>/draftgo_sync.py roles [id]` | ⚠️ 需向用户二次确认 |
|
|
414
|
+
| users | `python <skill_scripts>/draftgo_sync.py users [id]` | ⚠️ 需向用户二次确认(不下发 password / role_ids) |
|
|
415
|
+
|
|
416
|
+
> `<skill_scripts>` 为同步脚本实际路径,取决于安装方式(`.draftgo/skill/scripts/` 或 `.claude/skills/draftgo/scripts/`)。
|
|
417
|
+
|
|
64
418
|
### 同步规则
|
|
65
419
|
|
|
66
|
-
- 修改了哪个页面的 HTML → 同步该页面:`python
|
|
67
|
-
- 修改了哪个导航栏的 HTML → 同步该导航:`python
|
|
420
|
+
- 修改了哪个页面的 HTML → 同步该页面:`python <skill_scripts>/draftgo_sync.py pages <page_id>`
|
|
421
|
+
- 修改了哪个导航栏的 HTML → 同步该导航:`python <skill_scripts>/draftgo_sync.py nav <nav_id>`
|
|
422
|
+
- 修改了 DB Meta / AI 资产 / 外部 API / 系统配置 → 用对应模式同步:`python <skill_scripts>/draftgo_sync.py <mode> <id>`
|
|
423
|
+
- 修改了 roles/users → **必须先向用户确认**,确认后再运行 `roles` / `users` 模式
|
|
68
424
|
- 同时修改多个 → 逐个同步
|
|
69
425
|
- 同步失败时告知用户,不得静默忽略
|
|
70
426
|
|
|
427
|
+
### ⚠️ roles / users 同步注意事项
|
|
428
|
+
|
|
429
|
+
roles 和 users 涉及权限与账号安全,虽然脚本已支持,**仍必须人工确认后执行**:
|
|
430
|
+
1. 告知用户即将同步的变更内容(角色名/权限变更 或 用户信息变更)
|
|
431
|
+
2. **等待用户明确确认**后,再运行 `draftgo_sync.py roles` / `users`
|
|
432
|
+
3. 用户拒绝则不同步,仅保留本地修改
|
|
433
|
+
|
|
71
434
|
### 完整操作顺序(每次开发任务结束时)
|
|
72
435
|
|
|
73
436
|
1. 写迭代记录(静默)
|
|
74
|
-
2.
|
|
437
|
+
2. 运行同步脚本 / 调用 API(必须执行,roles/users 需二次确认)
|
|
75
438
|
3. 告知用户同步结果
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## 同步命令快捷入口
|
|
443
|
+
|
|
444
|
+
- `/draftgo sync pages` — 触发 sync skill
|
|
445
|
+
- `/draftgo sync nav` — 触发 sync skill
|
|
446
|
+
- `/draftgo sync db_meta` — 触发 sync skill
|
|
447
|
+
- `/draftgo sync aihub` — 触发 sync skill
|
|
448
|
+
- `/draftgo sync external_apis` — 触发 sync skill
|
|
449
|
+
- `/draftgo sync system_config` — 触发 sync skill
|
|
450
|
+
- `/draftgo sync roles` — 触发 sync skill(需人工确认)
|
|
451
|
+
- `/draftgo sync users` — 触发 sync skill(需人工确认)
|
|
452
|
+
- `/draftgo init` — 触发 init skill
|
|
@@ -38,12 +38,12 @@ allowed-tools: Bash(python:*), Bash(find:*), Read
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
##
|
|
41
|
+
## 第三步:确认结果
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
脚本执行完毕后,确认 `.draftgo/config.json` 生成成功:
|
|
44
44
|
|
|
45
45
|
```
|
|
46
|
-
!test -f
|
|
46
|
+
!test -f .draftgo/config.json && echo "OK" || echo "FAIL"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
---
|
|
@@ -58,6 +58,9 @@ allowed-tools: Bash(python:*), Bash(find:*), Read
|
|
|
58
58
|
|
|
59
59
|
## 完成提示
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
将脚本的输出摘要(页面数、导航数、外部 API 数、服务器地址)告知用户,并提示:
|
|
62
62
|
- 下一步:描述要开发的功能,或运行 `/draftgo sync` 同步修改
|
|
63
63
|
- 如需刷新数据:重新运行 `/draftgo init`
|
|
64
|
+
- 已拉取的外部 API 列表存于 `.draftgo/external_apis/index.json`(`auth_config` 已脱敏,仅供 AI 了解可调用 API;如需新增/修改请走管理端 API,详见根 SKILL.md "用自然语言注册/管理外部 API")
|
|
65
|
+
|
|
66
|
+
> 注意:拉取 `external_apis` 需要 SAT 具备 admin 权限。若该项返回 0 条且用户期望应有数据,提醒用户检查 token 角色。
|
|
@@ -253,10 +253,29 @@ App.showLoading(); await someAsyncOp(); App.hideLoading();
|
|
|
253
253
|
|
|
254
254
|
// 文件上传
|
|
255
255
|
const result = await App.uploadFile(file, progress => console.log(progress));
|
|
256
|
+
|
|
257
|
+
// 外部 API 调用(已注册到「外部 API 接入」)
|
|
258
|
+
// 认证由后端注入;不要在页面里写 API Key / Bearer
|
|
259
|
+
const r = await App.callApi('weather-now', {
|
|
260
|
+
path_params: { city: 'beijing' }, // 替换 path 模板里的 {city}
|
|
261
|
+
query_params: { unit: 'metric' },
|
|
262
|
+
// body: { ... }, // POST/PUT/PATCH 才生效
|
|
263
|
+
// headers: { 'X-Trace': 't1' }, // 不会覆盖后端注入的认证头
|
|
264
|
+
});
|
|
265
|
+
// 返回:{ status_code, headers, body, duration_ms, error }
|
|
266
|
+
// body 已按 Content-Type 自动解析;上游业务错误看 status_code,不是 error
|
|
267
|
+
|
|
268
|
+
// 列出当前用户可调用的外部 API(含 code / name / 各 JSON Schema)
|
|
269
|
+
const apis = await App.listApis();
|
|
256
270
|
```
|
|
257
271
|
|
|
258
272
|
路径解析:`https://...` 直接请求 · `/api/...` 拼接 origin · 其他相对路径拼接 apiBase
|
|
259
273
|
|
|
274
|
+
**外部 API 注意事项:**
|
|
275
|
+
- code 不知道时先 `await App.listApis()` 查询
|
|
276
|
+
- `r.error` 仅在代理层错误(超时 / 网络 / 校验失败)时非空
|
|
277
|
+
- API 未注册 / 已禁用 / 无权限 → 后端以 4xx 抛错,会被 `App.callApi` 当成异常 throw
|
|
278
|
+
|
|
260
279
|
---
|
|
261
280
|
|
|
262
281
|
## 颜色 Token(强制)
|
|
@@ -104,14 +104,7 @@ def explode_html_field(out_dir, raw, html_field, fname_prefix, rel_prefix):
|
|
|
104
104
|
return len(index)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
"""复制 skill 的 CLAUDE.md 模板,仅替换服务器地址。"""
|
|
109
|
-
template_path = script_dir.parent / "CLAUDE.md"
|
|
110
|
-
if not template_path.exists():
|
|
111
|
-
return f"# DraftGo 项目上下文\n\n> 由 draftgo-init 自动生成。\n\n## 连接信息\n- 服务器:{server}\n"
|
|
112
|
-
content = template_path.read_text(encoding="utf-8")
|
|
113
|
-
content = content.replace("https://dev.draftgo.cn", server)
|
|
114
|
-
return content
|
|
107
|
+
|
|
115
108
|
|
|
116
109
|
|
|
117
110
|
def main():
|
|
@@ -150,6 +143,7 @@ def main():
|
|
|
150
143
|
"users": "/api/users?page=1&page_size=500",
|
|
151
144
|
"db_meta": "/api/db-meta",
|
|
152
145
|
"aihub": "/api/aihub",
|
|
146
|
+
"external_apis": "/api/external-apis?page=1&page_size=500",
|
|
153
147
|
"system_config": "/api/system/config",
|
|
154
148
|
}
|
|
155
149
|
raw = {}
|
|
@@ -172,8 +166,17 @@ def main():
|
|
|
172
166
|
)
|
|
173
167
|
print(f" OK .draftgo/navigations/ ({counts['navigations']} navs)")
|
|
174
168
|
|
|
175
|
-
for name in ("roles", "users", "db_meta", "aihub", "system_config"):
|
|
169
|
+
for name in ("roles", "users", "db_meta", "aihub", "external_apis", "system_config"):
|
|
176
170
|
items = extract_items(raw[name])
|
|
171
|
+
if name == "external_apis" and items:
|
|
172
|
+
# 列表接口仅返回 summary,缺 param_schema/headers/auth_config;
|
|
173
|
+
# 逐个 GET 详情合并,确保 AI 上下文包含调用所需 schema。
|
|
174
|
+
hydrated = []
|
|
175
|
+
for it in items:
|
|
176
|
+
api_id = it.get("id")
|
|
177
|
+
detail = fetch(server, token, f"/api/external-apis/{api_id}") if api_id else None
|
|
178
|
+
hydrated.append({**it, **detail} if isinstance(detail, dict) else it)
|
|
179
|
+
items = hydrated
|
|
177
180
|
write_index(dg_dir / name, items)
|
|
178
181
|
counts[name] = len(items)
|
|
179
182
|
print(f" OK .draftgo/{name}/ ({counts[name]} items)")
|
|
@@ -184,7 +187,6 @@ def main():
|
|
|
184
187
|
|
|
185
188
|
# rules 已经由 CLI 安装到 .draftgo/skill/rules/,这里不再重复写入。
|
|
186
189
|
|
|
187
|
-
(root / "CLAUDE.md").write_text(build_claude_md(server, SCRIPT_DIR), encoding="utf-8")
|
|
188
190
|
print(f"\nDone: {counts['pages']} pages, {counts['navigations']} navs, server: {server}")
|
|
189
191
|
|
|
190
192
|
|