@hupan56/wlkj 2.7.12 → 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.
- package/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# CDP Proxy API 参考
|
|
2
|
+
|
|
3
|
+
## 基础信息
|
|
4
|
+
|
|
5
|
+
- 地址:`http://localhost:3456`
|
|
6
|
+
- 启动:`node ~/.claude/skills/web-access/scripts/cdp-proxy.mjs &`
|
|
7
|
+
- 启动后持续运行,不建议主动停止(重启需 Chrome 重新授权)
|
|
8
|
+
- 强制停止:`pkill -f cdp-proxy.mjs`
|
|
9
|
+
|
|
10
|
+
## API 端点
|
|
11
|
+
|
|
12
|
+
### GET /health
|
|
13
|
+
健康检查,返回连接状态。
|
|
14
|
+
```bash
|
|
15
|
+
curl -s http://localhost:3456/health
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### GET /targets
|
|
19
|
+
列出所有已打开的页面 tab。返回数组,每项含 `targetId`、`title`、`url`。
|
|
20
|
+
```bash
|
|
21
|
+
curl -s http://localhost:3456/targets
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### POST /new
|
|
25
|
+
创建新后台 tab,自动等待页面加载完成。**URL 通过 POST body 原样传入**,无需 URL-encode、不会因 query 中含 `&` 被切分。返回 `{ targetId }`。
|
|
26
|
+
```bash
|
|
27
|
+
curl -s -X POST --data-raw 'https://example.com' http://localhost:3456/new
|
|
28
|
+
# 含 query 的目标 URL(如带 token 的小红书笔记)也直接原样传:
|
|
29
|
+
curl -s -X POST --data-raw 'https://www.xiaohongshu.com/explore/xxx?xsec_source=app_share&xsec_token=ABC&type=normal' http://localhost:3456/new
|
|
30
|
+
```
|
|
31
|
+
> v2.5.3 起改为 POST。旧的 `GET /new?url=...` 返回 400 + 迁移指引,详见 `migration-2.5.3.md`。
|
|
32
|
+
|
|
33
|
+
### GET /close?target=ID
|
|
34
|
+
关闭指定 tab。
|
|
35
|
+
```bash
|
|
36
|
+
curl -s "http://localhost:3456/close?target=TARGET_ID"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### POST /navigate?target=ID
|
|
40
|
+
在已有 tab 中导航到新 URL,自动等待加载。**target 走 query(不带特殊字符的不透明 ID),URL 走 POST body**。
|
|
41
|
+
```bash
|
|
42
|
+
curl -s -X POST --data-raw 'https://example.com' "http://localhost:3456/navigate?target=ID"
|
|
43
|
+
```
|
|
44
|
+
> v2.5.3 起改为 POST。旧的 `GET /navigate?target=...&url=...` 返回 400 + 迁移指引,详见 `migration-2.5.3.md`。
|
|
45
|
+
|
|
46
|
+
### GET /back?target=ID
|
|
47
|
+
后退一页。
|
|
48
|
+
```bash
|
|
49
|
+
curl -s "http://localhost:3456/back?target=ID"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### GET /info?target=ID
|
|
53
|
+
获取页面基础信息(title、url、readyState)。
|
|
54
|
+
```bash
|
|
55
|
+
curl -s "http://localhost:3456/info?target=ID"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### POST /eval?target=ID
|
|
59
|
+
执行 JavaScript 表达式,POST body 为 JS 代码。
|
|
60
|
+
```bash
|
|
61
|
+
curl -s -X POST "http://localhost:3456/eval?target=ID" -d 'document.title'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### POST /click?target=ID
|
|
65
|
+
JS 层面点击(`el.click()`),POST body 为 CSS 选择器。自动 scrollIntoView 后点击。简单快速,覆盖大多数场景。
|
|
66
|
+
```bash
|
|
67
|
+
curl -s -X POST "http://localhost:3456/click?target=ID" -d 'button.submit'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### POST /clickAt?target=ID
|
|
71
|
+
CDP 浏览器级真实鼠标点击(`Input.dispatchMouseEvent`),POST body 为 CSS 选择器。先获取元素坐标,再模拟鼠标按下/释放。算真实用户手势,能触发文件对话框、绕过部分反自动化检测。
|
|
72
|
+
```bash
|
|
73
|
+
curl -s -X POST "http://localhost:3456/clickAt?target=ID" -d 'button.upload'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### POST /setFiles?target=ID
|
|
77
|
+
给 file input 设置本地文件路径(`DOM.setFileInputFiles`),完全绕过文件对话框。POST body 为 JSON。
|
|
78
|
+
```bash
|
|
79
|
+
curl -s -X POST "http://localhost:3456/setFiles?target=ID" -d '{"selector":"input[type=file]","files":["/path/to/file1.png","/path/to/file2.png"]}'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### GET /scroll?target=ID&y=3000&direction=down
|
|
83
|
+
滚动页面。`direction` 可选 `down`(默认)、`up`、`top`、`bottom`。滚动后自动等待 800ms 供懒加载触发。
|
|
84
|
+
```bash
|
|
85
|
+
curl -s "http://localhost:3456/scroll?target=ID&y=3000"
|
|
86
|
+
curl -s "http://localhost:3456/scroll?target=ID&direction=bottom"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### GET /screenshot?target=ID&file=/tmp/shot.png
|
|
90
|
+
截图。指定 `file` 参数保存到本地文件;不指定则返回图片二进制。可选 `format=jpeg`。
|
|
91
|
+
```bash
|
|
92
|
+
curl -s "http://localhost:3456/screenshot?target=ID&file=/tmp/shot.png"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## /eval 使用提示
|
|
96
|
+
|
|
97
|
+
- POST body 为任意 JS 表达式,返回 `{ value }` 或 `{ error }`
|
|
98
|
+
- 支持 `awaitPromise`:可以写 async 表达式
|
|
99
|
+
- 返回值必须是可序列化的(字符串、数字、对象),DOM 节点不能直接返回,需要提取属性
|
|
100
|
+
- 提取大量数据时用 `JSON.stringify()` 包裹,确保返回字符串
|
|
101
|
+
- 根据页面实际 DOM 结构编写选择器,不要套用固定模板
|
|
102
|
+
|
|
103
|
+
## 错误处理
|
|
104
|
+
|
|
105
|
+
| 错误 | 原因 | 解决 |
|
|
106
|
+
|------|------|------|
|
|
107
|
+
| `Chrome 未开启远程调试端口` | Chrome 未开启远程调试 | 提示用户打开 `chrome://inspect/#remote-debugging` 并勾选 Allow |
|
|
108
|
+
| `attach 失败` | targetId 无效或 tab 已关闭 | 用 `/targets` 获取最新列表 |
|
|
109
|
+
| `CDP 命令超时` | 页面长时间未响应 | 重试或检查 tab 状态 |
|
|
110
|
+
| `端口已被占用` | 另一个 proxy 已在运行 | 已有实例可直接复用 |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# v2.5.3 迁移指南:`/new` 和 `/navigate` 改 POST body
|
|
2
|
+
|
|
3
|
+
## TL;DR
|
|
4
|
+
|
|
5
|
+
```diff
|
|
6
|
+
- curl -s "http://localhost:3456/new?url=https://example.com"
|
|
7
|
+
+ curl -s -X POST --data-raw 'https://example.com' http://localhost:3456/new
|
|
8
|
+
|
|
9
|
+
- curl -s "http://localhost:3456/navigate?target=ID&url=https://example.com"
|
|
10
|
+
+ curl -s -X POST --data-raw 'https://example.com' "http://localhost:3456/navigate?target=ID"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
旧写法收到 `400` + 包含本文档路径的错误信息后,按下方对照表改写调用。同时把发现的旧写法在源站点经验文件中就地修正。
|
|
14
|
+
|
|
15
|
+
## 为什么改
|
|
16
|
+
|
|
17
|
+
旧版 `/new?url=...` 把目标 URL 塞进 query 参数。当目标 URL 自带 query(如小红书笔记必须携带 `xsec_token`、B 站、微博的分享链接),proxy 用 `URL.searchParams` 解析时,目标 URL 里未编码的 `&` 会被当成 **proxy 自身的** query 分隔符,导致目标 URL 被错误切分、token 等关键参数丢失,页面返回"内容不存在"或被反爬拦截。
|
|
18
|
+
|
|
19
|
+
例:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
GET /new?url=https://xhs.com/explore/x?a=1&b=2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
proxy 解析结果:
|
|
26
|
+
- `q.url = "https://xhs.com/explore/x?a=1"` ← 被截断
|
|
27
|
+
- `q.b = "2"` ← 被当成 proxy 自己的参数吃掉
|
|
28
|
+
|
|
29
|
+
根因是用一种"带语法的格式(query string)"承载另一种"也带相同语法的数据(URL)",存在结构性歧义。靠调用方守纪律做 URL-encode 治标不治本:Agent 偶尔忘记就翻车,且增加每次调用的 token 成本。
|
|
30
|
+
|
|
31
|
+
v2.5.3 把 URL 改为通过 **POST body** 传入。HTTP body 是不透明字节流(边界由 `Content-Length` header 显式声明,与数据本身解耦),不存在任何分隔符歧义,URL 原样传输,零编码负担。
|
|
32
|
+
|
|
33
|
+
## 转换对照表
|
|
34
|
+
|
|
35
|
+
### `/new`
|
|
36
|
+
|
|
37
|
+
| 场景 | 旧(v2.5.2) | 新(v2.5.3) |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| 简单 URL | `curl ".../new?url=https://example.com"` | `curl -X POST --data-raw 'https://example.com' .../new` |
|
|
40
|
+
| URL 含 query | `curl ".../new?url=https://xhs.com/explore/x?xsec_token=ABC"` ⚠️ token 丢失 | `curl -X POST --data-raw 'https://xhs.com/explore/x?xsec_token=ABC' .../new` ✓ |
|
|
41
|
+
| URL 含 `#` fragment | `curl ".../new?url=https://app/page#sec"` ⚠️ fragment 丢失 | `curl -X POST --data-raw 'https://app/page#sec' .../new` ✓ |
|
|
42
|
+
|
|
43
|
+
### `/navigate`
|
|
44
|
+
|
|
45
|
+
| 旧 | 新 |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `curl ".../navigate?target=ID&url=URL"` | `curl -X POST --data-raw 'URL' ".../navigate?target=ID"` |
|
|
48
|
+
|
|
49
|
+
`target` 仍走 query(它是无特殊字符的不透明 ID,没有歧义问题)。
|
|
50
|
+
|
|
51
|
+
## Agent 自检 checklist
|
|
52
|
+
|
|
53
|
+
在迁移后的环境中工作时,遇到任何来源的 web-access 调用,按以下顺序处理:
|
|
54
|
+
|
|
55
|
+
1. **判断调用是否过时**:搜索字符串 `localhost:3456/new?url=` 或 `localhost:3456/navigate?` 后跟 `&url=`。这两类模式属于 v2.5.2 旧写法。
|
|
56
|
+
2. **就地改写**:按上表对照修改成 POST 形式。
|
|
57
|
+
3. **更新源文件**:如果旧写法来自 `references/site-patterns/*.md` 中某个站点经验文件,把源文件也一并修正掉(不要只在当前调用上改)—— 这样下次复用同一站点经验时不会再踩坑。
|
|
58
|
+
4. **保留 URL 原样**:POST body 是不透明字节流,目标 URL 中的 `?`、`&`、`=`、`#`、`%` 等字符**无需任何转义**,从 DOM/分享链接抓到什么样就传什么样。
|
|
59
|
+
|
|
60
|
+
## FAQ
|
|
61
|
+
|
|
62
|
+
**Q: 我用 GET `?url=` 调用会发生什么?**
|
|
63
|
+
A: 收到 HTTP 400 响应,body 中包含 `error`、`migration` 文档路径、`example` 示例。按提示改 POST 调用即可。
|
|
64
|
+
|
|
65
|
+
**Q: 为什么不在 proxy 里做兼容(同时支持 GET 和 POST)?**
|
|
66
|
+
A: 兼容路径会留下永久的"启发式截取 query"代码 + SKILL.md 里"也支持旧写法"的脚注。两条路径长期共存 → Agent 学得不彻底、维护者两套都要测、读代码的人要分辨主路径 vs 兼容路径。把架构债转成了认知债。一次性 breaking change 配合迁移指南,更彻底也更便宜。
|
|
67
|
+
|
|
68
|
+
**Q: 我自己的脚本/别名/笔记里有大量旧写法,有迁移脚本吗?**
|
|
69
|
+
A: 没有也不打算提供。站点经验和脚本是人写的文档/代码,掺着说明、注释、上下文判断,正则替换容易误伤。本指南的 Agent 自检 checklist 就是给"Agent 看着内容自己判断怎么改"的,比脚本可靠。
|
|
70
|
+
|
|
71
|
+
**Q: 还有哪些 endpoint 用 POST body?**
|
|
72
|
+
A: 一直都有:`/eval`、`/click`、`/clickAt`、`/setFiles` 全是 POST + body。这次 `/new` `/navigate` 加入后,**所有传输"任意字符串载荷"的写操作都统一走 POST body** —— 内部一致性提升。
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// 浏览器 CDP 端口发现 + 选择 - 单一职责模块
|
|
2
|
+
// 被 check-deps.mjs 和 cdp-proxy.mjs 共享。
|
|
3
|
+
//
|
|
4
|
+
// 选择规则(resolution):
|
|
5
|
+
// 1. 调用方传入 override 参数(来自命令行 --browser) → 严格模式,找不到则硬错
|
|
6
|
+
// 2. config.env 里 WEB_ACCESS_BROWSER 设了 → 严格模式,找不到则硬错
|
|
7
|
+
// 3. 都没设 → "ask" 模式,提示调用方询问用户
|
|
8
|
+
//
|
|
9
|
+
// 不擅自降级:偏好不可用一律硬错,让用户介入。
|
|
10
|
+
// 持久态只有 config.env 一处;override 是单次 spawn 通过命令行参数表达,不读 process.env。
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import net from 'node:net';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const CONFIG_PATH = path.join(SKILL_ROOT, 'config.env');
|
|
20
|
+
|
|
21
|
+
// 已知支持 chrome://inspect#remote-debugging toggle 的浏览器
|
|
22
|
+
// 加新浏览器:只改这里
|
|
23
|
+
export function knownBrowsers() {
|
|
24
|
+
const home = os.homedir();
|
|
25
|
+
const localAppData = process.env.LOCALAPPDATA || '';
|
|
26
|
+
switch (os.platform()) {
|
|
27
|
+
case 'darwin':
|
|
28
|
+
return [
|
|
29
|
+
{ id: 'chrome', label: 'Chrome', devToolsPath: path.join(home, 'Library/Application Support/Google/Chrome/DevToolsActivePort') },
|
|
30
|
+
{ id: 'chrome-canary', label: 'Chrome Canary', devToolsPath: path.join(home, 'Library/Application Support/Google/Chrome Canary/DevToolsActivePort') },
|
|
31
|
+
{ id: 'chromium', label: 'Chromium', devToolsPath: path.join(home, 'Library/Application Support/Chromium/DevToolsActivePort') },
|
|
32
|
+
{ id: 'edge', label: 'Microsoft Edge', devToolsPath: path.join(home, 'Library/Application Support/Microsoft Edge/DevToolsActivePort') },
|
|
33
|
+
];
|
|
34
|
+
case 'linux':
|
|
35
|
+
return [
|
|
36
|
+
{ id: 'chrome', label: 'Chrome', devToolsPath: path.join(home, '.config/google-chrome/DevToolsActivePort') },
|
|
37
|
+
{ id: 'chromium', label: 'Chromium', devToolsPath: path.join(home, '.config/chromium/DevToolsActivePort') },
|
|
38
|
+
{ id: 'edge', label: 'Microsoft Edge', devToolsPath: path.join(home, '.config/microsoft-edge/DevToolsActivePort') },
|
|
39
|
+
];
|
|
40
|
+
case 'win32':
|
|
41
|
+
return [
|
|
42
|
+
{ id: 'chrome', label: 'Chrome', devToolsPath: path.join(localAppData, 'Google/Chrome/User Data/DevToolsActivePort') },
|
|
43
|
+
{ id: 'chromium', label: 'Chromium', devToolsPath: path.join(localAppData, 'Chromium/User Data/DevToolsActivePort') },
|
|
44
|
+
{ id: 'edge', label: 'Microsoft Edge', devToolsPath: path.join(localAppData, 'Microsoft/Edge/User Data/DevToolsActivePort') },
|
|
45
|
+
];
|
|
46
|
+
default:
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TCP 端口监听检测
|
|
52
|
+
// 用 TCP connect 而非 WebSocket,避免触发浏览器的远程调试授权弹窗。
|
|
53
|
+
export function checkPort(port, host = '127.0.0.1', timeoutMs = 2000) {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const socket = net.createConnection(port, host);
|
|
56
|
+
const timer = setTimeout(() => { socket.destroy(); resolve(false); }, timeoutMs);
|
|
57
|
+
socket.once('connect', () => { clearTimeout(timer); socket.destroy(); resolve(true); });
|
|
58
|
+
socket.once('error', () => { clearTimeout(timer); resolve(false); });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 读 config.env 文件(不写入 process.env,分清来源)
|
|
63
|
+
// 格式:KEY=VALUE,# 开头是注释
|
|
64
|
+
function readConfig() {
|
|
65
|
+
const cfg = {};
|
|
66
|
+
let content;
|
|
67
|
+
try { content = fs.readFileSync(CONFIG_PATH, 'utf8'); }
|
|
68
|
+
catch { return cfg; }
|
|
69
|
+
for (const line of content.split(/\r?\n/)) {
|
|
70
|
+
const t = line.trim();
|
|
71
|
+
if (!t || t.startsWith('#')) continue;
|
|
72
|
+
const i = t.indexOf('=');
|
|
73
|
+
if (i === -1) continue;
|
|
74
|
+
const k = t.slice(0, i).trim();
|
|
75
|
+
const v = t.slice(i + 1).trim();
|
|
76
|
+
if (k && v) cfg[k] = v;
|
|
77
|
+
}
|
|
78
|
+
return cfg;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 返回所有开了 toggle 且端口活的浏览器
|
|
82
|
+
async function detectAll() {
|
|
83
|
+
const result = [];
|
|
84
|
+
for (const browser of knownBrowsers()) {
|
|
85
|
+
let content;
|
|
86
|
+
try { content = fs.readFileSync(browser.devToolsPath, 'utf8'); }
|
|
87
|
+
catch { continue; }
|
|
88
|
+
const lines = content.trim().split(/\r?\n/).filter(Boolean);
|
|
89
|
+
const port = parseInt(lines[0], 10);
|
|
90
|
+
if (!(port > 0 && port < 65536)) continue;
|
|
91
|
+
if (!(await checkPort(port))) continue;
|
|
92
|
+
result.push({ ...browser, port, wsPath: lines[1] || null });
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 决策入口
|
|
98
|
+
// 参数:override — 调用方解析自命令行 --browser 的值(null 表示未传)
|
|
99
|
+
// 返回 { kind, browser?, source?, detected, configured, override? }
|
|
100
|
+
// kind ∈ 'ok' | 'ambiguous' | 'mismatch' | 'empty'
|
|
101
|
+
// source ∈ 'override' | 'preference' | undefined
|
|
102
|
+
// ambiguous = 没设偏好 + 至少一个浏览器开了 toggle,需问用户
|
|
103
|
+
// mismatch = override/配偏好设了但未检测到对应 toggle,硬错
|
|
104
|
+
// empty = 0 浏览器开 toggle 且未设偏好/override
|
|
105
|
+
export async function selectBrowser(override = null) {
|
|
106
|
+
const detected = await detectAll();
|
|
107
|
+
const configured = readConfig().WEB_ACCESS_BROWSER || null;
|
|
108
|
+
|
|
109
|
+
// 1. 命令行 override(最高优先,单次有效)
|
|
110
|
+
if (override) {
|
|
111
|
+
const match = detected.find(b => b.id === override);
|
|
112
|
+
if (match) return { kind: 'ok', browser: match, source: 'override', detected, configured, override };
|
|
113
|
+
return { kind: 'mismatch', source: 'override', detected, configured, override };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 2. config.env preference(持久)
|
|
117
|
+
if (configured) {
|
|
118
|
+
const match = detected.find(b => b.id === configured);
|
|
119
|
+
if (match) return { kind: 'ok', browser: match, source: 'preference', detected, configured };
|
|
120
|
+
return { kind: 'mismatch', source: 'preference', detected, configured };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 3. 无偏好 —— 一律询问用户(哪怕 detected 只有一个)
|
|
124
|
+
if (detected.length === 0) {
|
|
125
|
+
return { kind: 'empty', detected, configured };
|
|
126
|
+
}
|
|
127
|
+
return { kind: 'ambiguous', detected, configured };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 兜底:扫描常用固定端口
|
|
131
|
+
// 适用场景:用户手动 --remote-debugging-port=9222 启动浏览器,
|
|
132
|
+
// 此时 DevToolsActivePort 可能不在默认 user-data-dir。
|
|
133
|
+
export async function findFallbackPort() {
|
|
134
|
+
for (const port of [9222, 9229, 9333]) {
|
|
135
|
+
if (await checkPort(port)) return port;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|