@szc-ft/mcp-szcd-client 0.25.0 → 0.27.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.
Files changed (26) hide show
  1. package/agents/opencode-extension/agents/szcd-component-expert.md +160 -40
  2. package/agents/qwen-extension/agents/szcd-component-expert.md +160 -40
  3. package/agents/src/szcd-component-expert.md +187 -40
  4. package/agents/src/tools.json +2 -2
  5. package/agents/szcd-component-expert.md +160 -40
  6. package/agents/szcd-component-expert.qoder.md +186 -41
  7. package/agents/szcd-component-expert.trae.md +185 -40
  8. package/opencode-extension/agents/szcd-component-expert.md +160 -40
  9. package/opencode-extension/skills/local-api-tool/SKILL.md +160 -28
  10. package/opencode-extension/skills/local-api-tool/scripts/extract-swagger.mjs +625 -0
  11. package/opencode-extension/skills/local-api-tool/scripts/extract_swagger.py +125 -9
  12. package/opencode-extension/skills/szcd-component-helper/SKILL.md +12 -13
  13. package/opencode-extension/skills/szcd-design-to-code/SKILL.md +250 -28
  14. package/package.json +1 -1
  15. package/qwen-extension/agents/szcd-component-expert.md +160 -40
  16. package/qwen-extension/qwen-extension.json +1 -1
  17. package/qwen-extension/skills/local-api-tool/SKILL.md +160 -28
  18. package/qwen-extension/skills/local-api-tool/scripts/extract-swagger.mjs +625 -0
  19. package/qwen-extension/skills/local-api-tool/scripts/extract_swagger.py +125 -9
  20. package/qwen-extension/skills/szcd-component-helper/SKILL.md +12 -13
  21. package/qwen-extension/skills/szcd-design-to-code/SKILL.md +250 -28
  22. package/standard-skill/local-api-tool/SKILL.md +160 -28
  23. package/standard-skill/local-api-tool/scripts/extract-swagger.mjs +625 -0
  24. package/standard-skill/local-api-tool/scripts/extract_swagger.py +125 -9
  25. package/standard-skill/szcd-component-helper/SKILL.md +12 -13
  26. package/standard-skill/szcd-design-to-code/SKILL.md +251 -28
@@ -38,7 +38,7 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
38
38
  **在正式开始之前,获取项目架构全局视图,同时验证 MCP 连接。**
39
39
 
40
40
  执行动作:
41
- 1. 调用 `get_architecture_overview`(detail="summary")获取7层架构图、模板组合模式、推荐使用顺序和 LLM 映射错误提示
41
+ 1. 调用 `get_architecture_overview`(detail="full")一次性获取:7 层架构图、模板组合模式、推荐使用顺序、LLM 映射错误提示,**以及 `componentSummary`(全量组件 + 设计元信息:layoutRole/variantProps/styleInjection/useCases,other/echarts 层附带 compositionSlots/requiredHooks/chartType/sketchSignals)+ `styleMapping`(节点样式映射)+ `assetExtraction`(图标/位图/图表色提取规则)**——这些是步骤 3.5 推理组件候选和步骤 5 样式注入的核心依据
42
42
  2. 如果连接失败,**立即停止并返回错误信息**,告知用户需要先配置 MCP 服务器
43
43
  3. 如果成功,根据返回的 `templatePatterns` 初步判断用户需求匹配哪个模板
44
44
 
@@ -127,6 +127,27 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
127
127
  | 元素归属 | 凭 x/y 坐标判定("x=272 列都是同区域") | 看 `getMultipleNodeInfo` 返回的 `parent.id/name` 字段 |
128
128
  | 颜色提取 | 跳过 group 只取 text/rectangle | 同批提交 group ID,从 `style.fills` 取渐变背景 |
129
129
 
130
+ #### getMultipleNodeInfo 返回字段契约(sketch-mcp-server ≥ v1.6.1)
131
+
132
+ | 字段 | 适用类型 | 用途 |
133
+ |------|----------|------|
134
+ | `position:{x,y}` + `size:{width,height}` | 全部 | 步骤 2.6 表 1/2/3/4 的坐标推理 |
135
+ | `parent:{id,name,type}` | 全部(顶层除外) | 步骤 2.6 表 1 大区归属、Pass 2 质检 |
136
+ | `style.fills[].{color,gradient}` | rectangle / group / text | 表 5 样式映射 |
137
+ | `style.borders[].{color,thickness}` | rectangle / group | 表 5 样式映射 |
138
+ | `style.shadows[]` | rectangle / group | 表 5 样式映射 |
139
+ | `text.attributes[0].attributes.{fontFamily,fontSize,color,lineHeight}` | text | 字号/字重/颜色(fontFamily 编码字重,如 PingFangSC-Semibold → 600) |
140
+ | `children:[id]` | group / artboard | 推断容器子节点集合(与 parent 反向印证) |
141
+ | `isVisualContainer:bool` | group / artboard | true = 该 group 自身带 fills/borders/shadows,必须作为 UI 容器渲染(不是纯图层分组) |
142
+ | `shape.cornerRadius` | rectangle / oval | 圆角 |
143
+
144
+ **字段缺失自检**(任一项失败 → sketch-mcp-server 版本过旧,升级到 ≥ v1.6.1 后重跑):
145
+
146
+ - [ ] 任取一个 text 节点,`parent.id` 非空 ✅
147
+ - [ ] 任取一个 group 节点,`isVisualContainer` 类型是 boolean、`children` 是数组 ✅
148
+ - [ ] 任取一个 rectangle 节点,`style.fills` 数组存在(即使为空也要是 `[]` 不能 undefined) ✅
149
+ - [ ] 任取一个 text 节点,`text.attributes[0].attributes.fontFamily` 可读出(编码字重) ✅
150
+
130
151
  **实战经验沉淀(重要)**:
131
152
 
132
153
  **收获 1:首轮 `getMultipleNodeInfo` 必须全量拉取,不能挑**
@@ -141,15 +162,20 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
141
162
  错误做法:先查 text 拿自身样式 → 再根据 parent ID 单独查 rectangle(多走一步)
142
163
  正确做法:用 `queryNodes(type=["text","rectangle","group"])` 一次拿三类 ID,`getMultipleNodeInfo` 返回的 `parent` 字段直接告诉你归属关系
143
164
 
144
- **收获 3:元素归属必须走 group 父子关系,禁止 x 坐标平面判定**
165
+ **收获 3:归属判定走 x+y+parent 三轴联合,禁止单轴定论(坐标和 parent 互为纠错器)**
145
166
 
146
- 实战教训:`'主题'text(x=272,y=149)` 被错误识别为内容区块标题,实际是左侧树标题(与树节点同 x=272 列)。同列坐标不代表同区域。
167
+ 实战教训:
168
+ - 反例 A:`'主题'text(x=272,y=149)` 单看 x 在右侧内容区列,但 `parent.name=leftTreeHeader` 强语义 → 归左树(情形 C:parent 修正坐标)
169
+ - 反例 B(反馈 #11):`'段码含义'text(x=300,y=257)` 坐标落在 `渐变容器(x=240..960, y=189..297)` 内,但 parent 是兄弟编组 `cataMain` → 视觉上属于渐变容器内(情形 B:图层拆开,按坐标渲染)
147
170
 
148
171
  正确做法:
149
- - 步骤 2 拿到完整节点树后,先识别顶层 group 分区(通常 2-5 个,对应 LeftTree区/Query+Table区/信息栏区/标题栏区)
150
- - `getMultipleNodeInfo` v1.5.0+ **自动返回** `parent: { id, name, type }` 字段,按 parent 归属到具体分区
151
- - "主题"这类标题文本,先看 `parent.name` 是否带 "LeftTree" / "树" / "PageContent" 等容器关键字,不要看 x 坐标
152
- - 坐标仅作为同 parent 内子元素排序兜底使用
172
+ - 步骤 2.5 4 步先 `mode="tree"` 看顶层 group 分区(通常 2-5 个)
173
+ - 步骤 2.6 做双向校验:Pass 1 坐标推假设 Pass 2 parent 质检 四类情形 A/B/C/D 处理
174
+ - 大区归属:`parent.name` `LeftTree/树/PageContent/sidebar/cataMain` 等强语义关键字时优先于 x 分布
175
+ - 容器归属:必须 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]`,单轴判定无效
176
+ - 跨分区不比 y;同分区内按 y 升序;同行(y 差<8px)按 x 升序
177
+
178
+ 详细决策矩阵见步骤 2.6。
153
179
 
154
180
  **收获 4:信息栏/卡片容器必须先取 group 再取子元素**
155
181
 
@@ -195,14 +221,129 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
195
221
 
196
222
  - **兜底方案**:① 用 `extractBitmaps` 提取的 PNG/PDF 资源;② `matchIconFromName` 的 Top-1(若 source=compound);③ unmatched 时按 parent 上下文 + 上表选 antd `@ant-design/icons` 复合图标;④ 在代码注释中标注"⚠️ 矢量图标用 antd 图标替代,原始 Sketch 为 shapePath"
197
223
 
198
- **提示(sketch-mcp-server v1.5.0+)**:
224
+ **收获 8:rectangle 的 fill/border 必须以内联样式或精确选择器落到 DOM,禁止借用通用 CSS class**
225
+
226
+ 实战教训(反馈 #11):5 个段码视觉块的颜色(#f2f8fd/#f5ffed/#fffbe6/#f5f6f7)完整地从 rectangle 节点拿到了,但代码却用了 `.beforebg{background:#ebf0ff}` 这种与设计稿无对应关系的通用 class,导致 5 个块颜色全错。
227
+
228
+ 正确做法:
229
+ - 步骤 2.6 的样式映射表(表 5)必须为每个"颜色块"列出 rectangle 数据 → 内联 style 的对应关系
230
+ - 写代码时直接用 `<div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}>`
231
+ - 若必须用 CSS class,类名要能反向追到 Sketch 节点(如 `.segmentBlock-blue` 而非 `.beforebg`),且 class 内容必须**精确等于** rectangle 的 fill/border
232
+ - 反例排查:写完代码后 grep 一下 CSS class 名,出现 `.bg1/.box2/.before*` 这类无语义类名、且颜色和设计稿对不上 → 几乎确定是凭空兜底
233
+
234
+ ### 步骤2.6:布局推理(必做,写代码前的最后一道关)
235
+
236
+ ⚠️ 这一步专治"数据全拿到却放错位置"。反馈 #11 的 5 处错位全部由跳过此步骤导致。
237
+
238
+ **核心思路**:x+y 坐标先画"视觉假设",parent 字段做质检纠错。两套信号**互为纠错器**,不是单方向"谁优先"。
239
+
240
+ **输入**:步骤 2.5 产出的 `text + rectangle + group` 节点全量数据(含 `position.{x,y}`、`size.{width,height}`、`parent.{id,name,type}`、`style`、`isVisualContainer`)
241
+
242
+ #### Pass 1:坐标推假设(产出 5 张表)
243
+
244
+ 每张表都必须输出,即使某类节点为空也要写"无"。
245
+
246
+ ##### 表 1:分区识别表(x+y+parent 联合,定大区)
247
+
248
+ ```
249
+ | 节点 name | x | y | parent.name | 推断分区 | 决策依据 |
250
+ |----------------|-----|-----|------------------|--------------|-------------------------|
251
+ | 主题(text) | 272 | 149 | leftTreeHeader | 左树标题区 | parent 强语义压过 x 列 |
252
+ | 段码定义(text) | 320 | 200 | cataMain | 右侧内容-顶部 | parent 含 PageContent 类语义 |
253
+ ```
254
+
255
+ 判定流程:
256
+ 1. 按 x 把画板切成 1-3 列(x<300 → 左区、x≥300 → 右区,或按设计稿实际边界)
257
+ 2. 用 `parent.name` 校验:含 `LeftTree/树/sidebar/cataMain/PageContent/header/footer` 等关键字时以 parent 为准
258
+ 3. 冲突时遵循"收获 3"的 A/B/C/D 矩阵(见下方 Pass 2)
259
+
260
+ → 此表输出**布局类型**:UpDown(单列)/ LeftRight(双列)/ LeftMiddleRight(三列)
261
+
262
+ ##### 表 2:纵向顺序表(同分区内按 y)
263
+
264
+ ```
265
+ | 分区 | 节点 | y | 推断角色 |
266
+ |------------|---------------|-----|-------------------|
267
+ | 右侧内容区 | 段码定义 | 200 | 卡片 1 |
268
+ | 右侧内容区 | Tabs | 420 | Tabs(type=card) |
269
+ | 右侧内容区 | 配置说明 | 478 | 卡片 2(Tabs 下方) |
270
+ ```
271
+
272
+ 铁律:**同分区内**渲染顺序按 y 升序。跨分区不比 y(左树第 100 行 y 和右内容区第 1 行 y 没有可比性)。
273
+
274
+ ##### 表 3:横向排列表(同行按 x,y 差<8px 视为同行)
275
+
276
+ ```
277
+ | 分区/容器 | 节点 | x | y | 推断角色 |
278
+ |-----------------|--------------|-----|-----|-------------------------|
279
+ | Query 操作栏 | 名称输入框 | 320 | 50 | Form.Item col-span=6 |
280
+ | Query 操作栏 | 状态下拉 | 540 | 50 | Form.Item col-span=6 |
281
+ | Query 操作栏 | 查询按钮 | 980 | 50 | 操作按钮(右对齐) |
282
+ ```
283
+
284
+ 判定流程:
285
+ 1. y 差<8px 识别同一行
286
+ 2. 同行按 x 升序排
287
+ 3. 计算 x 间距推算 antd Col span(24 栅格)或 flex
288
+ 4. 最右元素若 `x + width` 接近容器右边界 → 右对齐(justify-content: flex-end)
289
+
290
+ ##### 表 4:容器归属表(x+y 双轴边界检测)
291
+
292
+ 仅对 `isVisualContainer === true` 的 group 和带 fill/border 的 rectangle 做检测。
293
+
294
+ ```
295
+ | 容器 | x..x+w | y..y+h | 落入子元素 |
296
+ |------------------|----------|----------|----------------------------------|
297
+ | 渐变容器(group) | 240..960 | 189..297 | 段码含义(x=300,y=257) ✅ |
298
+ | 段码块-蓝(rect) | 320..420 | 320..360 | (无子节点,仅样式) |
299
+ ```
300
+
301
+ 判定式:子元素 `x ∈ [容器x, 容器x+w]` **且** `y ∈ [容器y, 容器y+h]` → 渲染在容器内部。**单轴判定无效**。
302
+
303
+ ##### 表 5:样式映射表(rectangle/group fill → UI 组件内联样式)
304
+
305
+ ```
306
+ | Sketch 节点 | 数据来源 | bg/border | 映射到代码 |
307
+ |----------------------|--------------------------|--------------------------|-----------------------------------------------------------|
308
+ | 段码块-蓝(rectangle) | style.fills[0]/borders[0]| bg #f2f8fd / b #d9eafb | <div style={{background:'#f2f8fd',border:'1px solid #d9eafb'}}> |
309
+ | 渐变容器(group) | style.fills[0].gradient | linear #f3faff→#f8fdff | <div style={{background:'linear-gradient(180deg,#f3faff,#f8fdff)'}}> |
310
+ | Tab 激活态 | rect bg #fff border #0079f7 | bg #fff / b #0079f7 | Tabs type="card" + .segmentTabs .ant-tabs-tab-active 覆盖 |
311
+ ```
312
+
313
+ 铁律(见收获 8):rectangle 的 fill/border 必须以**内联样式**或精确选择器落到代码,**禁止**用 `.beforebg{background:#ebf0ff}` 这类与设计稿无对应关系的 CSS class。
314
+
315
+ #### Pass 2:parent 质检(按 A/B/C/D 矩阵处理冲突)
316
+
317
+ 逐行扫表 1,对照 parent 字段:
318
+
319
+ | 情形 | 坐标判定 | parent 判定 | 处理 |
320
+ |------|----------|-------------|------|
321
+ | A | 在容器 X 内 | parent 链含 X | ✅ 双向一致,直接采用 |
322
+ | B | 在容器 X 内 | parent 是兄弟节点 Y | ⚠️ **视觉容器优先**(设计师把图层拆开了)→ 按坐标渲染到 X 内 |
323
+ | C | 不在容器 X 内 | parent.name 强语义 = X | ⚠️ **parent 优先**(绝对定位/溢出布局)→ 按 parent 归属,加 `position:absolute` 标注 |
324
+ | D | 双向都不落 | parent 是顶层 | 独立元素,按坐标走纵向顺序 |
325
+
326
+ 输出**最终归属表**:在表 1 末尾追加 `情形(A/B/C/D)` 列。
327
+
328
+ #### Pass 3:自检清单(写代码前必跑)
329
+
330
+ - [ ] 表 1 是否按 x 分布判定了布局类型(UpDown / LeftRight / LeftMiddleRight)?
331
+ - [ ] 表 2 同分区内是否按 y 排序,没有跨分区比 y?
332
+ - [ ] 表 3 同行元素(y 差<8px)是否按 x 排序,计算了栅格 span 或右对齐?
333
+ - [ ] 表 4 容器归属是否做了 x+y 双轴检测,不是只看一轴?
334
+ - [ ] 表 5 所有"颜色块/卡片/渐变"样式都来自 rectangle/group 的 style.fills/borders,没有出现凭空 CSS class?
335
+ - [ ] Pass 2 冲突行是否标了 A/B/C/D 情形和决策依据?
336
+
337
+ 任一项 ❌ → 回 Pass 1 补;禁止跳到步骤 3 前未输出全部 5 张表 + 最终归属表。
338
+
339
+ **提示(sketch-mcp-server v1.6.1+)**:
199
340
  - 大型 .sketch 文件先 `queryNodes(pageId, type="artboard")` 锁定目标画板,避免一次性加载整个文档
200
341
  - `queryNodes(pageId, type=["text","rectangle","group"])` 一次拿三类节点 ID(type 字段支持数组),跳过 `getPageStructure(mode="ids")` 单独调用
201
342
  - `getPageStructure(pageId, mode="tree", maxDepth=2-3)` 用于看分区层级;`mode="summary"` 用于按类型统计概况
202
- - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段,元素归属直接走 parent,禁止用 x/y 坐标
343
+ - 批量 `getMultipleNodeInfo` 支持任意数量 ID(内部按 100 一批自动分片),无需手动拆分;每个节点自带 `parent: {id,name,type}` 字段 + group/artboard 额外有 `children` 和 `isVisualContainer` —— 步骤 2.6 的双向校验同时用 parent x/y,不是只挑一个
203
344
  - `matchIconFromName` 评分制返回 Top-3 + `bestMatch.source === "compound"` 强映射,shapePath 节点优先走它
204
345
  - 仅当结构化样式数据仍不足(如复杂渐变、阴影)时,才回退到 `renderNodeAsBase64` + `analyze_design_image`
205
- - 如果 sketch-mcp-server 工具不可用,提示用户安装:`npm install -g @szc-ft/sketch-mcp-server`
346
+ - 如果 sketch-mcp-server 工具不可用或版本 < v1.6.1(parent / isVisualContainer 字段缺失),提示用户升级:`npm install -g @szc-ft/sketch-mcp-server@latest`
206
347
 
207
348
  ### 步骤3:分析设计稿(条件执行)
208
349
 
@@ -225,6 +366,7 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
225
366
 
226
367
  **输入**:
227
368
  - 步骤 1 的 `get_architecture_overview` 返回的 `templatePatterns`(TreeQueryTable / QueryTabsTables / LeftRight / UpDown)
369
+ - 步骤 1 的 `componentSummary`:全量组件 + 设计元信息(layoutRole/variantProps/styleInjection/useCases,other/echarts 层附带 compositionSlots/requiredHooks/chartType/sketchSignals)—— **推理组件候选的权威目录,长尾场景按 layoutRole / useCases / sketchSignals 直接反查**
228
370
  - 步骤 1 的 `llmMappingHints.commonMistakes`(避免 LLM 常见映射错误)
229
371
  - 步骤 2.5 的 Sketch 节点结构 + 样式 Token
230
372
  - 或步骤 3 的图片分析描述
@@ -238,38 +380,15 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
238
380
  - ❌ 在步骤 3 内部推理组件候选列表(应只产出图片描述)
239
381
  - ❌ 跳过此环节直接调 `get_component_full_profile`(必须先有候选列表)
240
382
 
241
- **推理时使用映射规则**(**此表为本步骤专用,仅含核心高频场景**):
383
+ **推理依据**:步骤 1 返回的 `componentSummary` 是组件候选的权威目录(由组件库源码动态生成),每项含 `layoutRole` / `variantProps` / `useCases` / `sketchSignals`(图表层)。按以下顺序反查:
242
384
 
243
- *页面级布局*:
244
- | Sketch 特征 | 推断结果 | 依据 |
245
- |---|---|---|
246
- | 画板名称(如 "4.1-编目审核-待办") | `pageName` | 直接使用 |
247
- | 左右分区布局 | `TemplateMode(templateTpye="LeftRight")` | 区域分布 |
248
- | 上下分区布局 | `TemplateMode(templateTpye="TopBottom")` | 区域分布 |
249
- | 标签页(Tabs) + 每个 Tab 内有表格 | `QueryTabsTables` 或 `TemplateMode` + Tabs | templatePatterns.QueryTabsTables |
250
-
251
- *区域级组件*:
252
- | Sketch 特征 | 推断结果 | 依据 |
253
- |---|---|---|
254
- | 左侧窄区域 + 树形图层 | `LeftTree` 组件 | templatePatterns.TreeQueryTable |
255
- | 顶部输入框/下拉框/日期选择器 | `Query` 组件 | 搜索区域特征 |
256
- | 中间表头 + 数据行图层 | `TableOrList` 组件 | 表格区域特征 |
257
- | 弹窗/抽屉图层 | `ModelOrDrawer` 组件 | 弹窗交互特征 |
258
- | 页面标题 + 返回箭头 | `TitleAndBack` 组件 | 标题栏特征 |
259
- | 详情展示区域(只读字段) | `FormItemOrDetailItem(type="detail")` | 详情区特征 |
260
-
261
- *数据可视化(核心 3 类)*:
262
- | Sketch 特征 | 推断结果 | 依据 |
263
- |---|---|---|
264
- | 折线趋势图/时间序列图 | `Line` | ECharts 层图表组件 |
265
- | 柱状对比图/条形图 | `Bar` | ECharts 层图表组件 |
266
- | 饼图/环形图/占比图 | `Pie` | ECharts 层图表组件 |
385
+ 1. **画板名称** → 直接作为 `pageName`
386
+ 2. **大区切分** `componentSummary.other` `layoutRole === '页面定位'` 的组件(如 TemplateMode),结合 `variantProps.templateType`(LeftRight / TopBottom)和 `templatePatterns`(TreeQueryTable / QueryTabsTables)选模板
387
+ 3. **区域容器** → `layoutRole === '区域容器'` 的组件(LeftTree / Query / TableOrList / ModelOrDrawer / TitleAndBack / FormItemOrDetailItem 等),按 `useCases` 描述匹配 Sketch 区域语义
388
+ 4. **图表组件** `componentSummary.echarts[*].sketchSignals` 反查 `chartType`,锁定 Line / Bar / Pie / Scatter 等
389
+ 5. **变体选择** 同一组件的子类型(如 TableOrList 的 ProTable / ProList / EditableProTable)查 `variantProps` 配合 useCases
267
390
 
268
- *表格子类型(核心 2 类)*:
269
- | Sketch 特征 | 推断结果 | 依据 |
270
- |---|---|---|
271
- | 可编辑表格(单元格直接输入) | `TableOrList(componentType="EditableProTable")` | 可编辑表格特征 |
272
- | 卡片列表(非表格式数据卡片) | `TableOrList(componentType="ProList")` | 卡片布局特征 |
391
+ 不要硬编码任何「Sketch 特征 → 组件」对照表——`componentSummary` 是唯一可信源,硬编码表会和服务端动态扫描结果漂移。
273
392
 
274
393
  #### ⚠️ TemplateMode 槽位传参铁律(高频踩坑)
275
394
 
@@ -328,6 +447,7 @@ szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层
328
447
  - ProTable/ProForm 等使用 valueType 配置列/字段类型
329
448
  - 复合组件配合 TemplateMode 使用实现标准页面布局
330
449
 
450
+
331
451
  **交互节点**:代码生成后,主动询问用户是否需要调整:
332
452
  - "代码已生成,是否需要调整样式或交互细节?"
333
453
  - "是否需要补充表单校验规则?"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "szcd-component-helper",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "szcd 组件库 MCP 助手 — 查询组件信息、匹配需求、生成代码",
5
5
  "mcpServers": {
6
6
  "szcd-component-helper": {
@@ -1,14 +1,15 @@
1
1
  ---
2
2
  name: local-api-tool
3
- description: 本地网络 API 工具,专门处理 10.x.x.x 网段的 Swagger/YApi 文档拉取和联调测试。内置 scripts/extract_swagger.py 一步完成"下载→解析→按 tag 过滤→关联 DTO definitions→输出",与服务端 parseApiDocs 一比一对齐(递归深度 5、两层 definitions、传递闭包裁剪),避免 curl 管道截断和手写花括号平衡脚本的弯路。仅当脚本不可用时回退到服务端 parse_swagger_json 兜底。**仅限 10.x.x.x 网段**,172.16-31.x.x 和 192.168.x.x 服务端可直连,不要使用本技能。
3
+ description: 本地网络 API 工具,专门处理 10.x.x.x 网段的 Swagger/YApi 文档拉取和联调测试。内置 scripts/extract-swagger.mjs(Node.js,跨平台零安装;同目录 extract_swagger.py 为等价兜底)一步完成"下载→解析→按 tag 过滤→关联 DTO definitions→输出",与服务端 parseApiDocs 一比一对齐(递归深度 5、两层 definitions、传递闭包裁剪),自动修复非标 JSON(八进制字面量)、自带 /tmp 缓存(TTL 10 分钟),避免 curl 管道截断和手写花括号平衡脚本的弯路。仅当脚本不可用时回退到服务端 parse_swagger_json 兜底。**仅限 10.x.x.x 网段**,172.16-31.x.x 和 192.168.x.x 服务端可直连,不要使用本技能。
4
4
  compatibility:
5
5
  tools:
6
6
  - run_command
7
7
  - web_search
8
8
  - web_fetch
9
9
  runtime:
10
- - python3 >= 3.7
11
- - curl
10
+ - node >= 18(首选,跨平台无需额外依赖;内置 fetch + crypto + fs)
11
+ - python3 >= 3.7(兜底,仅当本地无 Node 时使用)
12
+ - curl(仅 Node < 18 或 fetch 不可用时降级使用)
12
13
  ---
13
14
 
14
15
  # 本地网络 API 工具(local-api-tool)
@@ -38,10 +39,47 @@ compatibility:
38
39
 
39
40
  ## 工作流
40
41
 
42
+ ### 🔍 第 0 步:环境探测(必做,决定后续路径)
43
+
44
+ 在执行任何拉取/解析操作**之前**,AI **必须**先用 `run_command` 探测一次本机运行时,按结果选择执行路径。**不要凭直觉默认 Node**,也不要省略探测——历史反馈中有 Windows/Trae 用户撞 python3 不存在、有精简容器没装 curl。
45
+
46
+ ```bash
47
+ # 一行三探(exit 0 = 可用;任意一项不可用不影响其他探测)
48
+ node --version 2>/dev/null && echo "NODE_OK"; \
49
+ python3 --version 2>/dev/null || python --version 2>/dev/null && echo "PY_OK"; \
50
+ curl --version 2>/dev/null | head -1 && echo "CURL_OK"
51
+ ```
52
+
53
+ **决策表(按优先级从高到低,命中第一个匹配项即停):**
54
+
55
+ | 输出包含 | 走的路径 | 命令头 |
56
+ |---------|---------|--------|
57
+ | `NODE_OK` | **Node 路径**(首选)| `node $SKILL_DIR/scripts/extract-swagger.mjs ...` |
58
+ | `PY_OK`(含 curl)| **Python 路径**(次选)| `python3 $SKILL_DIR/scripts/extract_swagger.py ...`(Windows 上用 `python`)|
59
+ | 仅 `CURL_OK`(无 Node 也无 Python)| **裸 curl + AI 直接推理**(兜底,见下方 A3)| `curl -o /tmp/swagger-raw.json ...` → `read_file('/tmp/swagger-raw.json')` → AI 在上下文里挑接口/字段 |
60
+ | swagger 文件 > 200KB 且裸 curl 兜底装不下上下文 | **MCP parse_swagger_json**(最终兜底)| `api_tool(action="parse_swagger_json", swaggerJson=...)` |
61
+ | 都没有 | **失败**,告知用户至少装一个 | 不要尝试用 `web_fetch` 等绕——会撞内网不可达 |
62
+
63
+ **为什么这么排(含三档兜底的实测对比):**
64
+ - Node 在 mcp 客户端用户中**已装率 100%**(用户必须装 Node 才能跑 szcd-mcp-client 本身),Windows 上单命令 `node` 跨平台无差异;脚本内置 fetch 不强依赖 curl
65
+ - Python 在 macOS ≥ 12.3 / Linux 主流发行版自带,但 **Windows 原生默认没装**——所以排第二
66
+ - **兜底选「裸 curl + read_file + AI 推理」而非「MCP parse_swagger_json」**:实测 100 接口 / 150 DTO / 77KB 的中等规模 swagger,前者只占 ~19K tokens(read_file 一次),后者要 ~38K tokens(read_file + 整段 JSON 作为 swaggerJson 参数再传一次),**MCP 路径反而更费上下文**
67
+ - **绝对禁止"AI 临时写 awk/sed/jq 脚本去切 swagger"**——历史反馈 #2026-06-11-catalog-tag 这条路踩了 3-5 分钟坑,`$ref`/`originalRef` 嵌套不是手写正则能搞定的
68
+ - MCP `parse_swagger_json` 仅作为"裸 curl 也撑不住(swagger 太大、read_file 一次进不来)"的最终兜底保留
69
+
70
+ **简化版(如果你只想一行判断):**
71
+ ```bash
72
+ command -v node >/dev/null && echo NODE || (command -v python3 >/dev/null && echo PY || (command -v python >/dev/null && echo PY || echo CURL_ONLY))
73
+ ```
74
+ 直接根据 stdout 第一个 token 选路径,无需 grep 解析。
75
+
76
+ ---
77
+
41
78
  ### 流程 A:拉取 Swagger API 文档(fetch)
42
79
 
43
- > **优先使用内置脚本 `scripts/extract_swagger.py`**,一步完成"下载 → 解析 → 过滤 → 关联 DTO → 输出",避免 curl 管道截断、临时脚本花括号平衡等弯路。脚本与服务端 `parseApiDocs` 一比一对齐(递归深度 5、两层 definitions、传递闭包裁剪)。
44
- > 仅当脚本不可用(无 python3 / 文件权限缺失)时才回退到「手工 curl + 服务端 parse_swagger_json」兜底流程。
80
+ > **优先使用内置脚本 `scripts/extract-swagger.mjs`(Node.js)**,一步完成"下载 → 解析 → 过滤 → 关联 DTO → 输出",避免 curl 管道截断、临时脚本花括号平衡等弯路。脚本与服务端 `parseApiDocs` 一比一对齐(递归深度 5、两层 definitions、传递闭包裁剪)。
81
+ > 同目录 `extract_swagger.py` 为等价 Python 兜底(行为一致),仅当本地无 Node 18 时使用。
82
+ > 仅当两个脚本都不可用时才回退到「手工 curl + 服务端 parse_swagger_json」兜底流程(见下方 A2)。
45
83
 
46
84
  #### A1. 内置脚本路径(首选)
47
85
 
@@ -52,12 +90,12 @@ compatibility:
52
90
  AI 识别 10.x.x.x 网段(或服务端返回 LOCAL_NETWORK_UNREACHABLE)
53
91
 
54
92
 
55
- 通过 run_command 在本地执行:
56
- bash $SKILL_DIR/scripts/extract_swagger.py \
93
+ 通过 run_command 在本地执行(Node 版,跨平台同一条命令):
94
+ node $SKILL_DIR/scripts/extract-swagger.mjs \
57
95
  --url <apiDocsUrl> [--cookie '...'] [--filter <关键词>] [--out /tmp/parsed.json]
58
96
 
59
97
 
60
- 脚本内部:curl 下载 解析 → 过滤 paths 自动展开被引用 DTO → 输出 JSON
98
+ 脚本内部:fetch 下载(Node 18+ 内置;不可用时降级 curl)→ 解析 → 过滤 → 自动展开 DTO → 输出 JSON
61
99
 
62
100
 
63
101
  AI 读取脚本输出(或 --out 指定的文件),结构与 api_tool(parse_swagger_json) 完全一致:
@@ -65,10 +103,13 @@ AI 读取脚本输出(或 --out 指定的文件),结构与 api_tool(parse_
65
103
  ```
66
104
 
67
105
  **$SKILL_DIR 是本 skill 的绝对目录**。在 qwen/qoder 中 skill 加载时通常通过环境变量或上下文提供,若不确定可用:
68
- - qwen-extension: `~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract_swagger.py`
69
- - opencode-extension: `~/.config/opencode/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract_swagger.py`
106
+ - qwen-extension: `~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract-swagger.mjs`
107
+ - opencode-extension: `~/.config/opencode/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract-swagger.mjs`
108
+ - claude-code: `~/.claude/skills/local-api-tool/scripts/extract-swagger.mjs`
109
+
110
+ **无 Node 环境时**:把命令头 `node .../extract-swagger.mjs` 换成 `python3 .../extract_swagger.py`(或 Windows 上 `python ...`),参数完全一致。
70
111
 
71
- #### A2. 服务端兜底(脚本不可用时)
112
+ #### A2. 裸 curl + AI 直接推理(无 Node 也无 Python 时的兜底)
72
113
 
73
114
  ```
74
115
  调用 api_tool(action="fetch", url=...)
@@ -79,16 +120,43 @@ AI 读取脚本输出(或 --out 指定的文件),结构与 api_tool(parse_
79
120
 
80
121
  AI 通过 run_command 在本地 curl 写到临时文件(禁止管道截断!):
81
122
  步骤1: 鉴权获取 Cookie(如 authInfo.hasPassword 为 true)
82
- 步骤2: curl -o /tmp/swagger.json <apiDocsUrl> ← 必须写文件
123
+ 步骤2: curl -o /tmp/swagger-raw.json <apiDocsUrl> ← 必须写文件
124
+
125
+
126
+ AI 用 read_file('/tmp/swagger-raw.json') 把整段 JSON 读进上下文
127
+
128
+
129
+ AI **直接在对话上下文里**阅读 JSON:按 tag 找 apis、按 $ref/originalRef 跳到 definitions 展开 DTO/VO、组织响应
130
+ (典型规模 100 接口 / 150 DTO / 77KB ≈ 19K tokens,单次 read_file 足够)
131
+ ```
132
+
133
+ > **🚫 绝对禁止"AI 临时写 awk/sed/jq/python 脚本去切 swagger"**
134
+ >
135
+ > 历史反馈 #2026-06-11-catalog-tag 在这条路上踩了 3-5 分钟坑——`$ref`/`originalRef` 双字段嵌套不是手写正则能搞定的,
136
+ > allOf/oneOf/数组 items 几层嵌套下来,临时脚本不是漏字段就是花括号失衡。
137
+ > **直接 read_file 进上下文人脑挑反而最快**。
138
+ > 如果 JSON 大到 read_file 装不下(一般 > 200KB),才升级到 A3 走服务端解析。
139
+
140
+ #### A3. 服务端兜底(仅当 swagger 大到 read_file 装不下,约 > 200KB)
141
+
142
+ ```
143
+ (接 A2 步骤 2:已经 curl -o /tmp/swagger-raw.json 写好文件)
144
+
145
+
146
+ 预判文件大小:ls -l /tmp/swagger-raw.json
147
+ ├─ ≤ 200KB → 回到 A2,read_file 直读即可
148
+ └─ > 200KB → 进入本路径
83
149
 
84
150
 
85
151
  AI 读取整个文件传给服务端:
86
- api_tool(action="parse_swagger_json", swaggerJson=<完整 JSON 字符串>, apiFilter=<可选>)
152
+ api_tool(action="parse_swagger_json", swaggerJson=<完整 JSON 字符串>, apiFilter=<必填,过滤接口避免输出爆炸>)
87
153
 
88
154
 
89
- 返回解析后的 API 文档给用户
155
+ 返回解析后(按 apiFilter 裁剪过)的 API 文档给用户
90
156
  ```
91
157
 
158
+ > 注意:此路径上下文成本翻倍(read_file 一次 + 字符串参数再传一次),**仅在 A2 装不下时启用**,且 `apiFilter` 必填以裁剪输出。
159
+
92
160
  ### 流程 B:联调测试(test)
93
161
 
94
162
  ```
@@ -112,26 +180,27 @@ AI 通过 run_command 在本地执行对应 curl 命令
112
180
 
113
181
  ## 详细执行步骤
114
182
 
115
- ### 一、拉取 Swagger 文档(推荐路径:extract_swagger.py
183
+ ### 一、拉取 Swagger 文档(推荐路径:extract-swagger.mjs
116
184
 
117
- **前提**:本地有 python3(≥ 3.7)和 curl。脚本只依赖标准库,无第三方依赖。
185
+ **前提**:本地有 Node.js ≥ 18(内置 fetch;mcp 客户端用户基本都装了)。脚本只用 Node 内置模块(fs/crypto/child_process),无第三方依赖。
186
+ **无 Node 时回退**:用同目录 `extract_swagger.py`(Python ≥ 3.7 + curl),参数完全一致。
118
187
 
119
188
  #### 1.1 直接通过 URL 下载并解析(推荐)
120
189
 
121
190
  ```bash
122
191
  # 无鉴权
123
- python3 ~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract_swagger.py \
192
+ node ~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract-swagger.mjs \
124
193
  --url 'http://10.2.7.60:30194/ikmp-knowledge/v2/api-docs' \
125
194
  --out /tmp/parsed.json
126
195
 
127
196
  # 有鉴权(先单独 curl 拿 Cookie,参考下方 1.3)
128
- python3 .../extract_swagger.py \
197
+ node .../extract-swagger.mjs \
129
198
  --url 'http://10.2.7.60:30194/ikmp-knowledge/v2/api-docs' \
130
199
  --cookie 'JSESSIONID=xxx' \
131
200
  --out /tmp/parsed.json
132
201
 
133
202
  # 按关键词过滤 + 自动裁剪 definitionsDetail(仅保留被引用的 VO 及传递闭包)
134
- python3 .../extract_swagger.py \
203
+ node .../extract-swagger.mjs \
135
204
  --url '<apiDocsUrl>' \
136
205
  --filter tag --filter user \
137
206
  --out /tmp/parsed.json
@@ -148,7 +217,7 @@ curl -sS --connect-timeout 10 --max-time 30 \
148
217
  'http://10.2.7.60:30194/ikmp-knowledge/v2/api-docs'
149
218
 
150
219
  # 第二步:交给脚本解析
151
- python3 .../extract_swagger.py --file /tmp/swagger-raw.json --filter tag --out /tmp/parsed.json
220
+ node .../extract-swagger.mjs --file /tmp/swagger-raw.json --filter tag --out /tmp/parsed.json
152
221
  ```
153
222
 
154
223
  #### 1.3 鉴权(如需 Cookie)
@@ -169,7 +238,7 @@ curl -sS -D - -X GET 'http://10.2.7.60:30194/ikmp-knowledge/swagger-resources/co
169
238
  如果脚本已经解析过、但 AI 后续发现某个 `definitionsDetail` 之外的 VO 也需要展开,用:
170
239
 
171
240
  ```bash
172
- python3 .../extract_swagger.py --file /tmp/swagger-raw.json --definition KnowledgeOperLogVO
241
+ node .../extract-swagger.mjs --file /tmp/swagger-raw.json --definition KnowledgeOperLogVO
173
242
  ```
174
243
 
175
244
  找不到时会自动给出模糊匹配建议(最多 10 个)。
@@ -202,7 +271,11 @@ python3 .../extract_swagger.py --file /tmp/swagger-raw.json --definition Knowled
202
271
  }
203
272
  ```
204
273
 
205
- ### 二、服务端兜底路径(脚本不可用时)
274
+ ### 二、本地兜底路径(脚本不可用时)
275
+
276
+ 无 Node 也无 Python 时按 **2.A → 2.B** 顺序处理:默认走 2.A(裸 curl + read_file + AI 推理),只有 swagger 超过 ~200KB read_file 装不下时才升级到 2.B(MCP parse_swagger_json)。
277
+
278
+ #### 2.A 裸 curl + AI 直接推理(默认兜底)
206
279
 
207
280
  **步骤 1:调用服务端 api_tool**
208
281
 
@@ -243,20 +316,50 @@ curl -sS --connect-timeout 10 --max-time 30 \
243
316
  -H 'Cookie: <步骤上一步的 Cookie>' \
244
317
  -o /tmp/swagger-raw.json \
245
318
  '<apiDocsUrl>'
319
+
320
+ # 检查大小:> 200KB 跳到 2.B;否则继续步骤 4
321
+ ls -l /tmp/swagger-raw.json
322
+ ```
323
+
324
+ **步骤 4:read_file 进上下文,AI 直接阅读 JSON**
325
+
326
+ ```
327
+ read_file('/tmp/swagger-raw.json')
246
328
  ```
247
329
 
248
- **步骤 4:调用服务端解析**
330
+ 随后 AI 在对话上下文里**直接阅读** JSON,按以下路径检索:
331
+
332
+ 1. 找到目标 tag(`tags[]` 或 `paths.*.tags`),列出该 tag 下所有 `paths`
333
+ 2. 对感兴趣的接口,看 `parameters[].schema.$ref` / `responses.200.schema.$ref`(或 `originalRef`)
334
+ 3. 跳到 `definitions.<VOName>`,沿 `properties.*.$ref` 递归展开(一般 2-3 层够用)
335
+ 4. 注意 `allOf`/`oneOf`/`items` 嵌套和 `required` 数组
336
+
337
+ > **🚫 禁止在此步骤写任何临时脚本(awk/sed/jq/python 一次性脚本)切 JSON**。
338
+ > 历史反馈 #2026-06-11-catalog-tag 已经验证:`$ref` 双字段嵌套 + allOf 让正则方案漏字段或花括号失衡,
339
+ > 调试 3-5 分钟 ≫ 直接 read_file 进上下文人脑挑(19K tokens 中等规模 < 30 秒)。
340
+ > 真正的结构化解析交给脚本(A1)或 MCP(2.B),不要中间态。
341
+
342
+ **步骤 5:把整理好的 API 信息呈现给用户**
343
+
344
+ #### 2.B MCP 服务端解析(仅当 swagger > 200KB read_file 装不下时升级)
345
+
346
+ **触发条件**:步骤 3 的 `ls -l` 显示 swagger 超过 ~200KB,或 read_file 后明显超出上下文预算。
347
+
348
+ **步骤 1:调用服务端解析**
249
349
 
250
350
  ```
251
351
  api_tool(
252
352
  action="parse_swagger_json",
253
353
  swaggerJson=<读取 /tmp/swagger-raw.json 的完整内容>,
254
354
  swaggerSourceUrl="http://10.2.7.60:30194/ikmp-knowledge/swagger-ui/index.html",
255
- apiFilter=["tag"] // 可选,强烈推荐:按关键词过滤 + 自动裁剪 definitionsDetail
355
+ apiFilter=["tag"] // 必填,避免输出爆炸 + 触发 definitionsDetail 传递闭包裁剪
256
356
  )
257
357
  ```
258
358
 
259
- **步骤 5:返回解析结果**
359
+ > 此路径上下文成本翻倍(read_file 一次 + 整段 JSON 作为字符串参数再传一次),所以 `apiFilter` 必填。
360
+ > 若不知道按什么过滤,先回到 2.A 步骤 4 用 read_file 看一眼 `tags[]`/`paths.*.tags` 选 1-3 个关键词再调。
361
+
362
+ **步骤 2:返回解析结果**
260
363
 
261
364
  将返回结果直接呈现给用户。
262
365
 
@@ -314,16 +417,45 @@ curl -s -X POST 'http://10.2.7.60:30194/ikmp-knowledge/api/xxx' \
314
417
  ## ⚠️ 避坑指南(来自历史反馈)
315
418
 
316
419
  1. **禁止用 `curl | head/tail/jq/python` 对完整 Swagger JSON 做管道切片** —— curl 管道经常因下游 SIGPIPE 截断,导致 JSON 不闭合 / Python 解析失败 / 临时写花括号平衡脚本,是历史踩坑率最高的反模式。**必须用 `-o /tmp/xxx.json` 写文件**,再用脚本或 `parse_swagger_json` 处理。
317
- 2. **不要在本地手写 Python 切 Swagger** —— `extract_swagger.py` 已经做了和服务端 `parseApiDocs` 完全对齐的递归展开、传递闭包、required 标注、过滤裁剪。本地手写一个 mini 版只会丢字段或踩 `$ref`/`originalRef` 双字段陷阱。
420
+ 2. **不要在本地手写脚本切 Swagger** —— `extract-swagger.mjs`/`extract_swagger.py` 已经做了和服务端 `parseApiDocs` 完全对齐的递归展开、传递闭包、required 标注、过滤裁剪。本地手写一个 mini 版只会丢字段或踩 `$ref`/`originalRef` 双字段陷阱。
318
421
  3. **过滤优先用 `--filter`(脚本)或 `apiFilter`(服务端)** —— 不要先拿全量再让 AI 自己挑接口看,过滤后 `definitionsDetail` 会自动按传递闭包裁剪,输出量减少 70%+。
319
422
  4. **单 VO 查询用 `--definition`(脚本)或 `get_definition`(服务端)** —— 不要回头再拉一次全量。
423
+ 5. **JSON 解析报错先看脚本输出,再考虑手工修复** —— 两个脚本都内置八进制字面量自动修复(如 `[011,015]` → `[11,15]`,Java/旧网关常见)和错误诊断(HTML 跳转/截断/编码)。**不要再手写正则修 JSON**,浪费 30s 起步;如果脚本仍然报错,按提示原因(HTML/截断/真八进制以外的非法字符)针对性处理。
424
+ 6. **首选 Node 版(.mjs),无 Node 才用 Python 版(.py)** —— `extract-swagger.mjs` 跨平台单命令 `node ...`,绕开 Python 在 Windows 上 `python3` 不存在、`python` 版本不一的麻烦;扩展名 `.mjs` 强制 ESM,也不会撞上宿主 `package.json "type":"module"` 的 .cjs 强制要求。两脚本输出结构完全等价(仅 `definitionsDetail` 数组顺序可能不同,因 Python `set` vs JS `Set` 迭代规则差异——业务无感)。
425
+ 7. **重复拉同一份 Swagger 走缓存** —— 同 URL+cookie+auth 默认 10 分钟内命中 `<tmpdir>/swagger-cache/`,无需手动判断。如需强制最新数据用 `--no-cache`;如需调整 TTL 用 `--cache-ttl <秒>`(设为 0 关闭缓存)。两个脚本共用同一目录、同一 sha256 key 算法,Node 版写的缓存 Python 版也能读,反之亦然。
426
+
427
+ ## 内置容错与性能优化
428
+
429
+ `extract-swagger.mjs` / `extract_swagger.py` 内置以下能力,**无需 AI 在外层重复实现**:
430
+
431
+ | 能力 | 触发条件 | 行为 |
432
+ |------|----------|------|
433
+ | 八进制修复 | JSON 含 `[011,015]` 这类非标字面量 | 首次 `JSON.parse`/`json.loads` 失败后自动正则修复重试,stderr 打印 `[INFO] 自动修复八进制字面量后解析成功` |
434
+ | 错误诊断 | 任何 JSON 解析失败 | stderr 输出"错误行列 + 上下文 ±80 字符 + `^` 指向 + 常见原因"(HTML 跳转/截断/八进制) |
435
+ | TTL 缓存 | `--url` 模式默认开启 | `<tmpdir>/swagger-cache/<sha256(url+cookie+auth)截16位>.json`,meta 文件记录 cached_at + ttl + size;Node 版与 Python 版共用缓存目录 |
436
+ | 缓存绕过 | 命令行 `--no-cache` 或 `--cache-ttl 0` | 强制重新拉取 |
437
+
438
+ **典型场景**(以 Node 版为例,Python 版只需把命令头换成 `python3 .../extract_swagger.py`):
439
+ ```bash
440
+ # 首次拉取 → fetch + 写缓存
441
+ node .../extract-swagger.mjs --url '...' --out /tmp/r1.json
442
+ # stderr: [CACHE] 已写入 c57b1f88a49f0f7d (530 bytes)
443
+
444
+ # 5 分钟内二次拉取 → 命中
445
+ node .../extract-swagger.mjs --url '...' --out /tmp/r2.json
446
+ # stderr: [CACHE] 命中 c57b1f88a49f0f7d (age=287s, ttl=600s)
447
+
448
+ # Cookie 变了(如重新登录)→ key 自动变化,不会串
449
+ node .../extract-swagger.mjs --url '...' --cookie 'JSESSIONID=NEW' --out /tmp/r3.json
450
+ # stderr: [CACHE] 已写入 a93f...(新 key)
451
+ ```
320
452
 
321
453
  ## 快捷方式:AI 直接识别 10 段地址
322
454
 
323
455
  如果 AI 能识别出用户请求的 URL 中包含 `10.x.x.x` 主机地址,可以**直接跳过首次 api_tool 调用**,在本地执行脚本,减少一次无用的服务端请求:
324
456
 
325
457
  ```bash
326
- python3 ~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract_swagger.py \
458
+ node ~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/extract-swagger.mjs \
327
459
  --url 'http://10.x.x.x/<service>/v2/api-docs' \
328
460
  [--cookie '<JSESSIONID=...>'] \
329
461
  [--filter '<关键词>'] \
@@ -344,7 +476,7 @@ python3 ~/.qwen/extensions/szcd-component-helper/skills/local-api-tool/scripts/e
344
476
 
345
477
  1. **Cookie 传递**:鉴权步骤获取的 Cookie 通过 `--cookie` 传给脚本,或通过 `-H 'Cookie: xxx'` 传给 curl
346
478
  2. **无密码场景**:如果用户未提供密码,跳过鉴权步骤,直接获取文档
347
- 3. **JSON 格式**:脚本/curl 获取的 API 文档必须是有效的 Swagger JSON,否则 `extract_swagger.py` / `parse_swagger_json` 会报错(脚本会打印预览前 200 字符方便排查)
479
+ 3. **JSON 格式**:脚本/curl 获取的 API 文档必须是有效的 Swagger JSON。八进制字面量(`[011,015]` 类)会被自动修复;其他非法格式(HTML 跳转/截断)脚本会输出错误位置 ±80 字符上下文 + 常见原因提示,按提示排查即可
348
480
  4. **超时处理**:脚本和 curl 都已内置 `--connect-timeout 10 --max-time 30`,可通过 `--timeout` 调整
349
481
  5. **错误处理**:脚本退出码 1=参数错误 / 2=网络IO / 3=解析;如果某步骤失败,将错误信息返回给用户,不要继续后续步骤
350
482
  6. **自动执行**:AI 识别 10.x.x.x 或 `reason: "LOCAL_NETWORK_UNREACHABLE"` 后应**自动**通过 `run_command` 执行脚本,无需让用户手动复制粘贴命令