@hupan56/wlkj 3.0.0 → 3.1.2

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 (237) hide show
  1. package/bin/cli.js +1435 -1373
  2. package/package.json +1 -1
  3. package/templates/qoder/commands/optional/wl-insight.md +6 -4
  4. package/templates/qoder/commands/optional/wl-report.md +20 -39
  5. package/templates/qoder/commands/optional/wl-status.md +4 -4
  6. package/templates/qoder/commands/wl-code.md +2 -2
  7. package/templates/qoder/commands/wl-design.md +5 -5
  8. package/templates/qoder/commands/wl-init.md +3 -3
  9. package/templates/qoder/commands/wl-prd.md +26 -13
  10. package/templates/qoder/commands/wl-req.md +3 -3
  11. package/templates/qoder/commands/wl-search.md +78 -47
  12. package/templates/qoder/commands/wl-task.md +343 -40
  13. package/templates/qoder/commands/wl-test.md +10 -10
  14. package/templates/qoder/config.yaml +35 -13
  15. package/templates/qoder/hooks/post-tool-use.py +181 -0
  16. package/templates/qoder/hooks/session-start.py +82 -1
  17. package/templates/qoder/hooks/stop-eval.py +207 -0
  18. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  19. package/templates/qoder/rules/wl-pipeline.md +70 -8
  20. package/templates/qoder/scripts/README.md +78 -115
  21. package/templates/qoder/scripts/capability/__init__.py +26 -0
  22. package/templates/qoder/scripts/capability/__main__.py +72 -0
  23. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  24. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  25. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  26. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  27. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  28. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  29. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  30. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  31. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  32. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  33. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  34. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  35. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  36. package/templates/qoder/scripts/capability/chain.py +92 -0
  37. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  38. package/templates/qoder/scripts/capability/registry.py +246 -0
  39. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  40. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  41. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  42. package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
  43. package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
  44. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  45. package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
  46. package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
  47. package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
  48. package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
  49. package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
  50. package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
  51. package/templates/qoder/scripts/domain/__init__.py +0 -0
  52. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  53. package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
  54. package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
  55. package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
  56. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  57. package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
  58. package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
  59. package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
  60. package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
  61. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
  62. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
  63. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
  64. package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
  65. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  66. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
  68. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  69. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  70. package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
  71. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  72. package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
  73. package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
  74. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  75. package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
  76. package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
  77. package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
  78. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  79. package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
  80. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  81. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  82. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  83. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  84. package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
  85. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  86. package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
  87. package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
  88. package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
  89. package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
  90. package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
  91. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  92. package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
  93. package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
  94. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  95. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  96. package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
  97. package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
  98. package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
  99. package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
  100. package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
  101. package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
  102. package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
  103. package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
  104. package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
  105. package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
  106. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  107. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  108. package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
  109. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  110. package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
  111. package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
  112. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
  113. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  114. package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
  115. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  116. package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
  117. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
  118. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  119. package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
  120. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
  121. package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
  122. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
  123. package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
  124. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  125. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  126. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
  127. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  128. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  129. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  130. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  131. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  132. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  133. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  134. package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
  135. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  136. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  137. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  138. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  139. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  140. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  141. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  142. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  143. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  144. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  145. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  146. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  147. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  148. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  153. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  154. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  155. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  156. package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
  157. package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
  158. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  159. package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
  160. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  161. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  162. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  163. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  164. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  165. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  166. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  167. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  168. package/templates/qoder/scripts/validation/__init__.py +0 -0
  169. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  171. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  172. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  173. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  174. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  175. package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
  176. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  177. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  178. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  179. package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
  180. package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
  181. package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
  182. package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
  183. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  184. package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
  185. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  186. package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
  187. package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
  188. package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
  189. package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
  190. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  191. package/templates/qoder/settings.json +30 -0
  192. package/templates/qoder/skills/design-import/SKILL.md +22 -13
  193. package/templates/qoder/skills/design-review/SKILL.md +19 -10
  194. package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
  195. package/templates/qoder/skills/prd-review/SKILL.md +6 -4
  196. package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
  197. package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
  198. package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
  199. package/templates/qoder/skills/test-generator/SKILL.md +7 -5
  200. package/templates/qoder/skills/wl-code/SKILL.md +23 -11
  201. package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
  202. package/templates/qoder/skills/wl-design/SKILL.md +5 -3
  203. package/templates/qoder/skills/wl-init/SKILL.md +6 -6
  204. package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
  205. package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
  206. package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
  207. package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
  208. package/templates/qoder/skills/wl-report/SKILL.md +25 -23
  209. package/templates/qoder/skills/wl-search/SKILL.md +149 -78
  210. package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
  211. package/templates/qoder/skills/wl-status/SKILL.md +48 -16
  212. package/templates/qoder/skills/wl-task/SKILL.md +92 -10
  213. package/templates/qoder/skills/wl-test/SKILL.md +75 -29
  214. package/templates/qoder/templates/prd-full-template.md +7 -0
  215. package/templates/root/AGENTS.md +275 -236
  216. package/templates/root/requirements.txt +3 -0
  217. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  218. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  219. package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
  220. package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
  221. package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
  222. package/templates/.qoder/learning/eval-history.jsonl +0 -28
  223. package/templates/data/index/wiki-index.json +0 -8
  224. package/templates/qoder/nul +0 -4
  225. package/templates/qoder/scripts/kg/extract.py +0 -398
  226. package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
  227. package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
  228. package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
  229. package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
  230. package/templates/qoder/scripts/task/__init__.py +0 -11
  231. package/templates/qoder/scripts/test/__init__.py +0 -11
  232. package/templates/qoder/scripts/wlkj.py +0 -116
  233. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  234. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  235. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  236. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  237. /package/templates/qoder/scripts/{test → validation/test}/secure-ls.js +0 -0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ // 环境检查 + 确保 CDP Proxy 就绪
3
+ //
4
+ // 用法:
5
+ // node check-deps.mjs 默认行为:读 config.env 偏好
6
+ // node check-deps.mjs --browser edge 本次临时指定浏览器(不写 config.env)
7
+ //
8
+ // 持久偏好 → config.env (skill 根目录, gitignored)
9
+ // 单次覆盖 → --browser 命令行参数(全链路 argv,不碰 process.env)
10
+
11
+ import { spawn } from 'node:child_process';
12
+ import fs from 'node:fs';
13
+ import os from 'node:os';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { selectBrowser, knownBrowsers, findFallbackPort } from './browser-discovery.mjs';
17
+
18
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
19
+ const PROXY_SCRIPT = path.join(ROOT, 'scripts', 'cdp-proxy.mjs');
20
+ const PROXY_PORT = Number(process.env.CDP_PROXY_PORT || 3456);
21
+ const CONFIG_PATH = path.join(ROOT, 'config.env');
22
+ const CONFIG_TEMPLATE = path.join(ROOT, 'templates', 'config.env.template');
23
+
24
+ // --- 参数解析 ---
25
+
26
+ function parseArgs(argv) {
27
+ const opts = { browser: null };
28
+ for (let i = 0; i < argv.length; i++) {
29
+ if (argv[i] === '--browser' && argv[i + 1]) { opts.browser = argv[i + 1]; i++; }
30
+ else if (argv[i].startsWith('--browser=')) { opts.browser = argv[i].slice('--browser='.length); }
31
+ }
32
+ return opts;
33
+ }
34
+
35
+ // --- 首次安装:从模板创建 config.env ---
36
+
37
+ function ensureConfigExists() {
38
+ if (fs.existsSync(CONFIG_PATH)) return;
39
+ try {
40
+ fs.copyFileSync(CONFIG_TEMPLATE, CONFIG_PATH);
41
+ console.log(`config: 已从模板创建 ${CONFIG_PATH}`);
42
+ } catch {
43
+ // 模板不存在或拷贝失败 —— 不阻塞,readConfig 会兜底
44
+ }
45
+ }
46
+
47
+ // --- Node.js 版本检查 ---
48
+
49
+ function checkNode() {
50
+ const major = Number(process.versions.node.split('.')[0]);
51
+ const version = `v${process.versions.node}`;
52
+ if (major >= 22) console.log(`node: ok (${version})`);
53
+ else console.log(`node: warn (${version}, 建议升级到 22+)`);
54
+ }
55
+
56
+ // --- CDP Proxy 启动与等待 ---
57
+
58
+ function httpGetJson(url, timeoutMs = 3000) {
59
+ return fetch(url, { signal: AbortSignal.timeout(timeoutMs) })
60
+ .then(async (res) => { try { return JSON.parse(await res.text()); } catch { return null; } })
61
+ .catch(() => null);
62
+ }
63
+
64
+ function startProxyDetached(browserOverride) {
65
+ const logFile = path.join(os.tmpdir(), 'cdp-proxy.log');
66
+ const logFd = fs.openSync(logFile, 'a');
67
+ const args = [PROXY_SCRIPT];
68
+ if (browserOverride) args.push('--browser', browserOverride);
69
+ const child = spawn(process.execPath, args, {
70
+ detached: true,
71
+ stdio: ['ignore', logFd, logFd],
72
+ ...(os.platform() === 'win32' ? { windowsHide: true } : {}),
73
+ });
74
+ child.unref();
75
+ fs.closeSync(logFd);
76
+ }
77
+
78
+ async function ensureProxy(expectedBrowserId, browserOverride) {
79
+ const healthUrl = `http://127.0.0.1:${PROXY_PORT}/health`;
80
+ const targetsUrl = `http://127.0.0.1:${PROXY_PORT}/targets`;
81
+
82
+ // 复用:proxy 已运行 + 已连接浏览器 → 校验 expected vs actual
83
+ const health = await httpGetJson(healthUrl);
84
+ if (health?.status === 'ok' && health.connected) {
85
+ const runningId = health.browser?.id;
86
+ const runningLabel = health.browser?.label || runningId || 'unknown';
87
+ if (expectedBrowserId && runningId && runningId !== 'unknown' && runningId !== expectedBrowserId) {
88
+ console.log(`proxy: 浏览器不一致 — 当前已连着 ${runningLabel},但本次需要 ${expectedBrowserId}`);
89
+ console.log(' 请在终端运行 pkill -f cdp-proxy.mjs 重置后再试');
90
+ return false;
91
+ }
92
+ console.log(`proxy: ready (${runningLabel})`);
93
+ return true;
94
+ }
95
+
96
+ console.log('proxy: connecting...');
97
+ startProxyDetached(browserOverride);
98
+
99
+ await new Promise((r) => setTimeout(r, 2000));
100
+
101
+ for (let i = 1; i <= 15; i++) {
102
+ const result = await httpGetJson(targetsUrl, 8000);
103
+ if (Array.isArray(result)) {
104
+ const newHealth = await httpGetJson(healthUrl);
105
+ const label = newHealth?.browser?.label || 'unknown';
106
+ console.log(`proxy: ready (${label})`);
107
+ return true;
108
+ }
109
+ if (i === 1) {
110
+ console.log('⚠️ 浏览器可能有授权弹窗,请点击「允许」后等待连接...');
111
+ }
112
+ await new Promise((r) => setTimeout(r, 1000));
113
+ }
114
+
115
+ console.log('❌ 连接超时,请检查浏览器调试设置');
116
+ console.log(` 日志:${path.join(os.tmpdir(), 'cdp-proxy.log')}`);
117
+ return false;
118
+ }
119
+
120
+ // --- 输出浏览器选择结果,返回是否可以继续启动 proxy ---
121
+
122
+ function printAvailableHint(detected) {
123
+ const detectedIds = new Set(detected.map(b => b.id));
124
+ const configurable = knownBrowsers().filter(b => !detectedIds.has(b.id));
125
+ if (detected.length) {
126
+ console.log(` 已开启远程调试:${detected.map(b => `${b.label} (${b.id}, port ${b.port})`).join('、')}`);
127
+ }
128
+ if (configurable.length) {
129
+ console.log(` 其他可配置:${configurable.map(b => `${b.label} (${b.id})`).join('、')}`);
130
+ }
131
+ }
132
+
133
+ async function resolveAndReport(override) {
134
+ const result = await selectBrowser(override);
135
+
136
+ switch (result.kind) {
137
+ case 'ok': {
138
+ const sourceTag = result.source === 'override' ? '[--browser 指定]' : '[config.env 偏好]';
139
+ console.log(`browser: ok (${result.browser.label}, port ${result.browser.port}) ${sourceTag}`);
140
+ return { proceed: true, browserId: result.browser.id };
141
+ }
142
+
143
+ case 'ambiguous': {
144
+ console.log('browser: needs decision — 用户尚未在 config.env 设置偏好');
145
+ printAvailableHint(result.detected);
146
+ console.log(' 请询问用户:哪个浏览器作为 Agent 的默认?(写入 config.env 的 WEB_ACCESS_BROWSER)');
147
+ console.log(' 若仅本次使用,可重跑:node check-deps.mjs --browser <id>');
148
+ return { proceed: false, exitCode: 2 };
149
+ }
150
+
151
+ case 'mismatch': {
152
+ const expected = result.override || result.configured;
153
+ const expectedLabel = knownBrowsers().find(b => b.id === expected)?.label || expected;
154
+ const sourceDesc = result.source === 'override' ? '本次指定' : '默认偏好';
155
+ console.log(`browser: error — ${sourceDesc}的浏览器是 "${expected}" (${expectedLabel}),但没连上`);
156
+ console.log(` Agent 处理顺序:`);
157
+ console.log(` 1. 先用系统命令打开 ${expectedLabel}(按你所在平台自行选择,如 macOS 的 open -a),再重新运行 node check-deps.mjs`);
158
+ console.log(` 2. 若仍报相同错误,可能是因为远程调试开关没启用 —— 告诉用户:在 ${expectedLabel} 的地址栏访问 ${expected}://inspect/#remote-debugging,勾选 "Allow remote debugging for this browser instance"`);
159
+ printAvailableHint(result.detected);
160
+ if (result.source === 'preference') {
161
+ console.log(` 也可以编辑 config.env 改默认偏好,或本次临时换浏览器:node check-deps.mjs --browser <id>`);
162
+ }
163
+ return { proceed: false, exitCode: 1 };
164
+ }
165
+
166
+ case 'empty': {
167
+ // 末路兜底:尝试常见固定端口(用户手动 --remote-debugging-port=9222 启动的场景)
168
+ const fallbackPort = await findFallbackPort();
169
+ if (fallbackPort) {
170
+ console.log(`browser: ok (port ${fallbackPort}) [通过手动调试端口连接]`);
171
+ return { proceed: true };
172
+ }
173
+ console.log('browser: 未连接 — 没有任何浏览器打开远程调试开关');
174
+ console.log(` 支持的浏览器:${knownBrowsers().map(b => b.label).join('、')}`);
175
+ console.log(' 在你想用的浏览器地址栏打开 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging,勾选 "Allow remote debugging for this browser instance"');
176
+ return { proceed: false, exitCode: 1 };
177
+ }
178
+ }
179
+ }
180
+
181
+ // --- main ---
182
+
183
+ async function main() {
184
+ const opts = parseArgs(process.argv.slice(2));
185
+ ensureConfigExists();
186
+ checkNode();
187
+
188
+ const { proceed, exitCode, browserId } = await resolveAndReport(opts.browser);
189
+ if (!proceed) process.exit(exitCode);
190
+
191
+ const proxyOk = await ensureProxy(browserId, opts.browser);
192
+ if (!proxyOk) process.exit(1);
193
+
194
+ // 列出已有站点经验
195
+ const patternsDir = path.join(ROOT, 'references', 'site-patterns');
196
+ try {
197
+ const sites = fs.readdirSync(patternsDir)
198
+ .filter(f => f.endsWith('.md'))
199
+ .map(f => f.replace(/\.md$/, ''));
200
+ if (sites.length) {
201
+ console.log(`\nsite-patterns: ${sites.join(', ')}`);
202
+ }
203
+ } catch {}
204
+ }
205
+
206
+ await main();
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ // find-url - 从本地 Chromium 系浏览器(Chrome / Edge)书签/历史中检索 URL
3
+ // 用于定位公网搜索覆盖不到的目标(组织内部系统、SSO 后台、内网域名等)。
4
+ //
5
+ // 用法:
6
+ // node find-url.mjs [关键词...] [--only bookmarks|history] [--limit N] [--since 1d|7h|YYYY-MM-DD]
7
+ //
8
+ // <关键词> 空格分词、多词 AND,匹配 title + url;可省略
9
+ // --only <source> 限定数据源(bookmarks / history),默认两者都查
10
+ // --browser <id> 限定浏览器(chrome / edge),默认遍历所有已安装的
11
+ // --limit N 条数上限,默认 20;0 = 不限
12
+ // --since <window> 时间窗(仅作用于历史)。1d / 7h / 30m 或 YYYY-MM-DD
13
+ // --sort recent|visits 历史排序:按最近访问 / 按访问次数,默认 recent
14
+ //
15
+ // 示例:
16
+ // node find-url.mjs 财务小智
17
+ // node find-url.mjs agent skills
18
+ // node find-url.mjs github --since 7d --only history
19
+ // node find-url.mjs --since 7d --only history --sort visits # 最近一周高频网站
20
+ // node find-url.mjs --since 2d --only history --limit 0
21
+ // node find-url.mjs github --browser edge # 只查 Edge
22
+
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+ import os from 'node:os';
26
+ import { execFileSync } from 'node:child_process';
27
+
28
+ // --- 参数解析 -----------------------------------------------------------
29
+ function parseArgs(argv) {
30
+ const a = { keywords: [], only: null, browser: null, limit: 20, since: null, sort: 'recent' };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const v = argv[i];
33
+ if (v === '--only') a.only = argv[++i];
34
+ else if (v === '--browser') a.browser = argv[++i];
35
+ else if (v === '--limit') a.limit = parseInt(argv[++i], 10);
36
+ else if (v === '--since') a.since = parseSince(argv[++i]);
37
+ else if (v === '--sort') a.sort = argv[++i];
38
+ else if (v === '-h' || v === '--help') { printUsage(); process.exit(0); }
39
+ else if (v.startsWith('--')) die(`未知参数: ${v}`);
40
+ else a.keywords.push(v);
41
+ }
42
+ if (a.only && !['bookmarks', 'history'].includes(a.only)) die(`--only 仅支持 bookmarks|history`);
43
+ if (!['recent', 'visits'].includes(a.sort)) die(`--sort 仅支持 recent|visits`);
44
+ if (Number.isNaN(a.limit) || a.limit < 0) die('--limit 需为非负整数');
45
+ return a;
46
+ }
47
+
48
+ function parseSince(s) {
49
+ if (!s) die('--since 需要值');
50
+ const m = s.match(/^(\d+)([dhm])$/);
51
+ if (m) {
52
+ const n = parseInt(m[1], 10);
53
+ const ms = { d: 86400000, h: 3600000, m: 60000 }[m[2]];
54
+ return new Date(Date.now() - n * ms);
55
+ }
56
+ const d = new Date(s);
57
+ if (Number.isNaN(d.getTime())) die(`无效 --since 值: ${s}(用 1d / 7h / 30m / YYYY-MM-DD)`);
58
+ return d;
59
+ }
60
+
61
+ function die(msg) { console.error(msg); process.exit(1); }
62
+ function printUsage() { console.error(fs.readFileSync(new URL(import.meta.url)).toString().split('\n').slice(1, 21).map(l => l.replace(/^\/\/ ?/, '')).join('\n')); }
63
+
64
+ // --- 浏览器用户数据目录(跨平台 + 多浏览器) -----------------------------
65
+ // 加新浏览器:只改这里
66
+ function knownBrowserDataDirs() {
67
+ const home = os.homedir();
68
+ const localAppData = process.env.LOCALAPPDATA || '';
69
+ switch (os.platform()) {
70
+ case 'darwin':
71
+ return [
72
+ { id: 'chrome', label: 'Chrome', dir: path.join(home, 'Library/Application Support/Google/Chrome') },
73
+ { id: 'edge', label: 'Edge', dir: path.join(home, 'Library/Application Support/Microsoft Edge') },
74
+ ];
75
+ case 'linux':
76
+ return [
77
+ { id: 'chrome', label: 'Chrome', dir: path.join(home, '.config/google-chrome') },
78
+ { id: 'edge', label: 'Edge', dir: path.join(home, '.config/microsoft-edge') },
79
+ ];
80
+ case 'win32':
81
+ return [
82
+ { id: 'chrome', label: 'Chrome', dir: path.join(localAppData, 'Google/Chrome/User Data') },
83
+ { id: 'edge', label: 'Edge', dir: path.join(localAppData, 'Microsoft/Edge/User Data') },
84
+ ];
85
+ default:
86
+ return [];
87
+ }
88
+ }
89
+
90
+ // --- Profile 枚举 -------------------------------------------------------
91
+ function listProfiles(dataDir) {
92
+ try {
93
+ const state = JSON.parse(fs.readFileSync(path.join(dataDir, 'Local State'), 'utf-8'));
94
+ const info = state?.profile?.info_cache || {};
95
+ const list = Object.keys(info).map(dir => ({ dir, name: info[dir].name || dir }));
96
+ if (list.length) return list;
97
+ } catch { /* 回退 */ }
98
+ return [{ dir: 'Default', name: 'Default' }];
99
+ }
100
+
101
+ // --- 书签检索 -----------------------------------------------------------
102
+ function searchBookmarks(profileDir, profileName, browserLabel, keywords) {
103
+ const file = path.join(profileDir, 'Bookmarks');
104
+ if (!fs.existsSync(file)) return [];
105
+ let data;
106
+ try { data = JSON.parse(fs.readFileSync(file, 'utf-8')); } catch { return []; }
107
+ if (!keywords.length) return []; // 书签无时间维度,无关键词不返回
108
+
109
+ const needles = keywords.map(k => k.toLowerCase());
110
+ const out = [];
111
+ function walk(node, trail) {
112
+ if (!node) return;
113
+ if (node.type === 'url') {
114
+ const hay = `${node.name || ''} ${node.url || ''}`.toLowerCase();
115
+ if (needles.every(n => hay.includes(n))) {
116
+ out.push({ browser: browserLabel, profile: profileName, name: node.name || '', url: node.url || '', folder: trail.join(' / ') });
117
+ }
118
+ }
119
+ if (Array.isArray(node.children)) {
120
+ const sub = node.name ? [...trail, node.name] : trail;
121
+ for (const c of node.children) walk(c, sub);
122
+ }
123
+ }
124
+ for (const root of Object.values(data.roots || {})) walk(root, []);
125
+ return out;
126
+ }
127
+
128
+ // --- 历史检索(SQLite 运行时锁定,需 copy 到 tmp) ------------------------
129
+ const WEBKIT_EPOCH_DIFF_US = 11644473600000000n; // 1601→1970 微秒差
130
+
131
+ function searchHistory(profileDir, profileName, browserLabel, keywords, since, limit, sort) {
132
+ const src = path.join(profileDir, 'History');
133
+ if (!fs.existsSync(src)) return [];
134
+ const tmp = path.join(os.tmpdir(), `browser-history-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.sqlite`);
135
+ try {
136
+ fs.copyFileSync(src, tmp);
137
+ const conds = ['last_visit_time > 0'];
138
+ for (const kw of keywords) {
139
+ const esc = kw.toLowerCase().replace(/'/g, "''");
140
+ conds.push(`LOWER(title || ' ' || url) LIKE '%${esc}%'`);
141
+ }
142
+ if (since) {
143
+ const webkitUs = BigInt(since.getTime()) * 1000n + WEBKIT_EPOCH_DIFF_US;
144
+ conds.push(`last_visit_time >= ${webkitUs}`);
145
+ }
146
+ const limitClause = limit === 0 ? -1 : limit;
147
+ const orderBy = sort === 'visits'
148
+ ? 'visit_count DESC, last_visit_time DESC'
149
+ : 'last_visit_time DESC';
150
+ const sql = `SELECT title, url,
151
+ datetime((last_visit_time - 11644473600000000)/1000000, 'unixepoch', 'localtime') AS visit,
152
+ visit_count
153
+ FROM urls WHERE ${conds.join(' AND ')}
154
+ ORDER BY ${orderBy} LIMIT ${limitClause};`;
155
+
156
+ const raw = execFileSync('sqlite3', ['-separator', '\t', tmp, sql], { encoding: 'utf-8', maxBuffer: 50 * 1024 * 1024 });
157
+ return raw.trim().split('\n').filter(Boolean).map(line => {
158
+ const [title, url, visit, visit_count] = line.split('\t');
159
+ return { browser: browserLabel, profile: profileName, title, url, visit, visit_count: parseInt(visit_count, 10) };
160
+ });
161
+ } catch (e) {
162
+ if (e.code === 'ENOENT') die('未找到 sqlite3 命令。macOS/Linux 通常自带;Windows 可用 `winget install sqlite.sqlite` 或从 https://sqlite.org/download.html 下载后加入 PATH。');
163
+ return [];
164
+ } finally {
165
+ try { fs.unlinkSync(tmp); } catch {}
166
+ }
167
+ }
168
+
169
+ // --- 输出格式化 ---------------------------------------------------------
170
+ // 用 `|` 作字段分隔符;字段内含 `|` 的替换成 `│`(全宽竖线)避免歧义
171
+ const clean = s => String(s ?? '').replaceAll('|', '│').trim();
172
+
173
+ function originTag(item, showBrowser, showProfile) {
174
+ if (showBrowser && showProfile) return '@' + clean(item.browser) + '-' + clean(item.profile);
175
+ if (showBrowser) return '@' + clean(item.browser);
176
+ if (showProfile) return '@' + clean(item.profile);
177
+ return null;
178
+ }
179
+
180
+ function printBookmarks(items, showBrowser, showProfile) {
181
+ console.log(`[书签] ${items.length} 条`);
182
+ for (const b of items) {
183
+ const segs = [clean(b.name) || '(无标题)', clean(b.url)];
184
+ if (b.folder) segs.push(clean(b.folder));
185
+ const tag = originTag(b, showBrowser, showProfile);
186
+ if (tag) segs.push(tag);
187
+ console.log(' ' + segs.join(' | '));
188
+ }
189
+ }
190
+
191
+ function printHistory(items, showBrowser, showProfile, sortLabel) {
192
+ console.log(`[历史] ${items.length} 条(${sortLabel})`);
193
+ for (const h of items) {
194
+ const segs = [clean(h.title) || '(无标题)', clean(h.url), h.visit];
195
+ if (h.visit_count > 1) segs.push(`visits=${h.visit_count}`);
196
+ const tag = originTag(h, showBrowser, showProfile);
197
+ if (tag) segs.push(tag);
198
+ console.log(' ' + segs.join(' | '));
199
+ }
200
+ }
201
+
202
+ // --- main ---------------------------------------------------------------
203
+ const args = parseArgs(process.argv.slice(2));
204
+
205
+ let browsers = knownBrowserDataDirs().filter(b => fs.existsSync(b.dir));
206
+ if (args.browser) {
207
+ const filtered = browsers.filter(b => b.id === args.browser);
208
+ if (!filtered.length) {
209
+ const available = browsers.map(b => b.id).join('、') || '无';
210
+ die(`未找到浏览器 ${args.browser} 的用户数据目录(已检测到:${available})`);
211
+ }
212
+ browsers = filtered;
213
+ }
214
+ if (!browsers.length) die('未找到任何浏览器(Chrome / Edge)的用户数据目录');
215
+
216
+ const doBookmarks = args.only !== 'history';
217
+ const doHistory = args.only !== 'bookmarks';
218
+
219
+ const bookmarks = [];
220
+ const history = [];
221
+ for (const browser of browsers) {
222
+ const profiles = listProfiles(browser.dir);
223
+ for (const p of profiles) {
224
+ const pDir = path.join(browser.dir, p.dir);
225
+ if (!fs.existsSync(pDir)) continue;
226
+ if (doBookmarks) bookmarks.push(...searchBookmarks(pDir, p.name, browser.label, args.keywords));
227
+ if (doHistory) history.push(...searchHistory(pDir, p.name, browser.label, args.keywords, args.since, args.limit === 0 ? 0 : args.limit * 2, args.sort));
228
+ }
229
+ }
230
+
231
+ // 历史跨 profile/浏览器 合并后按指定 sort 重排 + 切顶
232
+ if (args.sort === 'visits') {
233
+ history.sort((a, b) => (b.visit_count || 0) - (a.visit_count || 0) || (b.visit || '').localeCompare(a.visit || ''));
234
+ } else {
235
+ history.sort((a, b) => (b.visit || '').localeCompare(a.visit || ''));
236
+ }
237
+ const bookmarksOut = args.limit === 0 ? bookmarks : bookmarks.slice(0, args.limit);
238
+ const historyOut = args.limit === 0 ? history : history.slice(0, args.limit);
239
+
240
+ // 仅当结果真的横跨多个 browser/profile 时才输出 @ 标注(避免单源场景噪音)
241
+ const seenBrowsers = new Set([...bookmarksOut, ...historyOut].map(x => x.browser));
242
+ const seenProfiles = new Set([...bookmarksOut, ...historyOut].map(x => x.profile));
243
+ const showBrowser = seenBrowsers.size > 1;
244
+ const showProfile = seenProfiles.size > 1;
245
+
246
+ const sortLabel = args.sort === 'visits' ? '按访问次数' : '按最近访问';
247
+ if (doBookmarks) printBookmarks(bookmarksOut, showBrowser, showProfile);
248
+ if (doBookmarks && doHistory) console.log();
249
+ if (doHistory) printHistory(historyOut, showBrowser, showProfile, sortLabel);
250
+
251
+ if (!args.keywords.length && doBookmarks && !doHistory) {
252
+ console.error('\n提示:书签无时间维度,无关键词查询无意义。加关键词或切换 --only history。');
253
+ }
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ // 根据用户输入匹配站点经验文件(跨平台,替代 match-site.sh)
3
+ // 用法:node match-site.mjs "用户输入文本"
4
+ // 输出:匹配到的站点经验内容,无匹配则静默
5
+
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
11
+ const PATTERNS_DIR = path.join(ROOT, 'references', 'site-patterns');
12
+ const query = (process.argv[2] || '').trim();
13
+
14
+ if (!query || !fs.existsSync(PATTERNS_DIR)) {
15
+ process.exit(0);
16
+ }
17
+
18
+ for (const entry of fs.readdirSync(PATTERNS_DIR, { withFileTypes: true })) {
19
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
20
+
21
+ const domain = entry.name.replace(/\.md$/, '');
22
+ const raw = fs.readFileSync(path.join(PATTERNS_DIR, entry.name), 'utf8');
23
+
24
+ // 提取 aliases
25
+ const aliasesLine = raw.split(/\r?\n/).find((l) => l.startsWith('aliases:')) || '';
26
+ const aliases = aliasesLine
27
+ .replace(/^aliases:\s*/, '')
28
+ .replace(/^\[/, '').replace(/\]$/, '')
29
+ .split(',')
30
+ .map((v) => v.trim())
31
+ .filter(Boolean);
32
+
33
+ // 构建匹配模式
34
+ const escaped = (t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
35
+ const pattern = [domain, ...aliases].map(escaped).join('|');
36
+ if (!new RegExp(pattern, 'i').test(query)) continue;
37
+
38
+ // 跳过 frontmatter,输出正文
39
+ const fences = [...raw.matchAll(/^---\s*$/gm)];
40
+ const body = fences.length >= 2
41
+ ? raw.slice(fences[1].index + fences[1][0].length).replace(/^\r?\n/, '')
42
+ : raw;
43
+
44
+ process.stdout.write(`--- 站点经验: ${domain} ---\n`);
45
+ process.stdout.write(body.trimEnd() + '\n\n');
46
+ }
@@ -0,0 +1,9 @@
1
+ # web-access skill 用户偏好配置
2
+ # 此文件是模板。首次运行 check-deps.mjs 时会自动复制为 config.env(git ignored)。
3
+ #
4
+ # 用户长期偏好的默认 Agent 浏览器
5
+ # - 留空:每次启动都问用户偏好哪个
6
+ # - 设值:固定使用该浏览器;若未检测到对应 toggle 则硬错(不擅自降级)
7
+ #
8
+ # 合法值:chrome / edge
9
+ WEB_ACCESS_BROWSER=
@@ -0,0 +1,11 @@
1
+ # 子包初始化: 注入 scripts/根 + 所有子包到 sys.path
2
+ # 让跨包 import (旧式不带前缀) 仍可用, 兼容现有代码
3
+ import os as _os, sys as _sys
4
+ _root = _os.path.dirname(_os.path.dirname(_os.path.abspath(__file__)))
5
+ if _root not in _sys.path:
6
+ _sys.path.insert(0, _root)
7
+ # 注入所有子包目录 (兼容旧式 import xxx)
8
+ for _sub in ('kg', 'build', 'test', 'setup', 'io'):
9
+ _subdir = _os.path.join(_root, _sub)
10
+ if _os.path.isdir(_subdir) and _subdir not in _sys.path:
11
+ _sys.path.insert(0, _subdir)