cardbuddy 0.12.1 → 0.13.0
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 +63 -5
- package/dist/cli.js +42 -25
- package/dist/config.js +10 -4
- package/dist/init.js +43 -2
- package/dist/mcp.js +54 -0
- package/dist/preview-editor/assets/{index-CHuGGIR3.js → index-CjZzK7u7.js} +14 -14
- package/dist/preview-editor/index.html +1 -1
- package/dist/standalone.js +5 -5
- package/dist/vendored/{preview → card-editor}/api.js +1 -1
- package/dist/vendored/{preview → card-editor}/components/SvgSymbols.vue +1 -1
- package/dist/vendored/mcp/server.mjs +84 -0
- package/dist/vendored/server/core/nodeModels.js +1 -1
- package/dist/vendored/server/core/renderStatus.js +1 -1
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +1 -1
- package/dist/vendored/skills/dsl-card-ai/SKILL.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_autofix.mjs +1 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +2 -2
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-BCLshIzC.css +1 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-pP5kTKiI.js +88 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/standalone.html +2 -2
- package/dist/vendored/skills/finmall-card-sync/SKILL.md +2 -2
- package/package.json +1 -1
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-C9k-07y7.css +0 -1
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-oFRM4f4V.js +0 -55
- /package/dist/vendored/{preview → card-editor}/assets/default-image.png +0 -0
- /package/dist/vendored/{preview → card-editor}/assets/svg-symbols.svg +0 -0
- /package/dist/vendored/{preview → card-editor}/components/AbsoluteLayout.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Button.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Canvas.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/CheckGroup.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Column.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/CustomPlaceholder.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Divider.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/GridLayout.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Icon.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Image.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Input.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/ListLayout.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Map.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/MultiColumn.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/MultiText.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/ProgressSlider.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/RadioButton.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/RenderTree.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Root.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Row.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/SvgCode.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Text.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/Video.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/ViewContainer.vue +0 -0
- /package/dist/vendored/{preview → card-editor}/components/base.js +0 -0
- /package/dist/vendored/{preview → card-editor}/components/icons.js +0 -0
- /package/dist/vendored/{preview → card-editor}/components/index.js +0 -0
- /package/dist/vendored/{preview → card-editor}/meta/component-meta.json +0 -0
- /package/dist/vendored/{preview → card-editor}/meta/directive-meta.json +0 -0
- /package/dist/vendored/{preview → card-editor}/meta/style-meta.json +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/StyleParser.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/adapter.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/assets.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/canvas-context.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/card-runtime.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/evaluate.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/property.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/selectionOverlay.js +0 -0
- /package/dist/vendored/{preview → card-editor}/runtime/toPreview.js +0 -0
package/README.md
CHANGED
|
@@ -59,6 +59,12 @@ npm run preview # 本地服务(8787) + 内置编辑器前端 +
|
|
|
59
59
|
> 本命令取代了 dsl-card 仓库里旧的 `npm run gen:demo`:脚手架随 npm 包发布,无需检出仓库,
|
|
60
60
|
> 且版本天然与所装 `cardbuddy` 一致(旧方案生成的 `demo/` 会随主干漂移)。
|
|
61
61
|
|
|
62
|
+
> ⚠️ **`npx cardbuddy init` 卡住不退、目录里只多了个 `.cache/`?** 本机装着 0.12.0 之前的
|
|
63
|
+
> 全局旧版:`npx` 优先命中全局 bin,而旧版没有 `init` 子命令,会把 `init` 当无关参数忽略、
|
|
64
|
+
> 转而启动常驻开发服务(于是既不退、也不脚手架)。用 `npm ls -g cardbuddy` 核对版本,
|
|
65
|
+
> 再 `npm i -g cardbuddy@latest` 升级,或 `npm uninstall -g cardbuddy` 让 npx 每次自行拉取。
|
|
66
|
+
> 0.12.0 起 CLI 对未知子命令会直接报错退出,不再静默起服务。
|
|
67
|
+
|
|
62
68
|
---
|
|
63
69
|
|
|
64
70
|
## Preview 模式(本地独立调试)
|
|
@@ -89,6 +95,58 @@ cardbuddy --preview # 等价短参 -p
|
|
|
89
95
|
|
|
90
96
|
---
|
|
91
97
|
|
|
98
|
+
## MCP:把 `card_*` 工具挂给外部 AI agent
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cardbuddy --mcp # stdio JSON-RPC 2.0;不启 HTTP 服务、不占端口
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
编辑器内置的网页 AI 助手走 `POST /__ai/chat`;`--mcp` 把**同一套** 16 个 `card_*` 工具
|
|
105
|
+
(实现同为 `mcp/tools.mjs` + `mcp/client.mjs`,不存在第二份契约逻辑)以 stdio 暴露给
|
|
106
|
+
Codex / Claude 这类外部 agent。工具清单与用法见 `skills/dsl-card-ai/SKILL.md`。
|
|
107
|
+
|
|
108
|
+
agent 侧配置(`cwd` 必须是卡片工程根,读写才落在 `src/cards/`):
|
|
109
|
+
|
|
110
|
+
```jsonc
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"dsl-card": {
|
|
114
|
+
"command": "<工程绝对路径>/node_modules/.bin/cardbuddy",
|
|
115
|
+
"args": ["--mcp"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
> **stdout 是协议信道。** 经 `npm run` 调用必须加 `--silent`:`npm run mcp` 会把
|
|
122
|
+
> `> 包名 脚本名` 横幅打到 stdout,多一行客户端就解析失败;直接调
|
|
123
|
+
> `node_modules/.bin/cardbuddy --mcp` 无此问题。诊断一律走 stderr 且默认静默,
|
|
124
|
+
> `CARDBUDDY_MCP_VERBOSE=1` 打开(会打出注入后的 `root=` / `apiBase=`)。
|
|
125
|
+
|
|
126
|
+
`cardbuddy init` 生成的工程已带 `npm run mcp` 脚本。回归见 `tools/tests/cardbuddy-mcp.test.mjs`。
|
|
127
|
+
|
|
128
|
+
### 工具对本地服务的依赖分档
|
|
129
|
+
|
|
130
|
+
`--mcp` 自己不启服务。启动时自动注入 `CARD_ROOT=<工程根>` 与
|
|
131
|
+
`CARD_API_BASE=http(s)://127.0.0.1:<端口>`(端口取 `FINMALL_CARD_PORT` / `DSL_DEV_PORT`,默认
|
|
132
|
+
8787;已有同名环境变量时不覆盖),工具据此决定走 HTTP 还是本地降级:
|
|
133
|
+
|
|
134
|
+
| 档 | 工具 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| 纯本地,从不需要服务 | `card_route_input` `card_scaffold` `card_add_component` |
|
|
137
|
+
| 优先服务,连不上自动降级为直接读写盘(`validate` 跑 Python 校验器) | `card_list` `card_read` `card_write` `card_validate` `card_history` `card_rollback` |
|
|
138
|
+
| 优先服务,降级到包内独立离线渲染器 | `card_screenshot` |
|
|
139
|
+
| **必须**另起一个 `cardbuddy` | `card_render_status` `card_node_models` `card_import_meta` |
|
|
140
|
+
| **必须**另起服务 **且**已登录 FinMall | `card_sync` `card_save` `card_slice_upload` |
|
|
141
|
+
|
|
142
|
+
> **别直接跑包内 `dist/vendored/mcp/server.mjs`。** `mcp/client.mjs` 在**模块顶层**就 resolve
|
|
143
|
+
> `CARD_ROOT` / `CARD_API_BASE`(不是每次调用时读),绕过 CLI 包装层时前者会落到包内快照目录
|
|
144
|
+
> (`card_list` 返回 0 张卡)、后者回退 `devApiBase()` 的 5180(消费者工程没有 Vite 把 `/__*`
|
|
145
|
+
> 代理到 8787,`card_render_status` 直接 `dev-server-required`)。仓库内跑源码时入口回退
|
|
146
|
+
> `<repo>/mcp/server.mjs`,无需先 build。
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
92
150
|
## 配置(环境变量 / `.env`)
|
|
93
151
|
|
|
94
152
|
在仓库根目录的 `.env` 中配置,或直接用环境变量:
|
|
@@ -101,7 +159,7 @@ cardbuddy --preview # 等价短参 -p
|
|
|
101
159
|
| `EDITOR_URL` | `http://127.0.0.1:5180` | 截图自访问的编辑器实例地址(默认指向本地编辑器)。写入 `.env` 即可持久化。 |
|
|
102
160
|
| `DSL_DEV_TOKEN` | _(空)_ | 可选鉴权令牌;开启后请求须带 `x-dev-token` 头。默认仅靠 `127.0.0.1` 绑定(不暴露局域网)。 |
|
|
103
161
|
| `DSL_DEV_HTTPS` | `0` | 设为 `1` 启用自签 HTTPS(兜底旧 Safari / 企业策略的混合内容拦截)。 |
|
|
104
|
-
| `DSL_EDITOR_SRC` | _(空)_ | 内核开发用:指向本地编辑器 `
|
|
162
|
+
| `DSL_EDITOR_SRC` | _(空)_ | 内核开发用:指向本地编辑器 `card-editor/` 源码,绕过 vendored 快照调试。 |
|
|
105
163
|
| `DSL_DEV_PREVIEW` | `0` | 设为 `1` 等价于 `--preview`:同端口托管打包后的编辑器前端并自动打开浏览器。 |
|
|
106
164
|
| `DSL_PREVIEW_ROOT` | _(空)_ | 自定义 preview 前端构建产物目录(默认包内 `dist/preview-editor/`,回退 `cwd/dist`)。 |
|
|
107
165
|
| `DSL_EDITOR_CMD` | _(空)_ | 代码桥外部编辑器命令(如 `code` / `cursor` / 带参数整串),按 shell 语义执行并追加临时文件路径;缺省按平台拉起 VSCode(mac `open -a`、win `cmd /s /c "code"`、linux `code`),CLI 缺失再回退 `vscode://` 协议。 |
|
|
@@ -214,17 +272,17 @@ DSL_DEV_HTTPS=1 cardbuddy
|
|
|
214
272
|
|
|
215
273
|
## 本地开发(编辑器 + 本地服务)
|
|
216
274
|
|
|
217
|
-
本仓库既是「卡片编辑器平台」的源码,也是 `cardbuddy` 的源码。下面说明怎么在本地开发**编辑器本身**(即 `
|
|
275
|
+
本仓库既是「卡片编辑器平台」的源码,也是 `cardbuddy` 的源码。下面说明怎么在本地开发**编辑器本身**(即 `card-editor/` 前端与 `server/core/` 后端)。
|
|
218
276
|
|
|
219
277
|
### 关键区别:同域(dev)vs 跨域(生产)
|
|
220
278
|
|
|
221
|
-
`npm run dev` 能"免费"工作,是因为 `
|
|
279
|
+
`npm run dev` 能"免费"工作,是因为 `card-editor/api.js` 的 `resolveApiBase()` 在 `import.meta.env.DEV` 为真时**走同源**(第 3 条回退),而**这条同源路径在生产里不存在**——`build` 产物的 `DEV=false`,生产永远依赖「编辑器配好 8787 地址 + 跨源调用本地服务」。
|
|
222
280
|
|
|
223
281
|
所以本地开发分两条互补的路径:
|
|
224
282
|
|
|
225
283
|
| 命令 | 编辑器来源 | 后端来源 | 用途 |
|
|
226
284
|
| --- | --- | --- | --- |
|
|
227
|
-
| `npm run dev` | `
|
|
285
|
+
| `npm run dev` | `card-editor/` 源码(Vite dev + HMR,5180) | `cardbuddy` 服务(8787,由 `tools/dev.mjs` 同起) | 日常改编辑器 / 卡片 |
|
|
228
286
|
| `npm run dev:prod` | `dist/` **构建产物**(`vite preview`) | `cardbuddy` 服务(8787) | 部署前权威跨域 smoke test(最贴近生产) |
|
|
229
287
|
| 部署公网 | `dist/` 静态托管(CDN) | 用户本机起 8787 | 生产环境 |
|
|
230
288
|
|
|
@@ -266,7 +324,7 @@ npm run dev:prod
|
|
|
266
324
|
|
|
267
325
|
| 你改的文件 | `npm run dev` | `npm run dev:prod` |
|
|
268
326
|
| --- | --- | --- |
|
|
269
|
-
| `
|
|
327
|
+
| `card-editor/**`(Vue 组件 / runtime / 样式) | ✅ Vite HMR 即时 | ⚠️ 需重跑 `npm run dev:prod`(build 产物,无 HMR) |
|
|
270
328
|
| `src/cards/*.json`(卡片内容) | ✅ `cards-changed` watcher 自动重载 | ✅ 8787 SSE 推送自动重载 |
|
|
271
329
|
| `server/core/**`(后端中间件) | ⚠️ 需**重启** Vite | ⚠️ 需**重启** 8787 服务 |
|
|
272
330
|
| `vite.config.mjs` / 插件配置 | ⚠️ 需重启 Vite | ⚠️ 需重跑 `npm run dev:prod`(重新 build) |
|
package/dist/cli.js
CHANGED
|
@@ -17,9 +17,14 @@ import { installLogMirror } from './core.js'
|
|
|
17
17
|
import { loadConfig } from './config.js'
|
|
18
18
|
import { runInit } from './init.js'
|
|
19
19
|
|
|
20
|
+
const args = process.argv.slice(2)
|
|
21
|
+
const mcpMode = args.includes('--mcp')
|
|
22
|
+
|
|
20
23
|
// 尽早镜像 stdout/stderr:让启动期日志(配置解析、端口占用提示等)也进入
|
|
21
24
|
// 编辑器底部「服务日志」面板的环形缓冲。幂等,CARDBUDDY_STREAM_LOGS=0 时自动跳过。
|
|
22
|
-
|
|
25
|
+
// --mcp 下整个跳过:该模式不启 HTTP 服务,/__logs 没有消费者,让镜像去逐帧拦截
|
|
26
|
+
// JSON-RPC 报文纯属白做功;协议信道的纯净由「这条路径上不打日志」保证(见 src/mcp.js)。
|
|
27
|
+
if (!mcpMode) installLogMirror()
|
|
23
28
|
|
|
24
29
|
// 被 dev.mjs 拉起时让出来源前缀(由启动器带背景色 chip 标注),避免双重来源标签。
|
|
25
30
|
const srcTag =
|
|
@@ -40,12 +45,11 @@ for (const p of ['.env', '.env.cb', '.env.cb.local']) {
|
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
const args = process.argv.slice(2)
|
|
44
48
|
if (args.includes('--help') || args.includes('-h')) {
|
|
45
49
|
console.log(`cardbuddy — CardBuddy 本地开发服务
|
|
46
50
|
|
|
47
51
|
用法:
|
|
48
|
-
cardbuddy [--preview] [--help]
|
|
52
|
+
cardbuddy [--preview] [--mcp] [--help]
|
|
49
53
|
cardbuddy init [目标目录] [--force] [--dry-run] [--no-skills] [--no-sample]
|
|
50
54
|
|
|
51
55
|
命令:
|
|
@@ -61,6 +65,13 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
61
65
|
选项:
|
|
62
66
|
--preview, -p 同时托管打包后的编辑器前端(本地预览),替代线上地址,并自动打开浏览器
|
|
63
67
|
旧名 --offline / -o 仍可用,已废弃
|
|
68
|
+
--mcp 以 stdio 起 MCP server(JSON-RPC 2.0),把 16 个 card_* 工具挂给 Codex / Claude
|
|
69
|
+
等外部 agent;不启 HTTP 服务、不占端口。stdout 只走协议(经 npm run 调用要加
|
|
70
|
+
--silent,否则横幅污染协议流),诊断走 stderr(CARDBUDDY_MCP_VERBOSE=1 打开)。
|
|
71
|
+
本命令自身不起服务,工具按依赖分档:route_input / scaffold / add_component 纯本地;
|
|
72
|
+
list / read / write / validate / history / rollback 降级为直接读写盘;screenshot
|
|
73
|
+
降级到包内离线渲染器;render_status / node_models / import_meta 必须另起一个
|
|
74
|
+
cardbuddy;sync / save / slice_upload 还必须已登录 FinMall。
|
|
64
75
|
--help, -h 显示帮助
|
|
65
76
|
|
|
66
77
|
环境变量:
|
|
@@ -117,26 +128,32 @@ if (subCommand === 'init') {
|
|
|
117
128
|
}
|
|
118
129
|
}
|
|
119
130
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
131
|
+
if (mcpMode) {
|
|
132
|
+
// stdio MCP server:不碰端口 / TLS / Vite,由 server.mjs 常驻监听 stdin 直到对端关闭。
|
|
133
|
+
const { startMcpServer } = await import('./mcp.js')
|
|
134
|
+
await startMcpServer(loadConfig())
|
|
135
|
+
} else {
|
|
136
|
+
const previewFlag = args.includes('--preview') || args.includes('-p')
|
|
137
|
+
const legacyOfflineFlag = args.includes('--offline') || args.includes('-o')
|
|
138
|
+
if (legacyOfflineFlag) {
|
|
139
|
+
console.warn(
|
|
140
|
+
srcTag + chalk.yellow('⚠ --offline 已废弃,请改用 --preview(本次仍按 preview 模式启动)')
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
// 只在显式给了 flag 时才覆盖配置:无条件传 { preview: false } 会把 DSL_DEV_PREVIEW=1 顶成
|
|
144
|
+
// false —— 旧实现正因如此,环境变量开关一直是失效的。
|
|
145
|
+
startServer(previewFlag || legacyOfflineFlag ? { preview: true } : {})
|
|
146
|
+
.then(({ shutdown }) => {
|
|
147
|
+
const stop = async () => {
|
|
148
|
+
console.log('\n' + srcTag + chalk.cyan('正在关闭…'))
|
|
149
|
+
await shutdown().catch(() => {})
|
|
150
|
+
process.exit(0)
|
|
151
|
+
}
|
|
152
|
+
process.on('SIGINT', stop)
|
|
153
|
+
process.on('SIGTERM', stop)
|
|
154
|
+
})
|
|
155
|
+
.catch((e) => {
|
|
156
|
+
console.error(srcTag + chalk.red('启动失败:') + e)
|
|
157
|
+
process.exit(1)
|
|
158
|
+
})
|
|
126
159
|
}
|
|
127
|
-
// 只在显式给了 flag 时才覆盖配置:无条件传 { preview: false } 会把 DSL_DEV_PREVIEW=1 顶成
|
|
128
|
-
// false —— 旧实现正因如此,环境变量开关一直是失效的。
|
|
129
|
-
startServer(previewFlag || legacyOfflineFlag ? { preview: true } : {})
|
|
130
|
-
.then(({ shutdown }) => {
|
|
131
|
-
const stop = async () => {
|
|
132
|
-
console.log('\n' + srcTag + chalk.cyan('正在关闭…'))
|
|
133
|
-
await shutdown().catch(() => {})
|
|
134
|
-
process.exit(0)
|
|
135
|
-
}
|
|
136
|
-
process.on('SIGINT', stop)
|
|
137
|
-
process.on('SIGTERM', stop)
|
|
138
|
-
})
|
|
139
|
-
.catch((e) => {
|
|
140
|
-
console.error(srcTag + chalk.red('启动失败:') + e)
|
|
141
|
-
process.exit(1)
|
|
142
|
-
})
|
package/dist/config.js
CHANGED
|
@@ -88,12 +88,18 @@ export function loadConfig(env = process.env) {
|
|
|
88
88
|
portTakeover,
|
|
89
89
|
// 迷你仓库快照根(发布态 = dist/vendored)
|
|
90
90
|
vendorRoot,
|
|
91
|
-
// 内嵌 Vite 的 SSR 根:build 阶段按「SSR 模块闭包」从
|
|
91
|
+
// 内嵌 Vite 的 SSR 根:build 阶段按「SSR 模块闭包」从 card-editor/ 精选复制(components/* +
|
|
92
92
|
// runtime/* + meta/* + 被引用的少量 assets + api.js),不含编辑器 UI(App.vue / styles /
|
|
93
93
|
// fidelity)—— 那些只存在于已构建好的 preview-editor 静态产物里。
|
|
94
|
-
vendoredRuntimeDir: path.join(vendorRoot, '
|
|
95
|
-
// 仓库内开发回退:从仓库根运行时(cwd=仓库根)能直接命中
|
|
96
|
-
|
|
94
|
+
vendoredRuntimeDir: path.join(vendorRoot, 'card-editor'),
|
|
95
|
+
// 仓库内开发回退:从仓库根运行时(cwd=仓库根)能直接命中 card-editor/,无需先 build。
|
|
96
|
+
repoEditorDir: path.resolve(process.cwd(), 'card-editor'),
|
|
97
|
+
// MCP stdio server 入口(`cardbuddy --mcp`):发布态命中包内快照 vendored/mcp/server.mjs;
|
|
98
|
+
// 仓库内跑源码时 src/vendored 不存在,回退到 <repo>/mcp/server.mjs —— src 与 dist 同深度,
|
|
99
|
+
// 故 __dirname 相对路径在两种情形下都对(不像 root 那样依赖 cwd)。
|
|
100
|
+
mcpServerEntry: fs.existsSync(path.join(vendorRoot, 'mcp', 'server.mjs'))
|
|
101
|
+
? path.join(vendorRoot, 'mcp', 'server.mjs')
|
|
102
|
+
: path.resolve(__dirname, '../../../mcp/server.mjs'),
|
|
97
103
|
// 包内技能快照(build 阶段从仓库 skills/ 全量内联,排除 .local / node_modules / __pycache__):
|
|
98
104
|
// 既是运行时的只读回退,也是 `cardbuddy init` 释放到项目根的源。
|
|
99
105
|
vendoredSkillsDir,
|
package/dist/init.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 释放后既跑得快也写得了(FinMall 凭据落在项目内,不进 node_modules)
|
|
10
10
|
* tools/link-skills.mjs 把 skills/ 链到 Codex / Claude / WorkBuddy 等 AI 工具的项目级技能目录
|
|
11
11
|
* .env.cb 共享配置模板(端口 / 远端基址 / AI 网关占位;密钥写 .env.cb.local)
|
|
12
|
-
* package.json 声明 cardbuddy 依赖与 dev / preview / link:skills 脚本
|
|
12
|
+
* package.json 声明 cardbuddy 依赖与 dev / preview / mcp / link:skills 脚本
|
|
13
13
|
* .gitignore 私密层与产物不入库
|
|
14
14
|
* README.md 上手说明
|
|
15
15
|
*
|
|
@@ -188,6 +188,43 @@ skills/ AI 协作技能库(校验 / 同步 / 建卡管线脚本
|
|
|
188
188
|
npm run link:skills # 交互式选择 Codex / Claude / WorkBuddy 等,建立项目级技能软链
|
|
189
189
|
\`\`\`
|
|
190
190
|
|
|
191
|
+
## 把卡片工具挂给外部 AI agent(MCP,可选)
|
|
192
|
+
|
|
193
|
+
编辑器内置的网页 AI 助手走 \`POST /__ai/chat\`。若要让 Codex / Claude 这类**外部** agent
|
|
194
|
+
直接操作卡片,用随包提供的 MCP stdio server(16 个 \`card_*\` 工具,清单见
|
|
195
|
+
\`skills/dsl-card-ai/SKILL.md\`):
|
|
196
|
+
|
|
197
|
+
\`\`\`jsonc
|
|
198
|
+
// agent 的 MCP 配置;cwd 必须是本工程根,卡片读写才落在 src/cards/
|
|
199
|
+
{
|
|
200
|
+
"mcpServers": {
|
|
201
|
+
"dsl-card": {
|
|
202
|
+
"command": "<本工程绝对路径>/node_modules/.bin/cardbuddy",
|
|
203
|
+
"args": ["--mcp"]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
\`\`\`
|
|
208
|
+
|
|
209
|
+
也可用 \`npm run --silent mcp\`。**\`--silent\` 不能省**:\`npm run\` 会把
|
|
210
|
+
\`> 包名 脚本名\` 横幅打到 **stdout**,而 stdout 是 JSON-RPC 协议信道,多一行就让客户端解析失败。
|
|
211
|
+
|
|
212
|
+
\`--mcp\` 自身不启服务、不占端口,工具按对本地服务的依赖分档:
|
|
213
|
+
|
|
214
|
+
| 档 | 工具 |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| 纯本地,从不需要服务 | \`card_route_input\` \`card_scaffold\` \`card_add_component\` |
|
|
217
|
+
| 优先服务,连不上自动降级为直接读写盘(\`validate\` 跑 Python 校验器) | \`card_list\` \`card_read\` \`card_write\` \`card_validate\` \`card_history\` \`card_rollback\` |
|
|
218
|
+
| 优先服务,降级到包内独立离线渲染器 | \`card_screenshot\` |
|
|
219
|
+
| **必须**另起一个 \`npm run dev\` | \`card_render_status\` \`card_node_models\` \`card_import_meta\` |
|
|
220
|
+
| **必须**另起服务 **且**已登录 FinMall | \`card_sync\` \`card_save\` \`card_slice_upload\` |
|
|
221
|
+
|
|
222
|
+
\`--mcp\` 会自动把 \`CARD_ROOT\` 指到本工程根、\`CARD_API_BASE\` 指到 \`.env.cb\` 的
|
|
223
|
+
\`FINMALL_CARD_PORT\`(默认 8787)。**别直接跑包内 \`vendored/mcp/server.mjs\`**:那样
|
|
224
|
+
\`CARD_ROOT\` 会落到 node_modules 里的快照目录(\`card_list\` 返回 0 张卡),端口也回退到
|
|
225
|
+
5180(\`card_render_status\` 直接报 \`dev-server-required\`)。排查时可设
|
|
226
|
+
\`CARDBUDDY_MCP_VERBOSE=1\` 让启动信息打到 stderr。
|
|
227
|
+
|
|
191
228
|
## FinMall 远端能力(可选)
|
|
192
229
|
|
|
193
230
|
同步 / 保存 / 切图上传需要已登录的 FinMall 会话与 \`playwright\`
|
|
@@ -208,6 +245,10 @@ function projectPackageJson(projectName, version) {
|
|
|
208
245
|
dev: 'cardbuddy',
|
|
209
246
|
start: 'cardbuddy',
|
|
210
247
|
preview: 'cardbuddy --preview',
|
|
248
|
+
// 给外部 agent(Codex / Claude)挂 card_* 工具的 stdio MCP server。
|
|
249
|
+
// 注意:MCP 客户端要走 `npm run --silent mcp` 或直接调 node_modules/.bin/cardbuddy --mcp,
|
|
250
|
+
// 因为 `npm run` 的横幅会打到 stdout、污染 JSON-RPC 协议流(详见生成的 README)。
|
|
251
|
+
mcp: 'cardbuddy --mcp',
|
|
211
252
|
'link:skills': 'node tools/link-skills.mjs',
|
|
212
253
|
},
|
|
213
254
|
devDependencies: {
|
|
@@ -382,7 +423,7 @@ export async function runInit({
|
|
|
382
423
|
const pkgState = writeFileSafe(path.join(target, 'package.json'), projectPackageJson(projectName, version), opts)
|
|
383
424
|
pkgState === 'skipped'
|
|
384
425
|
? dim('package.json 已存在(跳过,--force 可覆盖)')
|
|
385
|
-
: ok(`${dryRun ? '将生成' : '已生成'} package.json(cardbuddy ^${version} + dev / preview / link:skills 脚本)`)
|
|
426
|
+
: ok(`${dryRun ? '将生成' : '已生成'} package.json(cardbuddy ^${version} + dev / preview / mcp / link:skills 脚本)`)
|
|
386
427
|
|
|
387
428
|
// 6) tools/link-skills.mjs(把 skills/ 链给 AI 工具)
|
|
388
429
|
const linkSrc = resolveLinkSkillsSource(config)
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cardbuddy --mcp` —— 以 stdio 起 MCP server(JSON-RPC 2.0,newline-delimited)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有它:包内快照早就带了 mcp/tools.mjs + client.mjs(AI 工具调度的实现真身),
|
|
5
|
+
* 却没带 server.mjs 这个 stdio 入口 —— 于是消费者工程只能用编辑器内置的网页 AI 助手
|
|
6
|
+
* (/__ai/chat),没法把同一套 card_* 工具挂给 Codex / Claude 这类外部 agent。而随包释放的
|
|
7
|
+
* dsl-card-ai 技能整篇是以「只调 dsl-card MCP 工具、绝不重实现契约逻辑」为前提写的,
|
|
8
|
+
* 缺了入口那份技能在消费者工程里就落不了地。
|
|
9
|
+
*
|
|
10
|
+
* 两个必须守住的前提:
|
|
11
|
+
* 1. **stdout 是协议信道**,多一行非 JSON 就让客户端解析失败(`npm run` 的 `> 包名 脚本名`
|
|
12
|
+
* 横幅正是典型污染源,故推荐 `.bin/cardbuddy --mcp` 或 `npm run --silent mcp`)。这条路径
|
|
13
|
+
* 上因此一律不打日志:cli.js 跳过 installLogMirror()(该镜像服务于编辑器「服务日志」面板,
|
|
14
|
+
* MCP 模式没有 HTTP 服务、/__logs 无消费者),启动诊断只走 stderr 且默认静默。
|
|
15
|
+
* 2. **CARD_ROOT / CARD_API_BASE 必须在 import 之前落定**。mcp/client.mjs 在模块顶层
|
|
16
|
+
* 就读这两个变量(不是每次调用时读),晚一步注入就无效:
|
|
17
|
+
* - CARD_ROOT 不给,client 会把「mcp/ 的上一级」当工程根 —— 发布态那是包内快照目录,
|
|
18
|
+
* 根本找不到 src/cards;
|
|
19
|
+
* - CARD_API_BASE 不给,client 回退 devApiBase() 即 5180(仓库里由 Vite 把 /__* 代理到
|
|
20
|
+
* 8787),而消费者工程通常只有 cardbuddy 的 8787、没有 Vite,于是连不上,
|
|
21
|
+
* 且 render_status / sync / save 这三个不支持离线降级的工具直接不可用。
|
|
22
|
+
*
|
|
23
|
+
* 本地服务没起也能用一部分:client.mjs 对 list / read / write / validate 有「直接读盘 + 跑
|
|
24
|
+
* Python 校验器」的离线降级;render_status / sync / save 依赖服务与 FinMall 登录态,不降级。
|
|
25
|
+
*/
|
|
26
|
+
import fs from 'node:fs'
|
|
27
|
+
import { pathToFileURL } from 'node:url'
|
|
28
|
+
|
|
29
|
+
/** 启动信息只在对 stderr 可见时才打(交互式调试);被 MCP 客户端接管时保持静默。 */
|
|
30
|
+
function diag(line) {
|
|
31
|
+
if (process.stderr.isTTY || process.env.CARDBUDDY_MCP_VERBOSE === '1') {
|
|
32
|
+
process.stderr.write(`[cardbuddy --mcp] ${line}\n`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {ReturnType<import('./config.js').loadConfig>} config
|
|
38
|
+
* @returns {Promise<void>} 解析后 server.mjs 自行常驻监听 stdin,本函数不返回直到进程结束
|
|
39
|
+
*/
|
|
40
|
+
export async function startMcpServer(config) {
|
|
41
|
+
const entry = config.mcpServerEntry
|
|
42
|
+
if (!entry || !fs.existsSync(entry)) {
|
|
43
|
+
// 走 stderr + 非 0 退出:MCP 客户端会把它显示成 server 启动失败,而不是静默挂着
|
|
44
|
+
process.stderr.write(`[cardbuddy --mcp] 找不到 MCP server 入口:${entry || '(未解析)'}\n`)
|
|
45
|
+
process.exit(1)
|
|
46
|
+
}
|
|
47
|
+
if (!process.env.CARD_ROOT) process.env.CARD_ROOT = config.root
|
|
48
|
+
if (!process.env.CARD_API_BASE) {
|
|
49
|
+
process.env.CARD_API_BASE = `${config.https ? 'https' : 'http'}://127.0.0.1:${config.port}`
|
|
50
|
+
}
|
|
51
|
+
diag(`root=${process.env.CARD_ROOT}`)
|
|
52
|
+
diag(`apiBase=${process.env.CARD_API_BASE}(本地服务未起时 list/read/write/validate 自动降级为直接读盘)`)
|
|
53
|
+
await import(pathToFileURL(entry).href)
|
|
54
|
+
}
|