@szc-ft/mcp-szcd-client 0.35.0 → 0.39.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 (117) hide show
  1. package/agents/build.js +59 -1
  2. package/agents/platforms.json +7 -0
  3. package/agents/szcd-component-expert.qoder.md +2 -0
  4. package/opencode-extension/opencode.json +9 -0
  5. package/opencode-extension/skills/local-browser-test/SKILL.md +186 -18
  6. package/opencode-extension/skills/local-browser-test/config.example.yaml +51 -0
  7. package/opencode-extension/skills/local-browser-test/lib/api-call-executor.js +251 -0
  8. package/opencode-extension/skills/local-browser-test/lib/browser-engine.js +95 -21
  9. package/opencode-extension/skills/local-browser-test/lib/config-loader.js +104 -0
  10. package/opencode-extension/skills/local-browser-test/lib/console-archiver.js +100 -0
  11. package/opencode-extension/skills/local-browser-test/lib/global-setup.js +73 -0
  12. package/opencode-extension/skills/local-browser-test/lib/har-collector.js +124 -0
  13. package/opencode-extension/skills/local-browser-test/lib/html-reporter.js +425 -0
  14. package/opencode-extension/skills/local-browser-test/lib/projects.js +46 -0
  15. package/opencode-extension/skills/local-browser-test/lib/raw-script-helpers.js +93 -0
  16. package/opencode-extension/skills/local-browser-test/lib/redact.js +94 -0
  17. package/opencode-extension/skills/local-browser-test/lib/report-retain.js +84 -0
  18. package/opencode-extension/skills/local-browser-test/lib/retry-policy.js +160 -0
  19. package/opencode-extension/skills/local-browser-test/lib/storage-state.js +111 -0
  20. package/opencode-extension/skills/local-browser-test/lib/tag-runner.js +103 -0
  21. package/opencode-extension/skills/local-browser-test/lib/test-plan.js +2 -0
  22. package/opencode-extension/skills/local-browser-test/lib/trace-collector.js +94 -0
  23. package/opencode-extension/skills/local-browser-test/lib/trend-aggregator.js +97 -0
  24. package/opencode-extension/skills/local-browser-test/lib/workspace-init.js +178 -0
  25. package/opencode-extension/skills/local-browser-test/local-browser-executor.js +264 -4
  26. package/opencode-extension/skills/local-browser-test/tests/integration/pr-01.test.js +254 -0
  27. package/opencode-extension/skills/local-browser-test/tests/integration/pr-02.test.js +263 -0
  28. package/opencode-extension/skills/local-browser-test/tests/integration/pr-03.test.js +308 -0
  29. package/opencode-extension/skills/local-browser-test/tests/integration/pr-04.test.js +168 -0
  30. package/opencode-extension/skills/local-browser-test/tests/integration/pr-05.test.js +183 -0
  31. package/opencode-extension/skills/local-browser-test/tests/integration/pr-06.test.js +252 -0
  32. package/opencode-extension/skills/local-browser-test/tests/integration/pr-07.test.js +168 -0
  33. package/opencode-extension/skills/local-browser-test/tests/integration/pr-08.test.js +293 -0
  34. package/opencode-extension/skills/shadcn-design-to-code/SKILL.md +352 -0
  35. package/opencode-extension/skills/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
  36. package/opencode-extension/skills/shadcn-design-to-code/references/install-pitfalls.md +526 -0
  37. package/opencode-extension/skills/shadcn-design-to-code/references/layouts.md +551 -0
  38. package/opencode-extension/skills/shadcn-design-to-code/references/sketch-mapping.md +247 -0
  39. package/opencode-extension/skills/shadcn-design-to-code/references/tokens.md +534 -0
  40. package/opencode-extension/skills/szcd-upload-zip/SKILL.md +45 -0
  41. package/package.json +70 -70
  42. package/qwen-extension/qwen-extension.json +10 -1
  43. package/qwen-extension/skills/local-browser-test/SKILL.md +186 -18
  44. package/qwen-extension/skills/local-browser-test/config.example.yaml +51 -0
  45. package/qwen-extension/skills/local-browser-test/lib/api-call-executor.js +251 -0
  46. package/qwen-extension/skills/local-browser-test/lib/browser-engine.js +95 -21
  47. package/qwen-extension/skills/local-browser-test/lib/config-loader.js +104 -0
  48. package/qwen-extension/skills/local-browser-test/lib/console-archiver.js +100 -0
  49. package/qwen-extension/skills/local-browser-test/lib/global-setup.js +73 -0
  50. package/qwen-extension/skills/local-browser-test/lib/har-collector.js +124 -0
  51. package/qwen-extension/skills/local-browser-test/lib/html-reporter.js +425 -0
  52. package/qwen-extension/skills/local-browser-test/lib/projects.js +46 -0
  53. package/qwen-extension/skills/local-browser-test/lib/raw-script-helpers.js +93 -0
  54. package/qwen-extension/skills/local-browser-test/lib/redact.js +94 -0
  55. package/qwen-extension/skills/local-browser-test/lib/report-retain.js +84 -0
  56. package/qwen-extension/skills/local-browser-test/lib/retry-policy.js +160 -0
  57. package/qwen-extension/skills/local-browser-test/lib/storage-state.js +111 -0
  58. package/qwen-extension/skills/local-browser-test/lib/tag-runner.js +103 -0
  59. package/qwen-extension/skills/local-browser-test/lib/test-plan.js +2 -0
  60. package/qwen-extension/skills/local-browser-test/lib/trace-collector.js +94 -0
  61. package/qwen-extension/skills/local-browser-test/lib/trend-aggregator.js +97 -0
  62. package/qwen-extension/skills/local-browser-test/lib/workspace-init.js +178 -0
  63. package/qwen-extension/skills/local-browser-test/local-browser-executor.js +264 -4
  64. package/qwen-extension/skills/local-browser-test/tests/integration/pr-01.test.js +254 -0
  65. package/qwen-extension/skills/local-browser-test/tests/integration/pr-02.test.js +263 -0
  66. package/qwen-extension/skills/local-browser-test/tests/integration/pr-03.test.js +308 -0
  67. package/qwen-extension/skills/local-browser-test/tests/integration/pr-04.test.js +168 -0
  68. package/qwen-extension/skills/local-browser-test/tests/integration/pr-05.test.js +183 -0
  69. package/qwen-extension/skills/local-browser-test/tests/integration/pr-06.test.js +252 -0
  70. package/qwen-extension/skills/local-browser-test/tests/integration/pr-07.test.js +168 -0
  71. package/qwen-extension/skills/local-browser-test/tests/integration/pr-08.test.js +293 -0
  72. package/qwen-extension/skills/shadcn-design-to-code/SKILL.md +352 -0
  73. package/qwen-extension/skills/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
  74. package/qwen-extension/skills/shadcn-design-to-code/references/install-pitfalls.md +526 -0
  75. package/qwen-extension/skills/shadcn-design-to-code/references/layouts.md +551 -0
  76. package/qwen-extension/skills/shadcn-design-to-code/references/sketch-mapping.md +247 -0
  77. package/qwen-extension/skills/shadcn-design-to-code/references/tokens.md +534 -0
  78. package/qwen-extension/skills/szcd-upload-zip/SKILL.md +45 -0
  79. package/scripts/lib/common.js +7 -0
  80. package/standard-skill/local-browser-test/SKILL.md +186 -18
  81. package/standard-skill/local-browser-test/config.example.yaml +51 -0
  82. package/standard-skill/local-browser-test/lib/api-call-executor.js +251 -0
  83. package/standard-skill/local-browser-test/lib/browser-engine.js +95 -21
  84. package/standard-skill/local-browser-test/lib/config-loader.js +104 -0
  85. package/standard-skill/local-browser-test/lib/console-archiver.js +100 -0
  86. package/standard-skill/local-browser-test/lib/global-setup.js +73 -0
  87. package/standard-skill/local-browser-test/lib/har-collector.js +124 -0
  88. package/standard-skill/local-browser-test/lib/html-reporter.js +425 -0
  89. package/standard-skill/local-browser-test/lib/projects.js +46 -0
  90. package/standard-skill/local-browser-test/lib/raw-script-helpers.js +93 -0
  91. package/standard-skill/local-browser-test/lib/redact.js +94 -0
  92. package/standard-skill/local-browser-test/lib/report-retain.js +84 -0
  93. package/standard-skill/local-browser-test/lib/retry-policy.js +160 -0
  94. package/standard-skill/local-browser-test/lib/storage-state.js +111 -0
  95. package/standard-skill/local-browser-test/lib/tag-runner.js +103 -0
  96. package/standard-skill/local-browser-test/lib/test-plan.js +2 -0
  97. package/standard-skill/local-browser-test/lib/trace-collector.js +94 -0
  98. package/standard-skill/local-browser-test/lib/trend-aggregator.js +97 -0
  99. package/standard-skill/local-browser-test/lib/workspace-init.js +178 -0
  100. package/standard-skill/local-browser-test/local-browser-executor.js +264 -4
  101. package/standard-skill/local-browser-test/tests/integration/pr-01.test.js +254 -0
  102. package/standard-skill/local-browser-test/tests/integration/pr-02.test.js +263 -0
  103. package/standard-skill/local-browser-test/tests/integration/pr-03.test.js +308 -0
  104. package/standard-skill/local-browser-test/tests/integration/pr-04.test.js +168 -0
  105. package/standard-skill/local-browser-test/tests/integration/pr-05.test.js +183 -0
  106. package/standard-skill/local-browser-test/tests/integration/pr-06.test.js +252 -0
  107. package/standard-skill/local-browser-test/tests/integration/pr-07.test.js +168 -0
  108. package/standard-skill/local-browser-test/tests/integration/pr-08.test.js +293 -0
  109. package/standard-skill/shadcn-design-to-code/SKILL.md +352 -0
  110. package/standard-skill/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
  111. package/standard-skill/shadcn-design-to-code/references/install-pitfalls.md +526 -0
  112. package/standard-skill/shadcn-design-to-code/references/layouts.md +551 -0
  113. package/standard-skill/shadcn-design-to-code/references/sketch-mapping.md +247 -0
  114. package/standard-skill/shadcn-design-to-code/references/tokens.md +534 -0
  115. package/standard-skill/szcd-upload-zip/SKILL.md +45 -0
  116. package/opencode-extension/skills/local-browser-test/local-browser-executor.cjs +0 -742
  117. package/qwen-extension/skills/local-browser-test/local-browser-executor.cjs +0 -742
package/agents/build.js CHANGED
@@ -346,6 +346,11 @@ function build() {
346
346
  const srcSkillPath = path.join(srcSkillDir, "SKILL.md");
347
347
  const destSkillDir = path.join(qwenExtDir, "skills", skillDir);
348
348
  if (fs.existsSync(srcSkillPath)) {
349
+ // PR #0 v3: 先 rm 旧副本,避免脏数据残留(cjs 等已废弃文件)
350
+ if (fs.existsSync(destSkillDir)) {
351
+ fs.rmSync(destSkillDir, { recursive: true, force: true });
352
+ console.log(` ✓ Cleaned stale ${skillDir}/`);
353
+ }
349
354
  copyDirRecursive(srcSkillDir, destSkillDir);
350
355
  console.log(` ✓ Synced skill to qwen-extension/skills/${skillDir}/`);
351
356
  }
@@ -376,6 +381,10 @@ function build() {
376
381
  const srcSkillPath = path.join(srcSkillDir, "SKILL.md");
377
382
  const destSkillDir = path.join(opencodeExtDir, "skills", skillDir);
378
383
  if (fs.existsSync(srcSkillPath)) {
384
+ // PR #0 v3: 先 rm 旧副本(与 5a 同步逻辑保持一致)
385
+ if (fs.existsSync(destSkillDir)) {
386
+ fs.rmSync(destSkillDir, { recursive: true, force: true });
387
+ }
379
388
  copyDirRecursive(srcSkillDir, destSkillDir);
380
389
  }
381
390
  }
@@ -496,8 +505,57 @@ function build() {
496
505
  console.log("\n✅ Agent build complete!");
497
506
  }
498
507
 
508
+ // ★ PR #0 v3 修复:build 锁(防止并发 build 时 ENOTEMPTY)
509
+ // 跨进程互斥:第一个 build 写 .build.lock,后续 build 等到锁释放
510
+ const lockFile = path.join(process.cwd(), ".build.lock");
511
+ const lockMaxWait = 30000; // 30s 超时
512
+ const lockPollInterval = 100; // 100ms 检查一次
513
+ const lockStaleAge = 60000; // 60s 视为 stale 锁
514
+ async function acquireBuildLock() {
515
+ const start = Date.now();
516
+ while (Date.now() - start < lockMaxWait) {
517
+ try {
518
+ const fd = fs.openSync(lockFile, "wx"); // 独占创建(原子操作)
519
+ fs.writeSync(fd, String(process.pid));
520
+ return fd;
521
+ } catch (e) {
522
+ if (e.code === "EEXIST") {
523
+ // 锁存在 → 检查是否 stale
524
+ try {
525
+ const stat = fs.statSync(lockFile);
526
+ if (Date.now() - stat.mtimeMs > lockStaleAge) {
527
+ console.log(` ⚠ 锁文件陈旧 (>${lockStaleAge/1000}s),强制删除`);
528
+ try { fs.unlinkSync(lockFile); } catch {}
529
+ continue;
530
+ }
531
+ } catch {}
532
+ // 等待
533
+ await new Promise((r) => setTimeout(r, lockPollInterval));
534
+ } else {
535
+ throw e;
536
+ }
537
+ }
538
+ }
539
+ return null; // 超时
540
+ }
541
+ function releaseBuildLock(fd) {
542
+ if (fd) {
543
+ try { fs.closeSync(fd); } catch {}
544
+ try { fs.unlinkSync(lockFile); } catch {}
545
+ }
546
+ }
547
+
499
548
  try {
500
- build();
549
+ const lockFd = await acquireBuildLock();
550
+ if (!lockFd) {
551
+ process.stderr.write(`❌ build 锁获取超时 (${lockMaxWait}ms)\n`);
552
+ process.exit(2);
553
+ }
554
+ try {
555
+ build();
556
+ } finally {
557
+ releaseBuildLock(lockFd);
558
+ }
501
559
  } catch (error) {
502
560
  console.error("❌ Build failed:", error.message);
503
561
  console.error(error.stack);
@@ -6,6 +6,13 @@
6
6
  "type": "stdio",
7
7
  "command": "sketch-mcp-server",
8
8
  "args": []
9
+ },
10
+ {
11
+ "name": "shadcn",
12
+ "type": "stdio",
13
+ "command": "npx",
14
+ "args": ["-y", "shadcn@latest", "mcp"],
15
+ "description": "shadcn/ui 官方 stdio MCP(registry 搜索/查看/add 命令)。需项目根目录有 components.json,否则调 list_items_in_registries 报 'No registries are configured'。"
9
16
  }
10
17
  ],
11
18
  "platforms": {
@@ -15,12 +15,14 @@ tools:
15
15
  skills:
16
16
  - local-api-tool
17
17
  - local-browser-test
18
+ - shadcn-design-to-code
18
19
  - szcd-component-helper
19
20
  - szcd-design-to-code
20
21
  - szcd-upload-zip
21
22
  mcpServers:
22
23
  - szcd-component-helper
23
24
  - sketch-mcp-server
25
+ - shadcn
24
26
  ---
25
27
 
26
28
  # szcd-component-expert: szcd 组件库专家
@@ -10,6 +10,15 @@
10
10
  "command": [
11
11
  "sketch-mcp-server"
12
12
  ]
13
+ },
14
+ "shadcn": {
15
+ "type": "local",
16
+ "command": [
17
+ "npx",
18
+ "-y",
19
+ "shadcn@latest",
20
+ "mcp"
21
+ ]
13
22
  }
14
23
  }
15
24
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: local-browser-test
3
- description: 在用户真实 Chrome 中执行自动化测试,覆盖 P0 用例批量执行、深度表单填写、微前端测试、长任务生命周期、设计稿对比、JS 错误诊断。通过 puppeteer-core 连接 CDP,天然继承登录态/微前端环境。
3
+ description: 在用户真实 Chrome 中执行自动化测试,覆盖测试用例批量执行、深度表单填写、微前端测试、长任务生命周期、设计稿对比、JS 错误诊断。通过 puppeteer-core 连接 CDP,天然继承登录态/微前端环境。
4
4
  compatibility:
5
5
  tools:
6
6
  - run_command
@@ -72,7 +72,7 @@ export EXECUTOR="$SKILL_DIR/local-browser-executor.js"
72
72
 
73
73
  ## 🎯 三种典型任务(场景驱动)
74
74
 
75
- ### 任务 A:P0 用例批量执行(首选)
75
+ ### 任务 A:测试用例批量执行(首选)
76
76
 
77
77
  用户说"执行 P0 用例 / 批量测试 / 生命周期测试"时,**不要写 ad-hoc puppeteer 脚本**,构造 testCase JSON:
78
78
 
@@ -144,7 +144,7 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action api-capture --
144
144
 
145
145
  ```
146
146
  Agent / SKILL.md
147
- → testCase JSON ← P0 用例首选入口
147
+ → testCase JSON ← 测试用例首选入口
148
148
  → $SKILL_DIR/local-browser-executor.js ($EXECUTOR)
149
149
  → $SKILL_DIR/lib/browser-engine.js (executeTestCase + Ant 适配 + expect)
150
150
  → $SKILL_DIR/lib/ant-adapter.js (Select/TreeSelect/Input/Radio/...)
@@ -672,14 +672,32 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
672
672
  - `--expect-result-contains <text>`:结果 JSON 包含指定文本
673
673
 
674
674
  ### 执行测试计划 JSON(ES Module 版)
675
+
676
+ **⚠️ 禁用 `--action plan`(该分支提前 `process.exit`,不会生成 HTML 报告)**
677
+ 直接用 `--plan-file`:
678
+
675
679
  ```bash
676
680
  NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
677
- --action plan \
678
681
  --plan-file /tmp/test-plan.json \
679
- --output /tmp/browser-test-result.json
682
+ --config /path/to/config.yaml
683
+ ```
684
+
685
+ 工作区默认在 CWD 的 `.lbt/`,无需手动传 `--workspace`:
686
+
687
+ ```
688
+ .lbt/
689
+ ├── config.yaml ← 项目配置
690
+ ├── reports/ ← HTML 报告(趋势图 + 过滤 + 详情页 + 截图)
691
+ │ ├── index.html
692
+ │ └── runs/<id>/
693
+ ├── tmp/ ← 执行过程临时文件(截图等)
694
+ ├── auth/ ← 保存的登录态
695
+ └── plans/ ← 回归脚本
680
696
  ```
681
697
 
682
- `--action plan` 使用 ES Module 执行器运行测试计划 JSON,支持全部步骤类型(包括 evaluate、antSelect 等)。与 `--action test-case` 的区别:plan 不走 dataset 循环,适合单次执行。
698
+ 不传 `--workspace` 时默认 `cwd/.lbt`,也可用 `--workspace <dir>` 指定其他目录。
699
+
700
+ 与 `--action test-case` 的区别:plan 不走 dataset 循环,适合单次执行。
683
701
 
684
702
  ### 快速诊断(JS错误 + 网络 + 资源 + iframe)
685
703
  ```bash
@@ -726,12 +744,15 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
726
744
  ### 可选参数
727
745
 
728
746
  - `--cdp-url http://localhost:9222`:CDP 地址(默认 9222)
729
- - `--url <url>`:目标页面 URL(不存在则导航)
747
+ - `--page-url <url>`:目标页面 URL(不存在则导航)
730
748
  - `--url-contains <keyword>`:按关键词查找已打开的页面
731
749
  - `--frame-url-contains <keyword>`:observe 时按 iframe URL 片段匹配目标 frame
732
750
  - `--frame-content-contains <keyword>`:observe 时按 iframe 文本内容匹配目标 frame
733
751
  - `--frame-index <n>`:observe 时按 frame 下标选择目标 frame
734
- - `--wait <ms>`:额外等待时间(默认 5000ms,微前端建议 15000)
752
+ - `--plan-file <path>`:测试计划 JSON 文件路径
753
+ - `--config <path>`:配置文件路径(如 config.yaml)
754
+ - `--workspace <dir>`:工作区目录,默认 `cwd/.lbt`。存放 `reports/`(HTML 报告)、`tmp/`(临时文件)、`auth/`(登录态)、`plans/`(回归脚本)
755
+ - `--wait <ms>`:额外等待时间(默认因 action 而异:api-capture 15000,其余 5000)
735
756
  - `--output <path>`:JSON 结果输出路径
736
757
  - `--output-dir <dir>`:截图/产物目录
737
758
  - `--filename <name>`:截图文件名
@@ -741,12 +762,41 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
741
762
  - `--click <selector>`:点击元素(支持 text=/role=/~)
742
763
  - `--type <text>`:输入文本
743
764
  - `--hover <selector>`:悬停元素
744
- - `--scroll <up|down|left|right>`:滚动方向
745
- - `--scroll-amount <px>`:滚动距离(默认 300)
746
- - `--screenshot-after`:交互后自动截图
765
+ - `--expression <js>`:evaluate 执行的 JS 表达式(需 IIFE 包裹)
766
+ - `--fail-on-falsy`:evaluate 结果为 falsy 时标记 FAIL
767
+ - `--expect-result-contains <text>`:evaluate 结果包含指定文本
768
+ - `--include-response-body`:api-capture 包含响应体
769
+ - `--include-all-targets`:api-capture 监听全部 target(含非 frame 的 worker/extension)
770
+ - `--reload`:api-capture 前重载页面
771
+ - `--url-pattern <pattern>`:api-capture 按 URL 过滤
772
+ - `--method <method>`:api-capture 按 HTTP 方法过滤
773
+ - `--capture-file <path>`:assert-api 读取的捕获结果文件
774
+ - `--max-failed <n>`:assert-api 允许的最大失败请求数
775
+ - `--forbid-status-gte <code>`:assert-api 禁止的状态码阈值
776
+ - `--required-url <pattern>`:assert-api 必需出现的 URL 模式
777
+ - `--include-request-body`:api-capture 包含请求体
778
+ - `--no-request-body`:api-capture 排除请求体
779
+ - `--target-url-contains <text>`:api-capture 目标 frame URL 包含
747
780
  - `--interval <ms>`:watch 模式间隔(默认 5000)
748
781
  - `--max-rounds <n>`:watch 最大轮数(默认 60)
749
782
  - `--allow-launch`:根执行器 action 模式允许无 CDP 时启动 headless Chrome;默认不启用,优先保障真实用户浏览器链路
783
+ - `--ci`:CI 模式(自动设置 screenshot=always, video=on, trace=on, retries=2)
784
+ - `--retries <n>`:用例级重试次数
785
+ - `--flaky-threshold <n>`:Flaky 测试阈值
786
+ - `--screenshot <always|only-on-failure>`:截图策略
787
+ - `--video <on|off|only-on-failure>`:录像策略
788
+ - `--trace <on|off>`:trace 录制
789
+ - `--record-har`:录制 HAR
790
+ - `--storage-state <path>`:认证存储状态文件路径(用于 global-setup)
791
+ - `--global-setup`:全局设置模式(保存登录态)
792
+ - `--init-force`:强制初始化工作区
793
+ - `--project <name>`:多环境项目选择
794
+ - `--env <name>`:环境选择
795
+ - `--tags <tag>`:按 tag 筛选用例
796
+ - `--exclude-tags <tag>`:排除 tag
797
+ - `--tag-mode <any|all>`:tag 匹配模式
798
+ - `--no-retry-actions`:禁用写操作重试
799
+ - `--no-reload`:diagnose 跳过页面重载
750
800
 
751
801
  ## AI 语义断言(aiAssert)
752
802
 
@@ -822,24 +872,47 @@ cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com --@
822
872
  - passed = total:全部通过
823
873
  - 有失败步骤:根据 `step` + `error` 字段定位代码问题
824
874
 
825
- ### 结果文件结构
875
+ ### HTML 报告(推荐)
876
+
877
+ 传 `--workspace <dir>` 时自动生成 Playwright 风格 HTML 报告:
878
+
879
+ ```
880
+ <workspace>/reports/
881
+ ├── index.html ← 入口:趋势图 + 过滤 + 运行列表
882
+ ├── trend.json ← 按日聚合数据
883
+ └── runs/
884
+ └── <timestamp>-pid<N>/
885
+ ├── index.html ← 详情页:步骤表格、截图、错误展开
886
+ ├── result.json ← 原始结果(脱敏)
887
+ └── screenshots/ ← 截图副本
888
+ ```
889
+
890
+ 入口支持交互过滤(状态 + 关键词搜索)、echarts 趋势图、详情页"返回列表"导航、截图展示。
891
+
892
+ ### 结果 JSON 结构(stdout)
826
893
  ```json
827
894
  {
828
895
  "planId": "xxx",
896
+ "description": "...",
829
897
  "mode": "connect",
898
+ "startTime": "2026-07-01T10:00:00.000Z",
899
+ "duration": "1583",
830
900
  "steps": [
831
- { "index": 1, "step": "navigate", "status": "PASS", "duration": 1200, ... },
832
- { "index": 2, "step": "screenshot", "status": "PASS", "duration": 500, ... },
833
- { "index": 3, "step": "compare", "status": "PASS", "fidelity": 94.7, ... }
901
+ { "index": 1, "step": "navigate", "status": "PASS", "duration": "1200", ... },
902
+ { "index": 2, "step": "screenshot", "status": "PASS", "duration": "500",
903
+ "filepath": "/tmp/.../screenshot.png", ... }
834
904
  ],
835
905
  "summary": {
836
- "total": 5, "passed": 4, "failed": 1,
837
- "fidelity": 94.7
906
+ "total": "9", "passed": "9", "failed": "0",
907
+ "fidelity": null
838
908
  },
909
+ "browserInfo": { "mode": "connect", "cdpUrl": "...", "currentUrl": "...", "totalPages": 6 },
839
910
  "outputDir": "/tmp/browser-test-xxx"
840
911
  }
841
912
  ```
842
913
 
914
+ 注意:`startTime`(非 `startedAt`)、`duration`(非 `durationMs`)。HTML 报告器内部会把 `startTime` → `startedAt`、`duration` → `durationMs`、`summary.*` → `stats.*` 做字段归一化。
915
+
843
916
  ## 批量动态菜单扫描(runTask)
844
917
 
845
918
  适用场景:测试系统有 N 条动态路由(来自后端菜单接口),要逐条点击触发并验证接口是否返回 200。6/18 反馈中,AI 最终用 130 余条 `act --menu-path` + `api-capture` 手工组合走完 129 条菜单——`runTask` 把这套流水固化为单步骤。
@@ -966,7 +1039,7 @@ cat > /tmp/login.json <<EOF
966
1039
  EOF
967
1040
 
968
1041
  # 3. 执行登录
969
- NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action plan --plan-file /tmp/login.json --url-contains "platform.aicityos.com" --wait 8000
1042
+ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --plan-file /tmp/login.json --config /path/to/config.yaml
970
1043
  ```
971
1044
 
972
1045
  **LLM 处理流程:**
@@ -1014,6 +1087,10 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action plan --plan-fi
1014
1087
  - ❌ **v20 格式**(生产环境,App-Bound Encryption):需 Python `pywin32` + `cryptography` 方案
1015
1088
  - ⚠️ Windows 专属(DPAPI),macOS/Linux 需 Keychain/KWallet 方案
1016
1089
 
1090
+ ## 已知技术债
1091
+
1092
+ - **代码重复**:`normalizeForReport`(executor.js)与 `normalizeRunResult`(html-reporter.js)是相同字段归一化逻辑的两份拷贝。修改任何字段映射时必须同步两处。
1093
+
1017
1094
  ## 反模式备忘(来自 6/17 + 6/18 实战反馈)
1018
1095
 
1019
1096
  | 错误做法 | 正确做法 | 原因 |
@@ -1307,3 +1384,94 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action test-case --te
1307
1384
  - ❌ 单元测试 / 组件级测试 → Vitest / Jest / RTL
1308
1385
  - ❌ 视觉回归全量对比(无 baseline 设计稿)→ Percy / Chromatic
1309
1386
  - ✅ 真实浏览器内的功能验证、API 时序捕获、设计稿单图对比、动态菜单批量扫描
1387
+
1388
+ ## rawScript 可用 API(PR #4 v3)
1389
+
1390
+ `rawScript` 步骤支持两种模式:
1391
+
1392
+ ### 模式 A:inline code
1393
+ ```json
1394
+ { "type": "rawScript", "code": "return { passed: document.title.includes('登录') }" }
1395
+ ```
1396
+
1397
+ ### 模式 B:codeFile(PR #4 新增)
1398
+ ```json
1399
+ { "type": "rawScript", "codeFile": ".lbt/scripts/check-title.js" }
1400
+ ```
1401
+ - 相对路径相对 `process.cwd()`
1402
+ - 文件不存在 → 明确报错(指出绝对路径)
1403
+ - 文件读失败 → 明确报错
1404
+
1405
+ ### rawScript 函数签名
1406
+
1407
+ ```js
1408
+ async (page, frame, params, browser) => {
1409
+ // page: puppeteer Page 对象
1410
+ // frame: PuppeteerFrame(iframe 上下文)
1411
+ // params: step.params 对象
1412
+ // browser: puppeteer Browser 对象
1413
+ return { passed: true, ... }; // 或 { passed: false, reason: '...' }
1414
+ }
1415
+ ```
1416
+
1417
+ ### ✅ 浏览器侧可用 API
1418
+
1419
+ | 类别 | API |
1420
+ |------|-----|
1421
+ | DOM | `document.*` / `window.*` / `frame.document` |
1422
+ | 存储 | `localStorage.*` / `sessionStorage.*` / `cookieStore` |
1423
+ | 网络 | `fetch()` / `XMLHttpRequest` / `WebSocket` |
1424
+ | JS 内置 | `JSON` / `Date` / `Math` / `Array` / `Object` / `Promise` / `async/await` |
1425
+ | 浏览器事件 | `MouseEvent` / `KeyboardEvent` / `CustomEvent` |
1426
+ | puppeteer | `page.*`(evaluate / click / type / waitForSelector 等)|
1427
+
1428
+ ### ❌ Node 侧 API 不可用(浏览器无)
1429
+
1430
+ | 错误信息 | 原因 | 浏览器侧替代 |
1431
+ |---------|------|------------|
1432
+ | `TypeError: require is not a function` | 浏览器无 `require` | `await fetch()` / `page.evaluate(() => ...)` |
1433
+ | `ReferenceError: __dirname is not defined` | 浏览器无 Node 全局 | `window.location.origin` / `frame.url()` |
1434
+ | `ReferenceError: process is not defined` | 浏览器无 `process` | `localStorage` / `URL` 替代 |
1435
+ | `ReferenceError: Buffer is not defined` | 浏览器无 Node `Buffer` | `Uint8Array` / `TextEncoder` |
1436
+ | `TypeError: require.main is not a function` | 浏览器无 `require.main` | 不需要 |
1437
+
1438
+ **★ PR #4 修复**:rawScript 执行器会在跑前**静态扫描**代码,发现 Node-only API 时**立即 fail**,错误信息包含"详见 SKILL.md > ## rawScript 可用 API",避免用户踩坑。
1439
+
1440
+ ### 常见错误速查
1441
+
1442
+ | 错误 | 修复 |
1443
+ |------|------|
1444
+ | `codeFile 不存在: /path/to/file` | 检查路径或用 inline `code` 替代 |
1445
+ | `rawScript 检测到 Node-only API: require('fs')` | 改用 `page.evaluate(() => ...)` 走浏览器 |
1446
+ | `script compile error: Unexpected token` | 检查 `code` 字段 JSON 转义(`"` → `\"`) |
1447
+ | `script execution error: ...` | 看具体堆栈;可能是 page 已 close 或网络超时 |
1448
+
1449
+ ### 示例:检查页面 title
1450
+
1451
+ ```js
1452
+ // 模式 A:inline
1453
+ { "type": "rawScript", "code": "return { passed: document.title.includes('数据集目录'), title: document.title }" }
1454
+
1455
+ // 模式 B:codeFile
1456
+ { "type": "rawScript", "codeFile": ".lbt/scripts/check-dataset-title.js" }
1457
+ // .lbt/scripts/check-dataset-title.js 内容:
1458
+ // return { passed: document.title.includes('数据集目录'), title: document.title };
1459
+ ```
1460
+
1461
+ ### 示例:拿所有按钮文本
1462
+
1463
+ ```js
1464
+ {
1465
+ "type": "rawScript",
1466
+ "code": "return { passed: true, buttons: [...document.querySelectorAll('button')].map(b => b.textContent.trim()) }"
1467
+ }
1468
+ ```
1469
+
1470
+ ### 示例:调浏览器 fetch 验证 API
1471
+
1472
+ ```js
1473
+ {
1474
+ "type": "rawScript",
1475
+ "code": "const r = await fetch('/api/health'); const j = await r.json(); return { passed: j.status === 'ok', data: j }"
1476
+ }
1477
+ ```
@@ -0,0 +1,51 @@
1
+ # local-browser-test 配置示例
2
+ # 复制为 .lbt/config.yaml 后填值(config.yaml 已在 .gitignore 内)
3
+ #
4
+ # 占位符 ${ENV_VAR} 走 process.env,CI 推荐用:
5
+ # export LBT_TOKEN=xxx
6
+ # export LBT_PASSWORD=xxx
7
+
8
+ # 应用基础配置
9
+ app:
10
+ baseURL: http://10.2.7.60:30020
11
+ timeout: 30000
12
+
13
+ # 鉴权(强烈建议用环境变量,避免明文)
14
+ auth:
15
+ type: bearer # bearer | cookie | oauth2
16
+ token: ${LBT_TOKEN} # 走环境变量
17
+ # password: ${LBT_PASSWORD}
18
+ # username: ${LBT_USERNAME}
19
+ # verifyCode: ${LBT_VERIFY_CODE}
20
+ storageState: null # 可选:复用 .lbt/auth/<project>.json
21
+
22
+ # 多环境配置(配合 --project <name> 切换)
23
+ projects:
24
+ - name: corpus-test
25
+ baseURL: http://119.0.87.158
26
+ auth:
27
+ type: bearer
28
+ token: ${LBT_TOKEN}
29
+ - name: corpus-stg
30
+ baseURL: http://10.2.7.60:30020
31
+ auth:
32
+ type: bearer
33
+ token: ${LBT_TOKEN}
34
+ - name: corpus-prod
35
+ baseURL: http://172.16.0.172:30010
36
+ auth:
37
+ type: bearer
38
+ token: ${LBT_TOKEN}
39
+
40
+ # 重试策略
41
+ retry:
42
+ maxRetries: 0 # 用例级(CI 跑 2 次)
43
+ maxRetriesPerStep: 2 # 步骤级(验证码过期等)
44
+ flakyThreshold: 5 # 5 次中 1 挂 4 过 → flaky
45
+ noRetryActions: true # 写操作(删除/提交/上线)永不重试
46
+
47
+ # 报告(v4 Playwright 风格)
48
+ report:
49
+ retainRuns: 30 # 保留最近 30 次 run
50
+ retainDays: 7 # 保留最近 7 天
51
+ retainFailedForever: true # 失败 run 永久保留