@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.
Files changed (253) hide show
  1. package/bin/cli.js +1435 -1107
  2. package/package.json +29 -29
  3. package/templates/qoder/agents/insight-planning.md +1 -1
  4. package/templates/qoder/agents/insight-research.md +28 -15
  5. package/templates/qoder/commands/optional/wl-insight.md +161 -161
  6. package/templates/qoder/commands/optional/wl-report.md +20 -39
  7. package/templates/qoder/commands/optional/wl-status.md +4 -4
  8. package/templates/qoder/commands/wl-code.md +2 -2
  9. package/templates/qoder/commands/wl-design.md +6 -6
  10. package/templates/qoder/commands/wl-init.md +3 -3
  11. package/templates/qoder/commands/wl-prd.md +26 -13
  12. package/templates/qoder/commands/wl-req.md +43 -0
  13. package/templates/qoder/commands/wl-search.md +78 -47
  14. package/templates/qoder/commands/wl-task.md +343 -40
  15. package/templates/qoder/commands/wl-test.md +43 -17
  16. package/templates/qoder/config.yaml +52 -14
  17. package/templates/qoder/hooks/post-tool-use.py +181 -0
  18. package/templates/qoder/hooks/session-start.py +94 -23
  19. package/templates/qoder/hooks/stop-eval.py +207 -0
  20. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  21. package/templates/qoder/rules/wl-pipeline.md +85 -49
  22. package/templates/qoder/scripts/README.md +102 -0
  23. package/templates/qoder/scripts/capability/__init__.py +26 -0
  24. package/templates/qoder/scripts/capability/__main__.py +72 -0
  25. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  26. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  27. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  28. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  29. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  30. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  31. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  32. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  33. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  34. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  35. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  36. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  37. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  38. package/templates/qoder/scripts/capability/chain.py +92 -0
  39. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  40. package/templates/qoder/scripts/capability/registry.py +246 -0
  41. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  42. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  43. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  44. package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
  45. package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
  46. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  47. package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
  48. package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
  49. package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
  50. package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
  51. package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
  52. package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
  53. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  54. package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
  55. package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
  56. package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
  57. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  58. package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
  59. package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
  60. package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
  61. package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
  62. package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
  63. package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
  64. package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
  65. package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
  66. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  68. package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
  69. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  70. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  71. package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
  72. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  73. package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
  74. package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
  75. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  76. package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
  77. package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
  78. package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
  79. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  80. package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
  81. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  82. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  83. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  84. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  85. package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
  86. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  87. package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
  88. package/templates/qoder/scripts/domain/report/__init__.py +11 -0
  89. package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
  90. package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
  91. package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
  92. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  93. package/templates/qoder/scripts/domain/report/role.py +39 -0
  94. package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
  95. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  96. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  97. package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
  98. package/templates/qoder/scripts/domain/requirement/req.py +228 -0
  99. package/templates/qoder/scripts/domain/task/__init__.py +11 -0
  100. package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
  101. package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
  102. package/templates/qoder/scripts/domain/task/task.py +229 -0
  103. package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
  104. package/templates/qoder/scripts/domain/task/task_query.py +162 -0
  105. package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
  106. package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
  107. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  108. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  109. package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
  110. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  111. package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
  112. package/templates/qoder/scripts/foundation/core/config.py +187 -0
  113. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
  114. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  115. package/templates/qoder/scripts/foundation/data/contract.py +317 -0
  116. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  117. package/templates/qoder/scripts/foundation/data/result.py +223 -0
  118. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
  119. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  120. package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
  121. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
  122. package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
  123. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
  124. package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
  125. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  126. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  127. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
  128. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  129. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  130. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  131. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  132. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  133. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  134. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  135. package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
  136. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  137. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  138. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  139. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  140. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  141. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  142. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  143. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  144. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  145. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  146. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  147. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  148. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  153. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  154. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  155. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  156. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  157. package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
  158. package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
  159. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  160. package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
  161. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  162. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  163. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  164. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  165. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  166. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  167. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  168. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  169. package/templates/qoder/scripts/validation/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  171. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  172. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  173. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  174. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  175. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  176. package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
  177. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  178. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  179. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  180. package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
  181. package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
  182. package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
  183. package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
  184. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  185. package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
  186. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  187. package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
  188. package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
  189. package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
  190. package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
  191. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  192. package/templates/qoder/settings.json +23 -2
  193. package/templates/qoder/skills/design-import/SKILL.md +235 -226
  194. package/templates/qoder/skills/design-review/SKILL.md +91 -82
  195. package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
  196. package/templates/qoder/skills/prd-review/SKILL.md +8 -6
  197. package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
  198. package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
  199. package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
  200. package/templates/qoder/skills/test-generator/SKILL.md +9 -7
  201. package/templates/qoder/skills/wl-code/SKILL.md +25 -13
  202. package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
  203. package/templates/qoder/skills/wl-design/SKILL.md +7 -5
  204. package/templates/qoder/skills/wl-init/SKILL.md +8 -8
  205. package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
  206. package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
  207. package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
  208. package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
  209. package/templates/qoder/skills/wl-report/SKILL.md +27 -25
  210. package/templates/qoder/skills/wl-search/SKILL.md +151 -80
  211. package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
  212. package/templates/qoder/skills/wl-status/SKILL.md +50 -18
  213. package/templates/qoder/skills/wl-task/SKILL.md +94 -12
  214. package/templates/qoder/skills/wl-test/SKILL.md +154 -45
  215. package/templates/qoder/templates/prd-full-template.md +7 -0
  216. package/templates/root/AGENTS.md +275 -237
  217. package/templates/root/requirements.txt +3 -0
  218. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  219. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  220. package/templates/qoder/hooks/inject-workflow-state.py +0 -169
  221. package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
  222. package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
  223. package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
  224. package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
  225. package/templates/qoder/scripts/check_carriers.py +0 -238
  226. package/templates/qoder/scripts/check_mcp.py +0 -298
  227. package/templates/qoder/scripts/check_mcp_launch.py +0 -183
  228. package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
  229. package/templates/qoder/scripts/collect_prds.py +0 -31
  230. package/templates/qoder/scripts/common/mentions.py +0 -134
  231. package/templates/qoder/scripts/common/utf8.py +0 -38
  232. package/templates/qoder/scripts/extract_api_params.py +0 -246
  233. package/templates/qoder/scripts/extract_routes.py +0 -54
  234. package/templates/qoder/scripts/extract_routes_tree.py +0 -78
  235. package/templates/qoder/scripts/handoff.py +0 -22
  236. package/templates/qoder/scripts/init_developer.py +0 -76
  237. package/templates/qoder/scripts/kg_mcp_server.py +0 -801
  238. package/templates/qoder/scripts/kg_semantic.py +0 -150
  239. package/templates/qoder/scripts/mcp_launcher.py +0 -414
  240. package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
  241. package/templates/qoder/scripts/parse_prds.py +0 -33
  242. package/templates/qoder/scripts/role.py +0 -51
  243. package/templates/qoder/scripts/sync_carriers.py +0 -259
  244. package/templates/qoder/scripts/task.py +0 -1261
  245. package/templates/qoder/scripts/workspace_init.py +0 -102
  246. package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
  247. package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
  248. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  249. /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
  250. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  251. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  252. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  253. /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
@@ -1,196 +1,208 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 全自动登录: 获取验证码 ddddocr识别 页面JS加密提交 → 保存token
4
- 登录态保存到 auth-state.json, 后续测试复用(无需再次识别验证码)
5
- """
6
- import base64
7
- import json
8
- import os
9
- import sys
10
- import requests
11
- import ddddocr
12
-
13
- BASE_URL = 'https://icsqas.inforecloud.com'
14
- API_PREFIX = '/test-api'
15
- USERNAME = '15888888887'
16
- PASSWORD = 'admin123'
17
- CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
18
-
19
- # RSA公钥 (从 .env.production)
20
- RSA_PUBLIC_KEY_B64 = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN/EkuOg1gq/t2/PUI8GpcJCsCFQgHkSHUEdk7ZPrEQYd8IM1hFkHmDJrCOQ+qUPqKHG9a5BAI44yE6FuPI9Vvqu2ZrCww/zryR7A+2n5jaKIG2rJG9KHDzT5mAVOJrlhBf8L2i7wZc8eWrNg0md0BlwEowQyVEEj9oIBlw/nqTwIDAQAB'
21
-
22
- # 保存路径
23
- SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
24
-
25
-
26
- def _read_developer():
27
- """读 .qoder/.developer 拿当前开发者名, 用于身份隔离。
28
- 多人共用 workspace 时, auth-state 必须按开发者分目录, 否则会串登录态。
29
- 没有身份 抛异常 (绝不 fallback 到 hupan/default, 防串号)。"""
30
- dev_file = os.path.join(SCRIPTS_DIR, '..', '..', '.qoder', '.developer')
31
- if os.path.isfile(dev_file):
32
- for ln in open(dev_file, encoding='utf-8'):
33
- if ln.strip().startswith('name='):
34
- return ln.strip().split('=', 1)[1].strip()
35
- raise RuntimeError(
36
- '未检测到开发者身份(.qoder/.developer)。请先运行 /wl-init 注册身份。'
37
- ' (auth-state 按开发者分目录, 没身份无法隔离 → 拒绝执行防串号)'
38
- )
39
-
40
-
41
- STATE_FILE = os.path.join(SCRIPTS_DIR, '..', '..', 'workspace', 'members',
42
- _read_developer(), 'auth-state.json')
43
-
44
- out = []
45
- def log(s=''):
46
- out.append(s)
47
- print(s)
48
-
49
-
50
- def get_captcha():
51
- """获取验证码图片 + uuid"""
52
- resp = requests.get(BASE_URL + API_PREFIX + '/auth/code', timeout=10)
53
- data = resp.json().get('data', {})
54
- return data.get('uuid'), data.get('img'), data.get('captchaEnabled', False)
55
-
56
-
57
- def recognize_captcha(img_b64):
58
- """用 ddddocr 识别验证码图片, 并解析数学题(ICS 是 a op b = ? 形式).
59
- 返回算出的答案字符串; 解析失败返回 None."""
60
- import re as _re
61
- ocr = ddddocr.DdddOcr(show_ad=False)
62
- img_bytes = base64.b64decode(img_b64)
63
- raw = ocr.classification(img_bytes)
64
- # 规范化运算符 (x/X/× *, ÷ /), 去掉 = ? 尾部
65
- expr = raw.replace('x', '*').replace('X', '*').replace('×', '*').replace('÷', '/')
66
- expr = expr.rstrip('=??').strip()
67
- m = _re.match(r'^\s*(\d+)\s*([+\-*/])\s*(\d+)\s*$', expr)
68
- if not m:
69
- return None
70
- a, op, b = int(m.group(1)), m.group(2), int(m.group(3))
71
- if op == '+':
72
- ans = a + b
73
- elif op == '-':
74
- ans = a - b
75
- elif op == '*':
76
- ans = a * b
77
- elif op == '/':
78
- ans = a // b if b else None
79
- else:
80
- return None
81
- return str(ans) if ans is not None else None
82
-
83
-
84
- def encrypt_login(username, password, code, uuid):
85
- """用 RSA+AES 加密登录请求 (复现前端加密逻辑)。
86
- 使用 cryptography (替代 pycryptodome)。"""
87
- import os as _os
88
- from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
89
- from cryptography.hazmat.primitives.padding import PKCS7
90
- from cryptography.hazmat.primitives.asymmetric import padding as asym_padding
91
- from cryptography.hazmat.primitives.serialization import load_der_public_key
92
-
93
- # 1. 生成随机 AES 密钥 (前端用 randomUUID = 32 hex chars)
94
- aes_key = _os.urandom(16).hex().encode('utf-8') # 32 bytes
95
-
96
- # 2. AES-ECB-PKCS7 加密请求体
97
- login_body = json.dumps({
98
- 'username': username,
99
- 'password': password,
100
- 'grantType': 'password',
101
- 'tenantId': '000000',
102
- 'clientId': CLIENT_ID,
103
- 'code': code,
104
- 'uuid': uuid,
105
- }, ensure_ascii=False).encode('utf-8')
106
-
107
- padder = PKCS7(128).padder()
108
- padded = padder.update(login_body) + padder.finalize()
109
- cipher = Cipher(algorithms.AES(aes_key), modes.ECB())
110
- encryptor = cipher.encryptor()
111
- encrypted_body = base64.b64encode(encryptor.update(padded) + encryptor.finalize()).decode()
112
-
113
- # 3. RSA 加密 AES 密钥 (前端: encrypt(encryptBase64(aesKey)))
114
- aes_key_b64 = base64.b64encode(aes_key).decode()
115
- pub_key = load_der_public_key(base64.b64decode(RSA_PUBLIC_KEY_B64))
116
- encrypted_key_bytes = pub_key.encrypt(aes_key_b64.encode('utf-8'), asym_padding.PKCS1v15())
117
- encrypted_key = base64.b64encode(encrypted_key_bytes).decode()
118
-
119
- return encrypted_body, encrypted_key
120
-
121
-
122
- def login():
123
- """全自动登录流程"""
124
- log('=== 全自动登录 ===')
125
-
126
- # 1. 获取验证码
127
- uuid, img_b64, captcha_enabled = get_captcha()
128
- log('验证码: enabled=%s, uuid=%s' % (captcha_enabled, uuid[:8] if uuid else 'N/A'))
129
-
130
- if not captcha_enabled:
131
- log('验证码未开启, 直接登录')
132
- code = ''
133
- uuid = ''
134
- else:
135
- # 2. OCR 识别 + 数学题解析
136
- code = recognize_captcha(img_b64)
137
- log('OCR识别验证码: %s' % code)
138
- if code is None:
139
- # ddddocr 没读对(数学题识别不稳定), 本次放弃, 让外层重试
140
- log('验证码识别失败(数学题未解析), 重试')
141
- return None
142
-
143
- # 3. 加密 + 登录
144
- try:
145
- encrypted_body, encrypted_key = encrypt_login(USERNAME, PASSWORD, code, uuid)
146
- log('加密完成, 发送登录请求...')
147
-
148
- resp = requests.post(
149
- BASE_URL + API_PREFIX + '/auth/login',
150
- data=encrypted_body,
151
- headers={
152
- 'Content-Type': 'application/json',
153
- 'encrypt-key': encrypted_key,
154
- },
155
- timeout=10,
156
- )
157
- result = resp.json()
158
- log('登录响应: code=%s, msg=%s' % (result.get('code'), result.get('msg')))
159
-
160
- if result.get('code') == 200 and result.get('data', {}).get('access_token'):
161
- token = result['data']['access_token']
162
- log('✓ 登录成功! token=%s...' % token[:20])
163
-
164
- # 保存登录态
165
- os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
166
- with open(STATE_FILE, 'w', encoding='utf-8') as f:
167
- json.dump({
168
- 'access_token': token,
169
- 'base_url': BASE_URL,
170
- 'api_prefix': API_PREFIX,
171
- 'saved_at': __import__('datetime').datetime.now().isoformat(),
172
- }, f, indent=2)
173
- log('登录态已保存: %s' % STATE_FILE)
174
- return token
175
- else:
176
- log('✗ 登录失败: %s' % json.dumps(result, ensure_ascii=False)[:200])
177
- log(' 可能验证码识别错误, 重试...')
178
- return None
179
- except Exception as e:
180
- log('登录异常: %s' % str(e))
181
- return None
182
-
183
-
184
- if __name__ == '__main__':
185
- # 数学题验证码识别不稳定, 多重试几次 (实测 ~30% 一次读对, 20 次基本必中)
186
- import time as _time
187
- for attempt in range(20):
188
- log('--- 尝试 %d ---' % (attempt + 1))
189
- token = login()
190
- if token:
191
- break
192
- _time.sleep(0.3)
193
- else:
194
- log('20次尝试均失败, 请检查账号/密码/验证码/网络')
195
-
196
- open(os.path.join(SCRIPTS_DIR, '_login_log.txt'), 'w', encoding='utf-8').write('\n'.join(out))
1
+ # -*- coding: utf-8 -*-
2
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
3
+ import os as _o, sys as _s
4
+ _f = _o.path.abspath(__file__)
5
+ for _ in range(10):
6
+ _f = _o.path.dirname(_f)
7
+ _cp = _o.path.join(_f, 'foundation')
8
+ if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
9
+ break
10
+ if _cp not in _s.path: _s.path.insert(0, _cp)
11
+ from bootstrap import setup; setup()
12
+ from foundation.core.paths import get_repo_root
13
+
14
+ """
15
+ 全自动登录: 获取验证码 → ddddocr识别 → 页面JS加密提交 → 保存token
16
+ 登录态保存到 auth-state.json, 后续测试复用(无需再次识别验证码)。
17
+ """
18
+ import base64
19
+ import json
20
+ import os
21
+ import sys
22
+ import requests
23
+ import ddddocr
24
+
25
+ BASE_URL = 'https://icsqas.inforecloud.com'
26
+ API_PREFIX = '/test-api'
27
+ USERNAME = '15888888887'
28
+ PASSWORD = 'admin123'
29
+ CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
30
+
31
+ # RSA公钥 (从 .env.production)
32
+ RSA_PUBLIC_KEY_B64 = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN/EkuOg1gq/t2/PUI8GpcJCsCFQgHkSHUEdk7ZPrEQYd8IM1hFkHmDJrCOQ+qUPqKHG9a5BAI44yE6FuPI9Vvqu2ZrCww/zryR7A+2n5jaKIG2rJG9KHDzT5mAVOJrlhBf8L2i7wZc8eWrNg0md0BlwEowQyVEEj9oIBlw/nqTwIDAQAB'
33
+
34
+ # 保存路径
35
+ SCRIPTS_DIR = get_repo_root()
36
+
37
+
38
+ def _read_developer():
39
+ """读 .qoder/.developer 拿当前开发者名, 用于身份隔离。
40
+ 多人共用 workspace 时, auth-state 必须按开发者分目录, 否则会串登录态。
41
+ 没有身份 抛异常 (绝不 fallback hupan/default, 防串号)。"""
42
+ dev_file = os.path.join(SCRIPTS_DIR, '..', '..', '.qoder', '.developer')
43
+ if os.path.isfile(dev_file):
44
+ for ln in open(dev_file, encoding='utf-8'):
45
+ if ln.strip().startswith('name='):
46
+ return ln.strip().split('=', 1)[1].strip()
47
+ raise RuntimeError(
48
+ '未检测到开发者身份(.qoder/.developer)。请先运行 /wl-init 注册身份。'
49
+ ' (auth-state 按开发者分目录, 没身份无法隔离 → 拒绝执行防串号)'
50
+ )
51
+
52
+
53
+ STATE_FILE = os.path.join(SCRIPTS_DIR, '..', '..', 'workspace', 'members',
54
+ _read_developer(), 'auth-state.json')
55
+
56
+ out = []
57
+ def log(s=''):
58
+ out.append(s)
59
+ print(s)
60
+
61
+
62
+ def get_captcha():
63
+ """获取验证码图片 + uuid"""
64
+ resp = requests.get(BASE_URL + API_PREFIX + '/auth/code', timeout=10)
65
+ data = resp.json().get('data', {})
66
+ return data.get('uuid'), data.get('img'), data.get('captchaEnabled', False)
67
+
68
+
69
+ def recognize_captcha(img_b64):
70
+ """用 ddddocr 识别验证码图片, 并解析数学题(ICS 是 a op b = ? 形式).
71
+ 返回算出的答案字符串; 解析失败返回 None."""
72
+ import re as _re
73
+ ocr = ddddocr.DdddOcr(show_ad=False)
74
+ img_bytes = base64.b64decode(img_b64)
75
+ raw = ocr.classification(img_bytes)
76
+ # 规范化运算符 (x/X/× → *, ÷ → /), 去掉 = ? 尾部
77
+ expr = raw.replace('x', '*').replace('X', '*').replace('×', '*').replace('÷', '/')
78
+ expr = expr.rstrip('=??').strip()
79
+ m = _re.match(r'^\s*(\d+)\s*([+\-*/])\s*(\d+)\s*$', expr)
80
+ if not m:
81
+ return None
82
+ a, op, b = int(m.group(1)), m.group(2), int(m.group(3))
83
+ if op == '+':
84
+ ans = a + b
85
+ elif op == '-':
86
+ ans = a - b
87
+ elif op == '*':
88
+ ans = a * b
89
+ elif op == '/':
90
+ ans = a // b if b else None
91
+ else:
92
+ return None
93
+ return str(ans) if ans is not None else None
94
+
95
+
96
+ def encrypt_login(username, password, code, uuid):
97
+ """用 RSA+AES 加密登录请求 (复现前端加密逻辑)。
98
+ 使用 cryptography 库 (替代 pycryptodome)。"""
99
+ import os as _os
100
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
101
+ from cryptography.hazmat.primitives.padding import PKCS7
102
+ from cryptography.hazmat.primitives.asymmetric import padding as asym_padding
103
+ from cryptography.hazmat.primitives.serialization import load_der_public_key
104
+
105
+ # 1. 生成随机 AES 密钥 (前端用 randomUUID = 32 hex chars)
106
+ aes_key = _os.urandom(16).hex().encode('utf-8') # 32 bytes
107
+
108
+ # 2. AES-ECB-PKCS7 加密请求体
109
+ login_body = json.dumps({
110
+ 'username': username,
111
+ 'password': password,
112
+ 'grantType': 'password',
113
+ 'tenantId': '000000',
114
+ 'clientId': CLIENT_ID,
115
+ 'code': code,
116
+ 'uuid': uuid,
117
+ }, ensure_ascii=False).encode('utf-8')
118
+
119
+ padder = PKCS7(128).padder()
120
+ padded = padder.update(login_body) + padder.finalize()
121
+ cipher = Cipher(algorithms.AES(aes_key), modes.ECB())
122
+ encryptor = cipher.encryptor()
123
+ encrypted_body = base64.b64encode(encryptor.update(padded) + encryptor.finalize()).decode()
124
+
125
+ # 3. RSA 加密 AES 密钥 (前端: encrypt(encryptBase64(aesKey)))
126
+ aes_key_b64 = base64.b64encode(aes_key).decode()
127
+ pub_key = load_der_public_key(base64.b64decode(RSA_PUBLIC_KEY_B64))
128
+ encrypted_key_bytes = pub_key.encrypt(aes_key_b64.encode('utf-8'), asym_padding.PKCS1v15())
129
+ encrypted_key = base64.b64encode(encrypted_key_bytes).decode()
130
+
131
+ return encrypted_body, encrypted_key
132
+
133
+
134
+ def login():
135
+ """全自动登录流程"""
136
+ log('=== 全自动登录 ===')
137
+
138
+ # 1. 获取验证码
139
+ uuid, img_b64, captcha_enabled = get_captcha()
140
+ log('验证码: enabled=%s, uuid=%s' % (captcha_enabled, uuid[:8] if uuid else 'N/A'))
141
+
142
+ if not captcha_enabled:
143
+ log('验证码未开启, 直接登录')
144
+ code = ''
145
+ uuid = ''
146
+ else:
147
+ # 2. OCR 识别 + 数学题解析
148
+ code = recognize_captcha(img_b64)
149
+ log('OCR识别验证码: %s' % code)
150
+ if code is None:
151
+ # ddddocr 没读对(数学题识别不稳定), 本次放弃, 让外层重试
152
+ log('验证码识别失败(数学题未解析), 重试')
153
+ return None
154
+
155
+ # 3. 加密 + 登录
156
+ try:
157
+ encrypted_body, encrypted_key = encrypt_login(USERNAME, PASSWORD, code, uuid)
158
+ log('加密完成, 发送登录请求...')
159
+
160
+ resp = requests.post(
161
+ BASE_URL + API_PREFIX + '/auth/login',
162
+ data=encrypted_body,
163
+ headers={
164
+ 'Content-Type': 'application/json',
165
+ 'encrypt-key': encrypted_key,
166
+ },
167
+ timeout=10,
168
+ )
169
+ result = resp.json()
170
+ log('登录响应: code=%s, msg=%s' % (result.get('code'), result.get('msg')))
171
+
172
+ if result.get('code') == 200 and result.get('data', {}).get('access_token'):
173
+ token = result['data']['access_token']
174
+ log('✓ 登录成功! token=%s...' % token[:20])
175
+
176
+ # 保存登录态
177
+ os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
178
+ with open(STATE_FILE, 'w', encoding='utf-8') as f:
179
+ json.dump({
180
+ 'access_token': token,
181
+ 'base_url': BASE_URL,
182
+ 'api_prefix': API_PREFIX,
183
+ 'saved_at': __import__('datetime').datetime.now().isoformat(),
184
+ }, f, indent=2)
185
+ log('登录态已保存: %s' % STATE_FILE)
186
+ return token
187
+ else:
188
+ log(' 登录失败: %s' % json.dumps(result, ensure_ascii=False)[:200])
189
+ log(' 可能验证码识别错误, 重试...')
190
+ return None
191
+ except Exception as e:
192
+ log('登录异常: %s' % str(e))
193
+ return None
194
+
195
+
196
+ if __name__ == '__main__':
197
+ # 数学题验证码识别不稳定, 多重试几次 (实测 ~30% 一次读对, 20 次基本必中)
198
+ import time as _time
199
+ for attempt in range(20):
200
+ log('--- 尝试 %d ---' % (attempt + 1))
201
+ token = login()
202
+ if token:
203
+ break
204
+ _time.sleep(0.3)
205
+ else:
206
+ log('20次尝试均失败, 请检查账号/密码/验证码/网络')
207
+
208
+ open(os.path.join(SCRIPTS_DIR, '_login_log.txt'), 'w', encoding='utf-8').write('\n'.join(out))
@@ -1,4 +1,15 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
3
+ import os as _o, sys as _s
4
+ _f = _o.path.abspath(__file__)
5
+ for _ in range(10):
6
+ _f = _o.path.dirname(_f)
7
+ _cp = _o.path.join(_f, 'foundation')
8
+ if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
9
+ break
10
+ if _cp not in _s.path: _s.path.insert(0, _cp)
11
+ from bootstrap import setup; setup()
12
+
2
13
  """
3
14
  Automated test runner using Playwright (headless) + knowledge graph.
4
15
 
@@ -23,9 +34,9 @@ import sys
23
34
  import time
24
35
  import yaml
25
36
 
26
- SCRIPTS = os.path.dirname(os.path.abspath(__file__))
37
+ SCRIPTS = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 子包→scripts/根
27
38
  sys.path.insert(0, SCRIPTS)
28
- from common.paths import DATA_INDEX_DIR, PROJECT_ROOT
39
+ from foundation.core.paths import DATA_INDEX_DIR, PROJECT_ROOT
29
40
 
30
41
  INDEX_DIR = str(DATA_INDEX_DIR)
31
42
  CODE_DIR = str(PROJECT_ROOT / 'data' / 'code')