@sanqianx/project-knowledge 5.0.24 → 5.0.25

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 (401) hide show
  1. package/.agents/plugins/marketplace.json +20 -20
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/CHANGELOG.md +1361 -1353
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +349 -349
  7. package/_modules/knowledge-engine/package.json +1 -1
  8. package/_modules/knowledge-engine/src/coordinator.js +48 -3
  9. package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
  10. package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
  11. package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
  12. package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
  13. package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
  14. package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
  15. package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
  16. package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
  17. package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
  18. package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
  19. package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
  20. package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
  21. package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
  22. package/_modules/knowledge-engine/ui/app.js +23 -2
  23. package/_site/README.md +30 -30
  24. package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
  25. package/_site/_test/agent-capture-ui-test.js +101 -101
  26. package/_site/_test/ai-profile-resolver-test.js +137 -137
  27. package/_site/_test/ai-profile-test.js +75 -75
  28. package/_site/_test/ai-vendor-presets-test.js +91 -91
  29. package/_site/_test/automatic-context-test.js +70 -70
  30. package/_site/_test/automation-queue-test.js +14 -14
  31. package/_site/_test/automation-ui-test.js +40 -40
  32. package/_site/_test/background-task-registry-test.js +43 -43
  33. package/_site/_test/baseline-schema-test.js +91 -91
  34. package/_site/_test/bin-cli-test.js +230 -230
  35. package/_site/_test/bridge-adapter-test.js +95 -95
  36. package/_site/_test/bridge-consumer-service-test.js +310 -310
  37. package/_site/_test/candidate-bin-content-test.js +35 -35
  38. package/_site/_test/candidate-product-assets-test.js +13 -13
  39. package/_site/_test/candidate-release-contract-test.js +14 -14
  40. package/_site/_test/capture-hook-dedup-test.js +158 -158
  41. package/_site/_test/cdp-browser-failure-test.js +103 -103
  42. package/_site/_test/chat-claudecodeui-match-test.js +92 -92
  43. package/_site/_test/claude-executable-discovery-test.js +90 -90
  44. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  45. package/_site/_test/claude-workbench-test.js +174 -174
  46. package/_site/_test/cli-instance-management-test.js +62 -62
  47. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  48. package/_site/_test/codex-conversation-projection-test.js +60 -60
  49. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  50. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  51. package/_site/_test/codex-notify-fanout-test.js +141 -134
  52. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  53. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  54. package/_site/_test/commit-conversation-binding-test.js +99 -99
  55. package/_site/_test/commit-evidence-test.js +110 -110
  56. package/_site/_test/configuration-generation-test.js +22 -22
  57. package/_site/_test/context-pack-test.js +200 -200
  58. package/_site/_test/conversation-api-test.js +119 -119
  59. package/_site/_test/conversation-origin-test.js +15 -15
  60. package/_site/_test/conversation-store-test.js +138 -138
  61. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  62. package/_site/_test/desktop-browser-compat-test.js +28 -28
  63. package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
  64. package/_site/_test/embedding-config-test.js +32 -32
  65. package/_site/_test/explicit-commit-processor-test.js +37 -37
  66. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  67. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  68. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  69. package/_site/_test/fixtures/make-git-repos.js +92 -92
  70. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
  71. package/_site/_test/folder-picker-output-test.js +19 -19
  72. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  73. package/_site/_test/fresh-product-data-test.js +56 -56
  74. package/_site/_test/full-integration-e2e-test.js +167 -167
  75. package/_site/_test/git-path-compatibility-test.js +80 -80
  76. package/_site/_test/git-validation-test.js +81 -81
  77. package/_site/_test/glm-ui-refresh-review-test.js +313 -404
  78. package/_site/_test/harness-isolation-test.js +138 -0
  79. package/_site/_test/helpers/cdp-browser.js +383 -0
  80. package/_site/_test/helpers/cdp-network-capture.js +66 -0
  81. package/_site/_test/helpers/find-chrome.js +34 -0
  82. package/_site/_test/helpers/free-port.js +8 -0
  83. package/_site/_test/helpers/logging-ui-fixture.js +131 -0
  84. package/_site/_test/helpers/module-fixture.js +282 -0
  85. package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
  86. package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
  87. package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
  88. package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
  89. package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
  90. package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
  91. package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
  92. package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
  93. package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
  94. package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
  95. package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
  96. package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
  97. package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
  98. package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
  99. package/_site/_test/helpers/parallel-memory-shell.js +470 -0
  100. package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
  101. package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
  102. package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
  103. package/_site/_test/helpers/parallel-nav-shell.js +361 -0
  104. package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
  105. package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
  106. package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
  107. package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
  108. package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
  109. package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
  110. package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
  111. package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
  112. package/_site/_test/helpers/parallel-workbench-host.js +80 -0
  113. package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
  114. package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
  115. package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
  116. package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
  117. package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
  118. package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
  119. package/_site/_test/helpers/spawn-server.js +106 -0
  120. package/_site/_test/helpers/version-visibility-fixture.js +251 -0
  121. package/_site/_test/hook-runtime-endpoint-test.js +88 -88
  122. package/_site/_test/hook-status-repair-api-test.js +152 -152
  123. package/_site/_test/hook-trigger-test.js +85 -85
  124. package/_site/_test/hook-worktree-test.js +42 -42
  125. package/_site/_test/import-preflight-api-test.js +234 -234
  126. package/_site/_test/index-builder-test.js +41 -41
  127. package/_site/_test/index-error-display-contract-test.js +10 -10
  128. package/_site/_test/index-writer-concurrency-test.js +135 -135
  129. package/_site/_test/integration-adapters-test.js +262 -262
  130. package/_site/_test/integration-auto-repair-test.js +113 -113
  131. package/_site/_test/integration-capture-setup-test.js +118 -118
  132. package/_site/_test/integration-intent-test.js +81 -81
  133. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  134. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  135. package/_site/_test/integration-runtime-binding-test.js +473 -473
  136. package/_site/_test/integration-status-scan-test.js +29 -29
  137. package/_site/_test/integration-surface-coverage-test.js +164 -164
  138. package/_site/_test/kb-framework-test.js +57 -57
  139. package/_site/_test/kbpath-follow-test.js +63 -63
  140. package/_site/_test/knowledge-db-test.js +144 -144
  141. package/_site/_test/knowledge-engine-client-test.js +120 -109
  142. package/_site/_test/knowledge-language-control-test.js +170 -170
  143. package/_site/_test/knowledge-maintenance-test.js +40 -40
  144. package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
  145. package/_site/_test/knowledge-query-test.js +54 -54
  146. package/_site/_test/knowledge-retrieval-service-test.js +77 -77
  147. package/_site/_test/knowledge-scopes-test.js +37 -37
  148. package/_site/_test/knowledge-storage-location-test.js +28 -28
  149. package/_site/_test/knowledge-storage-startup-test.js +52 -52
  150. package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
  151. package/_site/_test/launcher-readiness-test.js +80 -80
  152. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  153. package/_site/_test/legacy-routes-removed-test.js +47 -47
  154. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  155. package/_site/_test/live-gateway-test.js +48 -48
  156. package/_site/_test/local-marketplace-test.js +16 -16
  157. package/_site/_test/log-redaction-test.js +58 -58
  158. package/_site/_test/logging-api-test.js +89 -89
  159. package/_site/_test/logging-sse-no-gap-test.js +88 -88
  160. package/_site/_test/logging-ui-test.js +107 -107
  161. package/_site/_test/markdown-delta-overlay-test.js +85 -85
  162. package/_site/_test/markdown-maintenance-api-test.js +75 -75
  163. package/_site/_test/markdown-maintenance-test.js +11 -11
  164. package/_site/_test/mcp-server-test.js +149 -149
  165. package/_site/_test/model-context-windows-test.js +89 -89
  166. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  167. package/_site/_test/module-artifact-integrity-test.js +27 -27
  168. package/_site/_test/module-boundary-test.js +30 -30
  169. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  170. package/_site/_test/module-dynamic-port-test.js +118 -118
  171. package/_site/_test/module-index-proxy-test.js +33 -33
  172. package/_site/_test/module-model-configuration-test.js +44 -44
  173. package/_site/_test/module-process-lifecycle-test.js +35 -35
  174. package/_site/_test/module-stream-proxy-test.js +34 -34
  175. package/_site/_test/module-terminal-restart-test.js +63 -63
  176. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  177. package/_site/_test/native-context-integrations-test.js +86 -86
  178. package/_site/_test/non-release-ci-test.js +34 -34
  179. package/_site/_test/offline-boundary-isolation-test.js +32 -32
  180. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  181. package/_site/_test/package-boundary-test.js +43 -43
  182. package/_site/_test/package-startup-test.js +56 -56
  183. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  184. package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
  185. package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
  186. package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
  187. package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
  188. package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
  189. package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
  190. package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
  191. package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
  192. package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
  193. package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
  194. package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
  195. package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
  196. package/_site/_test/parallel-knowledge-engine-test.js +353 -0
  197. package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
  198. package/_site/_test/parallel-knowledge-vector-test.js +168 -0
  199. package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
  200. package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
  201. package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
  202. package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
  203. package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
  204. package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
  205. package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
  206. package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
  207. package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
  208. package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
  209. package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
  210. package/_site/_test/parallel-memory-candidate-test.js +301 -0
  211. package/_site/_test/parallel-memory-manual-test.js +197 -0
  212. package/_site/_test/parallel-memory-scope-test.js +228 -0
  213. package/_site/_test/parallel-memory-source-test.js +227 -0
  214. package/_site/_test/parallel-memory-sync-test.js +238 -0
  215. package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
  216. package/_site/_test/parallel-nav-failure-test.js +119 -0
  217. package/_site/_test/parallel-nav-isolation-test.js +308 -0
  218. package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
  219. package/_site/_test/parallel-nav-offline-test.js +272 -0
  220. package/_site/_test/parallel-nav-operability-test.js +257 -0
  221. package/_site/_test/parallel-nav-switch-test.js +247 -0
  222. package/_site/_test/parallel-nav-theme-test.js +130 -0
  223. package/_site/_test/parallel-nav-viewport-test.js +310 -0
  224. package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
  225. package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
  226. package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
  227. package/_site/_test/parallel-workbench-independent-host.js +262 -0
  228. package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
  229. package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
  230. package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
  231. package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
  232. package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
  233. package/_site/_test/parallel-workbench-model-probe.js +336 -0
  234. package/_site/_test/parallel-workbench-offline-test.js +169 -0
  235. package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
  236. package/_site/_test/parallel-workbench-source-match.js +206 -0
  237. package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
  238. package/_site/_test/path-consistency-test.js +151 -151
  239. package/_site/_test/pending-sweep-test.js +9 -9
  240. package/_site/_test/post-commit-automation-test.js +87 -87
  241. package/_site/_test/pr-consumer-contract-test.js +55 -55
  242. package/_site/_test/product-brand-contract-test.js +25 -25
  243. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  244. package/_site/_test/product-empty-state-test.js +148 -0
  245. package/_site/_test/product-import-ui-test.js +219 -104
  246. package/_site/_test/product-navigation-test.js +152 -0
  247. package/_site/_test/product-progress-status-test.js +84 -84
  248. package/_site/_test/project-delete-recovery-test.js +64 -64
  249. package/_site/_test/project-domain-create-test.js +91 -0
  250. package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
  251. package/_site/_test/project-git-init-confirm-test.js +115 -0
  252. package/_site/_test/project-goal-editor-test.js +144 -144
  253. package/_site/_test/project-import-duplicate-alias-test.js +134 -0
  254. package/_site/_test/project-import-unicode-restart-test.js +140 -0
  255. package/_site/_test/project-lifecycle-transaction-test.js +106 -106
  256. package/_site/_test/project-module-partial-recovery-test.js +222 -0
  257. package/_site/_test/project-remove-running-guard-test.js +79 -79
  258. package/_site/_test/project-store-test.js +90 -90
  259. package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
  260. package/_site/_test/project-unbind-writer-guard-test.js +231 -0
  261. package/_site/_test/prompt-settings-test.js +115 -115
  262. package/_site/_test/protected-architecture-gate-test.js +122 -122
  263. package/_site/_test/refactor-characterization-test.js +36 -36
  264. package/_site/_test/registry-events-test.js +38 -38
  265. package/_site/_test/release-version-sync-test.js +115 -115
  266. package/_site/_test/requirement-binding-test.js +113 -113
  267. package/_site/_test/requirement-recorder-test.js +173 -173
  268. package/_site/_test/run-all-tests.js +173 -161
  269. package/_site/_test/runtime-endpoint-test.js +64 -64
  270. package/_site/_test/scanner-test.js +83 -83
  271. package/_site/_test/server-security-test.js +96 -96
  272. package/_site/_test/sessions-stream-test.js +93 -93
  273. package/_site/_test/shared-contracts-test.js +58 -58
  274. package/_site/_test/shared-scope-root-binding-test.js +74 -67
  275. package/_site/_test/simple-import-test.js +98 -98
  276. package/_site/_test/startup-analysis-disabled-test.js +16 -16
  277. package/_site/_test/storage-foundation-test.js +78 -78
  278. package/_site/_test/structured-logger-test.js +101 -101
  279. package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
  280. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  281. package/_site/_test/team-knowledge-store-test.js +132 -132
  282. package/_site/_test/test-server-cleanup-test.js +35 -35
  283. package/_site/_test/test-server-module-isolation-test.js +37 -37
  284. package/_site/_test/tracking-start-test.js +144 -144
  285. package/_site/_test/vector-storage-root-test.js +75 -75
  286. package/_site/_test/version-visibility-browser-probe.js +249 -0
  287. package/_site/_test/version-visibility-test.js +92 -0
  288. package/_site/_test/workbench-context-receipts-test.js +22 -22
  289. package/_site/_test/workbench-permission-test.js +117 -117
  290. package/_site/_test/zcode-capture-integration-test.js +112 -112
  291. package/_site/lib/ai-profile-resolver.js +78 -78
  292. package/_site/lib/ai-vendor-presets.js +83 -83
  293. package/_site/lib/ai-workspace.js +104 -104
  294. package/_site/lib/atomic-file.js +204 -204
  295. package/_site/lib/automation-config.js +140 -140
  296. package/_site/lib/bridge-adapter.js +260 -260
  297. package/_site/lib/bridge-consumer-service.js +402 -402
  298. package/_site/lib/capture-hook-config.js +108 -108
  299. package/_site/lib/claude-cli-runner.js +1599 -1599
  300. package/_site/lib/cli-instances.js +43 -43
  301. package/_site/lib/codex-context-status.js +17 -17
  302. package/_site/lib/codex-notify-config.js +193 -193
  303. package/_site/lib/commit-conversation-binder.js +247 -247
  304. package/_site/lib/commit-processing-ledger.js +27 -27
  305. package/_site/lib/commit-prompt.js +202 -202
  306. package/_site/lib/commit-reconciler.js +444 -444
  307. package/_site/lib/context-pack-builder.js +299 -299
  308. package/_site/lib/contracts.js +291 -291
  309. package/_site/lib/conversation-exclusions.js +204 -204
  310. package/_site/lib/conversation-query-service.js +161 -161
  311. package/_site/lib/conversation-store.js +387 -387
  312. package/_site/lib/data-dir.js +13 -13
  313. package/_site/lib/embedding-service.js +85 -85
  314. package/_site/lib/engine-connection.js +93 -93
  315. package/_site/lib/engine-knowledge-client.js +76 -72
  316. package/_site/lib/evidence-bundle.js +228 -228
  317. package/_site/lib/folder-picker-output.js +24 -24
  318. package/_site/lib/git-path.js +14 -14
  319. package/_site/lib/git-runner.js +62 -62
  320. package/_site/lib/github-team-store.js +1175 -1175
  321. package/_site/lib/hook-manager.js +203 -203
  322. package/_site/lib/index-builder.js +157 -157
  323. package/_site/lib/index-service.js +173 -173
  324. package/_site/lib/integration-auto-repair.js +77 -77
  325. package/_site/lib/integration-installer.js +1240 -1240
  326. package/_site/lib/integration-intent.js +88 -88
  327. package/_site/lib/integration-status-scan.js +99 -99
  328. package/_site/lib/integration-status-worker.js +14 -14
  329. package/_site/lib/kb-framework.js +191 -191
  330. package/_site/lib/kb-validator.js +124 -124
  331. package/_site/lib/knowledge-db.js +363 -363
  332. package/_site/lib/knowledge-promotion.js +435 -435
  333. package/_site/lib/knowledge-retrieval-service.js +401 -401
  334. package/_site/lib/knowledge-schema.js +121 -121
  335. package/_site/lib/knowledge-tool-runtime.js +366 -366
  336. package/_site/lib/live-gateway.js +78 -78
  337. package/_site/lib/llm-client.js +173 -173
  338. package/_site/lib/markdown-knowledge-indexer.js +230 -230
  339. package/_site/lib/model-context-windows.js +82 -82
  340. package/_site/lib/module-bridge.js +874 -874
  341. package/_site/lib/module-port-resolver.js +57 -57
  342. package/_site/lib/post-commit-automation.js +141 -141
  343. package/_site/lib/product-data.js +35 -35
  344. package/_site/lib/project-lifecycle-service.js +554 -554
  345. package/_site/lib/project-registry-store.js +129 -129
  346. package/_site/lib/project-store.js +268 -268
  347. package/_site/lib/prompt-registry.js +148 -148
  348. package/_site/lib/public-version-info.js +37 -0
  349. package/_site/lib/registry-events.js +16 -16
  350. package/_site/lib/requirement-adapters.js +10 -10
  351. package/_site/lib/requirement-binder.js +112 -112
  352. package/_site/lib/requirement-recorder.js +278 -278
  353. package/_site/lib/runtime-control.js +89 -89
  354. package/_site/lib/runtime-endpoint.js +160 -160
  355. package/_site/lib/scanner.js +298 -298
  356. package/_site/lib/server-app.js +1672 -1664
  357. package/_site/lib/settings-store.js +153 -153
  358. package/_site/lib/storage-layout.js +166 -166
  359. package/_site/lib/structured-logger.js +574 -574
  360. package/_site/lib/zcode-context-integration.js +60 -60
  361. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  362. package/_site/scripts/folder-picker.ps1 +156 -156
  363. package/_site/scripts/hook-trigger.js +158 -158
  364. package/_site/scripts/install-module-candidates.js +69 -69
  365. package/_site/scripts/pack-module-candidates.js +89 -89
  366. package/_site/scripts/sync-product-content.js +28 -28
  367. package/_site/scripts/sync-release-version.js +164 -164
  368. package/_site/scripts/vendor-modules.js +49 -49
  369. package/_site/scripts/verify-module-runtime.js +50 -50
  370. package/_site/scripts/verify-product-runtime.js +36 -36
  371. package/_site/scripts/verify-retrieval-gold.js +83 -83
  372. package/_site/server.js +15 -15
  373. package/_site/start.bat +26 -26
  374. package/_site/stop.bat +11 -11
  375. package/bin/project-knowledge-integrations.js +254 -254
  376. package/bin/project-knowledge-kb.js +67 -67
  377. package/bin/project-knowledge-mcp.js +201 -201
  378. package/bin/project-knowledge.js +269 -170
  379. package/claude-prompts.json +21 -21
  380. package/docs/README.zh-CN.md +207 -205
  381. package/docs/assets/architecture.svg +181 -181
  382. package/docs/assets/logo.svg +49 -49
  383. package/docs/fresh-v5-start.md +44 -44
  384. package/docs/leader-glm-workbench-playbook.md +122 -122
  385. package/docs/project-registry-schema.md +33 -33
  386. package/docs/release-5.0.16-acceptance.md +176 -176
  387. package/docs/release-5.0.17-acceptance.md +117 -117
  388. package/docs/release-5.0.19-acceptance.md +39 -39
  389. package/docs/team-knowledge-mode-a-plan.md +73 -73
  390. package/docs/testing-strategy.md +33 -33
  391. package/module-runtime-manifest.json +66 -63
  392. package/package.json +113 -110
  393. package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
  394. package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
  395. package/plugins/project-knowledge/.mcp.json +1 -1
  396. package/templates/change.md +30 -30
  397. package/templates/project-readme.md +52 -52
  398. package/ui/index.html +3 -2
  399. package/ui/version-info.css +44 -0
  400. package/ui/version-info.js +24 -0
  401. package/vendor-manifest.json +55 -55
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+ // F-ENV-LEAK-001 canonical regression: a unit test and the suite runner are
3
+ // decided by their own fixtures, never by ambient module URLs or an ambient
4
+ // KB_KNOWLEDGE_DATA_DIR inherited from the developer shell. The read-only
5
+ // sentinel below stands in for the user's live instance: it records every
6
+ // request, so a leak is observable WITHOUT ever contacting a real instance,
7
+ // and it refuses every binding as foreign — exactly the protective 409 the
8
+ // real instance gave, which must surface, never be swallowed.
9
+ const assert = require('node:assert/strict');
10
+ const fs = require('node:fs');
11
+ const os = require('node:os');
12
+ const path = require('node:path');
13
+ const http = require('node:http');
14
+ const { spawn } = require('node:child_process');
15
+ const { StorageLayout } = require('../lib/storage-layout');
16
+ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
17
+
18
+ const MODULE_URL_VARS = ['KB_KNOWLEDGE_ENGINE_URL', 'KB_VECTORHUB_URL', 'KB_TERMINAL_URL', 'KB_EVENTBRIDGE_URL'];
19
+ const REPO = path.resolve(__dirname, '..', '..');
20
+
21
+ // The sentinel lives in THIS process, so children must be spawned
22
+ // asynchronously: a synchronous spawn would freeze the event loop and the
23
+ // sentinel could never answer (the leak-probe child would just time out).
24
+ function run(file, args, options, timeoutMs) {
25
+ return new Promise(resolve => {
26
+ const child = spawn(file, args, { ...options, windowsHide: true });
27
+ let out = '', err = '';
28
+ child.stdout.on('data', b => { out += b; }); child.stderr.on('data', b => { err += b; });
29
+ const timer = setTimeout(() => { child.kill(); resolve({ status: null, timedOut: true, out, err }); }, timeoutMs);
30
+ child.once('error', error => { clearTimeout(timer); resolve({ status: null, error, out, err }); });
31
+ child.once('close', status => { clearTimeout(timer); resolve({ status, out, err }); });
32
+ });
33
+ }
34
+
35
+ (async () => {
36
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-isolation-'));
37
+ const sentinelHits = [];
38
+ const sentinel = http.createServer((req, res) => {
39
+ sentinelHits.push(req.url);
40
+ res.setHeader('Content-Type', 'application/json');
41
+ if (req.url === '/api/health') return res.end(JSON.stringify({ name: 'knowledge-engine', dataSchemaVersion: 3 }));
42
+ if (req.url === '/api/projects') return res.end(JSON.stringify({ projects: [{ projectId: 'foreign', repoPath: path.join(root, 'foreign-repo'), knowledgePath: path.join(root, 'foreign-knowledge'), enabled: true }] }));
43
+ res.statusCode = 409;
44
+ res.end(JSON.stringify({ ok: false, error: { code: 'ENGINE_IDENTITY_MISMATCH' } }));
45
+ });
46
+ await new Promise(resolve => sentinel.listen(0, '127.0.0.1', resolve));
47
+ const sentinelUrl = `http://127.0.0.1:${sentinel.address().port}`;
48
+ const decoySite = path.join(root, 'decoy-site-data'), decoyKnowledge = path.join(root, 'decoy-knowledge-data');
49
+ fs.mkdirSync(decoySite); fs.mkdirSync(decoyKnowledge);
50
+ const polluted = {
51
+ ...process.env,
52
+ KB_KNOWLEDGE_ENGINE_URL: sentinelUrl, KB_VECTORHUB_URL: sentinelUrl, KB_TERMINAL_URL: sentinelUrl, KB_EVENTBRIDGE_URL: sentinelUrl,
53
+ KB_DATA_DIR: decoySite, KB_KNOWLEDGE_DATA_DIR: decoyKnowledge,
54
+ };
55
+ try {
56
+ // 1. Detector control: a client built WITHOUT an explicit engineUrl
57
+ // under the polluted env does reach the ambient address, and the
58
+ // identity guard refuses the foreign binding out loud.
59
+ const leakProbe = path.join(root, 'leak-probe.js');
60
+ fs.writeFileSync(leakProbe, `'use strict';
61
+ const { StorageLayout } = require(${JSON.stringify(path.resolve(__dirname, '../lib/storage-layout'))});
62
+ const { EngineKnowledgeClient } = require(${JSON.stringify(path.resolve(__dirname, '../lib/engine-knowledge-client'))});
63
+ (async () => {
64
+ const layout = new StorageLayout({ dataDir: process.env.KB_DATA_DIR });
65
+ const client = new EngineKnowledgeClient({ layout });
66
+ try {
67
+ await client.request('search', { query: 'probe' }, { projectId: 'foreign', repoPath: process.env.KB_DATA_DIR, knowledgePath: process.env.KB_DATA_DIR });
68
+ } catch (error) {
69
+ if (error.code !== 'ENGINE_IDENTITY_MISMATCH' || error.status !== 409) throw error;
70
+ console.log('LEAK_REJECTED_ENGINE_IDENTITY_MISMATCH');
71
+ return;
72
+ }
73
+ throw new Error('the sentinel must refuse the foreign binding');
74
+ })().catch(error => { console.error(error); process.exit(1); });
75
+ `);
76
+ const leak = await run(process.execPath, [leakProbe], { env: polluted }, 30000);
77
+ assert.equal(leak.status, 0, `detector control must fail loudly if broken: ${leak.err}`);
78
+ assert.match(leak.out, /LEAK_REJECTED_ENGINE_IDENTITY_MISMATCH/);
79
+ assert.ok(sentinelHits.length >= 1, 'the detector control must observe the leaked request');
80
+ const hitsAfterControl = sentinelHits.length;
81
+
82
+ // 2. The fixed canonical test under the same polluted env passes and
83
+ // sends not a single request to the ambient address.
84
+ const canonical = await run(process.execPath, [path.resolve(__dirname, 'knowledge-engine-client-test.js')], { env: polluted }, 120000);
85
+ assert.equal(canonical.status, 0, `canonical test must pass under a polluted environment: ${canonical.err}`);
86
+ assert.equal(sentinelHits.length, hitsAfterControl, 'the canonical test must not contact an ambient module address');
87
+
88
+ // 3. The runner scrubs its children: exclusive probe dir with the real
89
+ // (fixed) run-all-tests.js and one env-dump test file. The child of
90
+ // the runner must see no ambient module URL, must NOT see the decoy
91
+ // KB data dirs, and must get its own sandboxes instead.
92
+ const probeOut = path.join(root, 'probe-env.json');
93
+ const runnerDir = path.join(root, 'runner-probe', '_site', '_test');
94
+ fs.mkdirSync(runnerDir, { recursive: true });
95
+ fs.copyFileSync(path.resolve(__dirname, 'run-all-tests.js'), path.join(runnerDir, 'run-all-tests.js'));
96
+ fs.writeFileSync(path.join(runnerDir, 'zz-env-probe-test.js'), `'use strict';
97
+ const fs = require('node:fs');
98
+ fs.writeFileSync(process.env.PROBE_OUT, JSON.stringify({
99
+ ${MODULE_URL_VARS.map(name => `${name}: process.env.${name}`).join(', ')},
100
+ KB_DATA_DIR: process.env.KB_DATA_DIR,
101
+ KB_KNOWLEDGE_DATA_DIR: process.env.KB_KNOWLEDGE_DATA_DIR,
102
+ AI_CODING_EVENT_BRIDGE_HOME: process.env.AI_CODING_EVENT_BRIDGE_HOME,
103
+ KB_MODULES_AUTOSTART: process.env.KB_MODULES_AUTOSTART,
104
+ }));
105
+ `);
106
+ const runner = await run(process.execPath, [path.join(runnerDir, 'run-all-tests.js'), '--no-report'], {
107
+ cwd: REPO, env: { ...polluted, PROBE_OUT: probeOut },
108
+ }, 120000);
109
+ assert.equal(runner.status, 0, `runner probe must pass: ${runner.err}`);
110
+ assert.match(runner.out, /1 passed, 0 failed/);
111
+ const seen = JSON.parse(fs.readFileSync(probeOut, 'utf8'));
112
+ for (const name of MODULE_URL_VARS) assert.equal(seen[name], undefined, `runner children must not inherit ${name}`);
113
+ assert.notEqual(seen.KB_DATA_DIR, decoySite, 'runner children must not inherit the ambient site data dir');
114
+ assert.notEqual(seen.KB_KNOWLEDGE_DATA_DIR, decoyKnowledge, 'runner children must not inherit the ambient knowledge data dir');
115
+ assert.ok(seen.KB_DATA_DIR && seen.KB_DATA_DIR.includes('kb-runall-data-'), 'site data must be a per-run sandbox');
116
+ assert.ok(seen.KB_KNOWLEDGE_DATA_DIR && seen.KB_KNOWLEDGE_DATA_DIR.includes('kb-runall-knowledge-'), 'knowledge data must be a per-run sandbox');
117
+ assert.ok(seen.AI_CODING_EVENT_BRIDGE_HOME, 'bridge home must stay sandboxed');
118
+ assert.equal(seen.KB_MODULES_AUTOSTART, '0', 'module autostart must stay disabled');
119
+ assert.equal(sentinelHits.length, hitsAfterControl, 'the runner child must not contact an ambient module address');
120
+
121
+ // 4. Deliberate configuration is untouched: an explicit engineUrl still
122
+ // aims the client at the chosen address — proven by the sentinel hits
123
+ // this final control produces on purpose.
124
+ const layout = new StorageLayout({ dataDir: path.join(root, 'site-data') });
125
+ const deliberate = new EngineKnowledgeClient({ layout, engineUrl: sentinelUrl });
126
+ await assert.rejects(
127
+ deliberate.request('search', { query: 'probe' }, { projectId: 'foreign', repoPath: path.join(root, 'site-repo'), knowledgePath: path.join(root, 'site-knowledge') }),
128
+ error => error.code === 'ENGINE_IDENTITY_MISMATCH' && error.status === 409,
129
+ 'an explicitly configured client still gets the identity refusal surfaced, never swallowed',
130
+ );
131
+ assert.ok(sentinelHits.length > hitsAfterControl, 'the deliberate configuration must actually reach its target');
132
+ console.log('harness-isolation-test PASS (ambient module URLs and data dirs cannot decide tests; deliberate config and identity refusals stay loud)');
133
+ } finally {
134
+ sentinel.closeAllConnections();
135
+ await new Promise(resolve => sentinel.close(resolve));
136
+ fs.rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
137
+ }
138
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,383 @@
1
+ const fs = require('fs');
2
+ const http = require('http');
3
+ const path = require('path');
4
+ const { spawn, spawnSync } = require('child_process');
5
+ const WebSocket = require('ws');
6
+
7
+ const MAX_STREAM_TAIL_BYTES = 32 * 1024;
8
+ const DEFAULT_HTTP_TIMEOUT_MS = 1000;
9
+ // Hosted-runner cold starts (first Chrome launch of a suite, antivirus scan,
10
+ // dbus stalls) can exceed a single budget window; launch stalls are transient,
11
+ // so retry instead of failing the whole test run.
12
+ const DEFAULT_LAUNCH_TIMEOUT_MS = Number(process.env.KB_CDP_LAUNCH_TIMEOUT_MS || 30000);
13
+ const DEFAULT_LAUNCH_ATTEMPTS = Math.max(1, Number(process.env.KB_CDP_LAUNCH_ATTEMPTS || 3));
14
+
15
+ function wait(ms) {
16
+ return new Promise(resolve => setTimeout(resolve, ms));
17
+ }
18
+
19
+ async function waitFor(fn, label, timeoutMs = 15000) {
20
+ const deadline = Date.now() + timeoutMs;
21
+ let lastError;
22
+ while (Date.now() < deadline) {
23
+ try {
24
+ const result = await fn();
25
+ if (result) return result;
26
+ } catch (error) {
27
+ if (error && error.fatal) throw error;
28
+ lastError = error;
29
+ }
30
+ await wait(100);
31
+ }
32
+ throw new Error("Timed out waiting for " + label + (lastError ? ": " + lastError.message : ""));
33
+ }
34
+
35
+ function requestJson(url, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
36
+ return new Promise((resolve, reject) => {
37
+ let settled = false;
38
+ const request = http.get(url, response => {
39
+ let body = "";
40
+ response.setEncoding("utf8");
41
+ response.on("data", chunk => { body += chunk; });
42
+ response.on("end", () => {
43
+ if (settled) return;
44
+ settled = true;
45
+ clearTimeout(timer);
46
+ try { resolve(JSON.parse(body)); }
47
+ catch (error) { reject(error); }
48
+ });
49
+ });
50
+ const timer = setTimeout(() => {
51
+ if (settled) return;
52
+ settled = true;
53
+ const error = new Error("HTTP request timed out after " + timeoutMs + "ms: " + url);
54
+ error.code = "ETIMEDOUT";
55
+ request.destroy(error);
56
+ reject(error);
57
+ }, timeoutMs);
58
+ request.on("error", error => {
59
+ if (settled) return;
60
+ settled = true;
61
+ clearTimeout(timer);
62
+ reject(error);
63
+ });
64
+ });
65
+ }
66
+
67
+ function createBoundedTail(maxBytes = MAX_STREAM_TAIL_BYTES) {
68
+ let buffer = Buffer.alloc(0);
69
+ return {
70
+ push(chunk) {
71
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
72
+ if (buffer.length + data.length <= maxBytes) {
73
+ buffer = Buffer.concat([buffer, data]);
74
+ } else {
75
+ buffer = Buffer.concat([buffer.slice(buffer.length + data.length - maxBytes), data]);
76
+ }
77
+ },
78
+ text() { return buffer.toString("utf8"); },
79
+ };
80
+ }
81
+
82
+ function hasExited(child) {
83
+ return !child || child.exitCode !== null || child.signalCode !== null;
84
+ }
85
+
86
+ function isWindowsChromeLauncher(executable) {
87
+ if (process.platform !== "win32") return false;
88
+ return /(?:chrome|msedge)\.exe$/i.test(path.basename(String(executable || "")));
89
+ }
90
+
91
+ function platformLaunchArgs(platform = process.platform) {
92
+ // GitHub Linux runners occasionally leave Chrome alive without publishing
93
+ // DevToolsActivePort when its sandbox or shared-memory backing stalls.
94
+ // These flags are scoped to isolated, disposable test browsers only.
95
+ return platform === 'linux' ? ['--disable-dev-shm-usage', '--no-sandbox'] : [];
96
+ }
97
+
98
+ function waitForExit(child, timeoutMs = 3000) {
99
+ if (hasExited(child)) return Promise.resolve();
100
+ return new Promise(resolve => {
101
+ const finish = () => { clearTimeout(timer); resolve(); };
102
+ const timer = setTimeout(finish, timeoutMs);
103
+ child.once("exit", finish);
104
+ });
105
+ }
106
+
107
+ function terminateProfileProcesses(profileDir) {
108
+ if (process.platform !== "win32" || !profileDir) return;
109
+ const command = "$profile = $env:PK_CDP_PROFILE; "
110
+ + "Get-CimInstance Win32_Process -Filter \"Name = 'chrome.exe' OR Name = 'msedge.exe'\" "
111
+ + "| Where-Object { $_.CommandLine -and $_.CommandLine.IndexOf($profile, [System.StringComparison]::OrdinalIgnoreCase) -ge 0 } "
112
+ + "| ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }";
113
+ try {
114
+ spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
115
+ stdio: "ignore",
116
+ timeout: 10000,
117
+ windowsHide: true,
118
+ env: { ...process.env, PK_CDP_PROFILE: profileDir },
119
+ });
120
+ } catch {}
121
+ }
122
+
123
+ async function terminateProcessTree(child, profileDir) {
124
+ if (!hasExited(child) && process.platform === "win32" && child.pid) {
125
+ try {
126
+ spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore", timeout: 10000 });
127
+ } catch {}
128
+ }
129
+ if (!hasExited(child)) {
130
+ try { child.kill(); } catch {}
131
+ await waitForExit(child, 3000);
132
+ }
133
+ // Windows Chrome/Edge can detach from the launcher process. Clean those
134
+ // launchers by their unique profile, but do not pay for a machine-wide CIM
135
+ // scan when a non-browser failure fixture (for example node.exe) exited.
136
+ if (child && isWindowsChromeLauncher(child.spawnfile)) terminateProfileProcesses(profileDir);
137
+ }
138
+
139
+ function removeProfileDir(profileDir) {
140
+ try {
141
+ fs.rmSync(profileDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
142
+ } catch {}
143
+ return !fs.existsSync(profileDir);
144
+ }
145
+
146
+ async function cleanupSpawnedBrowser(child, profileDir) {
147
+ try {
148
+ await terminateProcessTree(child, profileDir);
149
+ } catch {}
150
+ if (profileDir) removeProfileDir(profileDir);
151
+ }
152
+
153
+ function enrichBrowserLaunchError(error, diagnostics) {
154
+ const parts = ["Failed to launch CDP browser: " + (error && error.message ? error.message : String(error))];
155
+ if (diagnostics.executable) parts.push("executable: " + diagnostics.executable);
156
+ if (diagnostics.pid) parts.push("pid: " + diagnostics.pid);
157
+ if (diagnostics.exitCode !== null && diagnostics.exitCode !== undefined) parts.push("exit code: " + diagnostics.exitCode);
158
+ if (diagnostics.signal) parts.push("signal: " + diagnostics.signal);
159
+ if (diagnostics.spawnError) parts.push("spawn error: " + diagnostics.spawnError);
160
+ if (diagnostics.profileDir) parts.push("user-data-dir: " + diagnostics.profileDir);
161
+ const stderrTail = String(diagnostics.stderrTail || "").trim();
162
+ const stdoutTail = String(diagnostics.stdoutTail || "").trim();
163
+ if (stderrTail) parts.push("stderr tail:\n" + stderrTail);
164
+ if (stdoutTail) parts.push("stdout tail:\n" + stdoutTail);
165
+ const enriched = new Error(parts.filter(Boolean).join("\n"));
166
+ if (error) {
167
+ enriched.cause = error;
168
+ if (error.code) enriched.code = error.code;
169
+ if (error.fatal) enriched.fatal = true;
170
+ }
171
+ enriched.diagnostics = diagnostics;
172
+ return enriched;
173
+ }
174
+
175
+ function describeEarlyExit(exitInfo, spawnError) {
176
+ if (spawnError) return "browser process failed to start: " + spawnError;
177
+ const code = exitInfo && exitInfo.code !== null && exitInfo.code !== undefined ? String(exitInfo.code) : "unknown";
178
+ const signal = exitInfo && exitInfo.signal ? " (signal " + exitInfo.signal + ")" : "";
179
+ return "browser exited before exposing CDP (exit code " + code + signal + ")";
180
+ }
181
+
182
+ async function waitForDevToolsPort(profileDir, state, timeoutMs, tolerateLauncherExit) {
183
+ const portFile = path.join(profileDir, "DevToolsActivePort");
184
+ return waitFor(() => {
185
+ let text;
186
+ try { text = fs.readFileSync(portFile, "utf8"); } catch {}
187
+ const port = Number.parseInt(String(text || "").split(/\r?\n/, 1)[0], 10);
188
+ if (Number.isInteger(port) && port > 0) return port;
189
+ if (state.exited && !tolerateLauncherExit) {
190
+ const error = new Error(describeEarlyExit(state.exitInfo, state.spawnError));
191
+ error.fatal = true;
192
+ throw error;
193
+ }
194
+ return null;
195
+ }, "DevToolsActivePort file", timeoutMs);
196
+ }
197
+
198
+ async function launchBrowserAttempt(options) {
199
+ const chrome = options.chrome;
200
+ const profileDir = path.resolve(options.profileDir);
201
+ const launchTimeoutMs = Number(options.launchTimeoutMs || DEFAULT_LAUNCH_TIMEOUT_MS);
202
+ const explicitPort = Number(options.debugPort) > 0 ? Number(options.debugPort) : 0;
203
+ const prependArgs = Array.isArray(options.prependArgs) ? options.prependArgs : [];
204
+ const tolerateLauncherExit = isWindowsChromeLauncher(chrome);
205
+
206
+ removeProfileDir(profileDir);
207
+ fs.mkdirSync(profileDir, { recursive: true });
208
+
209
+ const stdoutTail = createBoundedTail();
210
+ const stderrTail = createBoundedTail();
211
+ const state = { exited: false, exitInfo: null, spawnError: null };
212
+ const diagnostics = {
213
+ executable: chrome,
214
+ pid: null,
215
+ exitCode: null,
216
+ signal: null,
217
+ spawnError: null,
218
+ profileDir,
219
+ stdoutTail: "",
220
+ stderrTail: "",
221
+ };
222
+
223
+ let child = null;
224
+ let socket = null;
225
+
226
+ const failLaunch = async (error) => {
227
+ diagnostics.stdoutTail = stdoutTail.text();
228
+ diagnostics.stderrTail = stderrTail.text();
229
+ if (state.exitInfo) {
230
+ diagnostics.exitCode = state.exitInfo.code;
231
+ diagnostics.signal = state.exitInfo.signal;
232
+ }
233
+ if (state.spawnError) diagnostics.spawnError = state.spawnError;
234
+ if (socket) { try { socket.close(); } catch {} }
235
+ await cleanupSpawnedBrowser(child, profileDir);
236
+ throw enrichBrowserLaunchError(error, diagnostics);
237
+ };
238
+
239
+ try {
240
+ child = spawn(chrome, [
241
+ ...prependArgs,
242
+ ...platformLaunchArgs(),
243
+ "--headless=new",
244
+ "--disable-gpu",
245
+ "--disable-background-networking",
246
+ "--disable-default-apps",
247
+ "--disable-extensions",
248
+ "--no-first-run",
249
+ "--no-default-browser-check",
250
+ "--remote-debugging-port=" + explicitPort,
251
+ "--user-data-dir=" + profileDir,
252
+ "--window-size=" + Number(options.width || 1360) + "," + Number(options.height || 900),
253
+ "about:blank",
254
+ ], {
255
+ stdio: ["ignore", "pipe", "pipe"], windowsHide: true,
256
+ ...(options.env ? { env: options.env } : {}),
257
+ });
258
+ diagnostics.pid = child.pid;
259
+
260
+ child.stdout.on("data", chunk => stdoutTail.push(chunk));
261
+ child.stderr.on("data", chunk => stderrTail.push(chunk));
262
+ child.on("error", error => {
263
+ state.exited = true;
264
+ state.spawnError = error && error.message ? error.message : String(error);
265
+ if (!state.exitInfo) state.exitInfo = { code: null, signal: null };
266
+ });
267
+ child.on("exit", (code, signal) => {
268
+ state.exited = true;
269
+ state.exitInfo = { code, signal };
270
+ });
271
+
272
+ const debugPort = explicitPort || await waitForDevToolsPort(profileDir, state, launchTimeoutMs, tolerateLauncherExit);
273
+
274
+ const pages = await waitFor(() => {
275
+ if (state.exited && !tolerateLauncherExit) {
276
+ const error = new Error(describeEarlyExit(state.exitInfo, state.spawnError));
277
+ error.fatal = true;
278
+ throw error;
279
+ }
280
+ return requestJson("http://127.0.0.1:" + debugPort + "/json").then(items => {
281
+ const available = items.filter(item => item.type === "page");
282
+ return available.length ? available : null;
283
+ });
284
+ }, "Chrome debugging page (port " + debugPort + ")", launchTimeoutMs);
285
+ const page = pages[0];
286
+ socket = new WebSocket(page.webSocketDebuggerUrl);
287
+ await new Promise((resolve, reject) => {
288
+ socket.once("open", resolve);
289
+ socket.once("error", reject);
290
+ });
291
+
292
+ let nextId = 1;
293
+ const pending = new Map();
294
+ const exceptions = [];
295
+ const consoleErrors = [];
296
+ socket.on("message", raw => {
297
+ const message = JSON.parse(String(raw));
298
+ if (message.method === "Runtime.exceptionThrown") exceptions.push(message.params);
299
+ if (message.method === "Log.entryAdded" && message.params && message.params.entry && message.params.entry.level === "error") {
300
+ consoleErrors.push(message.params.entry);
301
+ }
302
+ if (!message.id || !pending.has(message.id)) return;
303
+ const callbacks = pending.get(message.id);
304
+ pending.delete(message.id);
305
+ if (message.error) callbacks.reject(new Error(message.error.message));
306
+ else callbacks.resolve(message.result || {});
307
+ });
308
+
309
+ function send(method, params) {
310
+ return new Promise((resolve, reject) => {
311
+ const id = nextId++;
312
+ pending.set(id, { resolve, reject });
313
+ socket.send(JSON.stringify({ id, method, params: params || {} }));
314
+ });
315
+ }
316
+
317
+ async function evaluate(expression) {
318
+ const result = await send("Runtime.evaluate", {
319
+ expression,
320
+ awaitPromise: true,
321
+ returnByValue: true,
322
+ userGesture: true,
323
+ });
324
+ if (result.exceptionDetails) {
325
+ const detail = result.exceptionDetails.exception && result.exceptionDetails.exception.description;
326
+ throw new Error(detail || result.exceptionDetails.text || "Browser evaluation failed");
327
+ }
328
+ return result.result ? result.result.value : undefined;
329
+ }
330
+
331
+ await send("Page.enable");
332
+ await send("Runtime.enable");
333
+ await send("Log.enable");
334
+ await send("Page.navigate", { url: options.url });
335
+ await waitFor(() => evaluate("document.readyState === 'complete'"), "page load");
336
+
337
+ async function screenshot(filePath) {
338
+ const result = await send("Page.captureScreenshot", { format: "png", fromSurface: true, captureBeyondViewport: false });
339
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
340
+ fs.writeFileSync(filePath, Buffer.from(result.data, "base64"));
341
+ }
342
+
343
+ async function setViewport(width, height, deviceScaleFactor = 1) {
344
+ await send("Emulation.setDeviceMetricsOverride", {
345
+ width,
346
+ height,
347
+ deviceScaleFactor,
348
+ mobile: width < 620,
349
+ screenWidth: width,
350
+ screenHeight: height,
351
+ });
352
+ }
353
+
354
+ async function close() {
355
+ try { socket.close(); } catch {}
356
+ await terminateProcessTree(child, profileDir);
357
+ removeProfileDir(profileDir);
358
+ }
359
+
360
+ return { child, socket, send, evaluate, screenshot, setViewport, exceptions, consoleErrors, close };
361
+ } catch (error) {
362
+ await failLaunch(error);
363
+ }
364
+ }
365
+
366
+ async function launchCdpBrowser(options) {
367
+ const attempts = Math.max(1, Number(options.launchAttempts || DEFAULT_LAUNCH_ATTEMPTS));
368
+ let lastError;
369
+ for (let attempt = 1; attempt <= attempts; attempt++) {
370
+ try {
371
+ return await launchBrowserAttempt(options);
372
+ } catch (error) {
373
+ lastError = error;
374
+ // Fatal failures (missing executable, deterministic early crash) never
375
+ // recover; only transient launch stalls get another attempt.
376
+ if (error && error.fatal) throw error;
377
+ if (attempt < attempts) console.log(`[cdp-browser] launch attempt ${attempt}/${attempts} failed (${String(error && error.message).split("\n")[0]}); retrying`);
378
+ }
379
+ }
380
+ throw lastError;
381
+ }
382
+
383
+ module.exports = { isWindowsChromeLauncher, platformLaunchArgs, launchCdpBrowser, requestJson, waitFor, wait };
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+ // Records what the real UI actually sent and received over HTTP.
3
+ //
4
+ // The import id and the per-module wiring payload are decided by the running
5
+ // product, so reading the UI source cannot stand in for this evidence: the
6
+ // captured request body proves which import id travelled with the request,
7
+ // and the captured response body proves which statuses the product reported.
8
+
9
+ function parseJson(text) {
10
+ if (!text) return null;
11
+ try { return JSON.parse(text); } catch { return null; }
12
+ }
13
+
14
+ function attachNetworkCapture(browser) {
15
+ const requests = [];
16
+ const byId = new Map();
17
+ const onMessage = raw => {
18
+ let message;
19
+ try { message = JSON.parse(String(raw)); } catch { return; }
20
+ const params = message.params || {};
21
+ if (message.method === 'Network.requestWillBeSent') {
22
+ const request = params.request || {};
23
+ const entry = {
24
+ id: params.requestId,
25
+ method: request.method,
26
+ url: request.url,
27
+ postData: request.postData || '',
28
+ body: parseJson(request.postData),
29
+ status: null,
30
+ };
31
+ requests.push(entry);
32
+ byId.set(params.requestId, entry);
33
+ return;
34
+ }
35
+ if (message.method === 'Network.responseReceived' && byId.has(params.requestId)) {
36
+ const response = params.response || {};
37
+ byId.get(params.requestId).status = response.status === undefined ? null : response.status;
38
+ }
39
+ };
40
+ browser.socket.on('message', onMessage);
41
+
42
+ return {
43
+ requests,
44
+ async enable() { await browser.send('Network.enable'); },
45
+ find(predicate) { return requests.filter(predicate); },
46
+ lastApi(method, pathname) {
47
+ const matches = requests.filter(entry => entry.method === method && entry.url.includes(pathname));
48
+ return matches.length ? matches[matches.length - 1] : null;
49
+ },
50
+ async body(entry) {
51
+ if (!entry) return null;
52
+ for (let attempt = 0; attempt < 20; attempt += 1) {
53
+ try {
54
+ const result = await browser.send('Network.getResponseBody', { requestId: entry.id });
55
+ return parseJson(result.body);
56
+ } catch {
57
+ await new Promise(resolve => setTimeout(resolve, 100));
58
+ }
59
+ }
60
+ return null;
61
+ },
62
+ detach() { browser.socket.off('message', onMessage); },
63
+ };
64
+ }
65
+
66
+ module.exports = { attachNetworkCapture };
@@ -0,0 +1,34 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function buildCandidates() {
5
+ return [
6
+ ['env', process.env.KB_CHROME_PATH],
7
+ ['chrome-localappdata', process.env.LOCALAPPDATA && path.join(process.env.LOCALAPPDATA, 'Google', 'Chrome', 'Application', 'chrome.exe')],
8
+ ['chrome-programfiles', process.env.PROGRAMFILES && path.join(process.env.PROGRAMFILES, 'Google', 'Chrome', 'Application', 'chrome.exe')],
9
+ ['chrome-programfiles-x86', process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Google', 'Chrome', 'Application', 'chrome.exe')],
10
+ ['edge-programfiles', process.env.PROGRAMFILES && path.join(process.env.PROGRAMFILES, 'Microsoft', 'Edge', 'Application', 'msedge.exe')],
11
+ ['edge-programfiles-x86', process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Microsoft', 'Edge', 'Application', 'msedge.exe')],
12
+ ['linux-google-chrome', '/usr/bin/google-chrome'],
13
+ ['linux-google-chrome-stable', '/usr/bin/google-chrome-stable'],
14
+ ['linux-chromium', '/usr/bin/chromium'],
15
+ ['linux-chromium-browser', '/usr/bin/chromium-browser'],
16
+ ['macos-chrome', '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'],
17
+ ].filter(entry => entry[1]);
18
+ }
19
+
20
+ function findChromeDetailed() {
21
+ const candidates = buildCandidates();
22
+ for (const [source, candidate] of candidates) {
23
+ if (fs.existsSync(candidate)) return { path: candidate, source };
24
+ }
25
+ const error = new Error(`Chrome/Chromium not found; checked: ${candidates.map(entry => entry[1]).join(', ')}`);
26
+ error.code = 'KB_CHROME_NOT_FOUND';
27
+ throw error;
28
+ }
29
+
30
+ function findChrome() {
31
+ return findChromeDetailed().path;
32
+ }
33
+
34
+ module.exports = { findChrome, findChromeDetailed };
@@ -0,0 +1,8 @@
1
+ const net = require('node:net');
2
+ module.exports = async function freePort() {
3
+ const probe = net.createServer();
4
+ await new Promise((resolve, reject) => { probe.once('error', reject); probe.listen(0, '127.0.0.1', resolve); });
5
+ const port = probe.address().port;
6
+ await new Promise(resolve => probe.close(resolve));
7
+ return port;
8
+ };