@sanqianx/project-knowledge 5.0.24 → 5.0.26
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/.agents/plugins/marketplace.json +20 -20
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +1361 -1353
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +349 -349
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/coordinator.js +48 -3
- package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
- package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
- package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
- package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
- package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
- package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
- package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
- package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
- package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
- package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
- package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
- package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
- package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
- package/_modules/knowledge-engine/ui/app.js +23 -2
- package/_site/README.md +30 -30
- package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
- package/_site/_test/agent-capture-ui-test.js +101 -101
- package/_site/_test/ai-profile-resolver-test.js +137 -137
- package/_site/_test/ai-profile-test.js +75 -75
- package/_site/_test/ai-vendor-presets-test.js +91 -91
- package/_site/_test/automatic-context-test.js +70 -70
- package/_site/_test/automation-queue-test.js +14 -14
- package/_site/_test/automation-ui-test.js +40 -40
- package/_site/_test/background-task-registry-test.js +43 -43
- package/_site/_test/baseline-schema-test.js +91 -91
- package/_site/_test/bin-cli-test.js +230 -230
- package/_site/_test/bridge-adapter-test.js +95 -95
- package/_site/_test/bridge-consumer-service-test.js +310 -310
- package/_site/_test/candidate-bin-content-test.js +35 -35
- package/_site/_test/candidate-product-assets-test.js +13 -13
- package/_site/_test/candidate-release-contract-test.js +14 -14
- package/_site/_test/capture-hook-dedup-test.js +158 -158
- package/_site/_test/cdp-browser-failure-test.js +103 -103
- package/_site/_test/chat-claudecodeui-match-test.js +92 -92
- package/_site/_test/claude-executable-discovery-test.js +90 -90
- package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
- package/_site/_test/claude-workbench-test.js +174 -174
- package/_site/_test/cli-instance-management-test.js +62 -62
- package/_site/_test/client-capture-restart-e2e-test.js +189 -189
- package/_site/_test/codex-conversation-projection-test.js +60 -60
- package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
- package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
- package/_site/_test/codex-notify-fanout-test.js +141 -134
- package/_site/_test/codex-notify-upgrade-test.js +253 -253
- package/_site/_test/commit-boundary-freeze-test.js +147 -147
- package/_site/_test/commit-conversation-binding-test.js +99 -99
- package/_site/_test/commit-evidence-test.js +110 -110
- package/_site/_test/configuration-generation-test.js +22 -22
- package/_site/_test/context-pack-test.js +200 -200
- package/_site/_test/conversation-api-test.js +119 -119
- package/_site/_test/conversation-origin-test.js +15 -15
- package/_site/_test/conversation-store-test.js +138 -138
- package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
- package/_site/_test/desktop-browser-compat-test.js +28 -28
- package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
- package/_site/_test/embedding-config-test.js +32 -32
- package/_site/_test/explicit-commit-processor-test.js +37 -37
- package/_site/_test/fixtures/fake-browser-exit.js +10 -10
- package/_site/_test/fixtures/fake-browser-hang.js +10 -10
- package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
- package/_site/_test/fixtures/make-git-repos.js +92 -92
- package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
- package/_site/_test/folder-picker-output-test.js +19 -19
- package/_site/_test/fresh-hook-reimport-test.js +53 -53
- package/_site/_test/fresh-product-data-test.js +56 -56
- package/_site/_test/full-integration-e2e-test.js +167 -167
- package/_site/_test/git-path-compatibility-test.js +80 -80
- package/_site/_test/git-validation-test.js +81 -81
- package/_site/_test/glm-ui-refresh-review-test.js +313 -404
- package/_site/_test/harness-isolation-test.js +138 -0
- package/_site/_test/helpers/cdp-browser.js +383 -0
- package/_site/_test/helpers/cdp-network-capture.js +66 -0
- package/_site/_test/helpers/find-chrome.js +34 -0
- package/_site/_test/helpers/free-port.js +8 -0
- package/_site/_test/helpers/logging-ui-fixture.js +131 -0
- package/_site/_test/helpers/module-fixture.js +282 -0
- package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
- package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
- package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
- package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
- package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
- package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
- package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
- package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
- package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
- package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
- package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
- package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
- package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
- package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
- package/_site/_test/helpers/parallel-memory-shell.js +470 -0
- package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
- package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
- package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
- package/_site/_test/helpers/parallel-nav-shell.js +361 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
- package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
- package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
- package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
- package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
- package/_site/_test/helpers/parallel-workbench-host.js +80 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
- package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
- package/_site/_test/helpers/spawn-server.js +106 -0
- package/_site/_test/helpers/version-visibility-fixture.js +251 -0
- package/_site/_test/hook-runtime-endpoint-test.js +88 -88
- package/_site/_test/hook-status-repair-api-test.js +152 -152
- package/_site/_test/hook-trigger-test.js +85 -85
- package/_site/_test/hook-worktree-test.js +42 -42
- package/_site/_test/import-preflight-api-test.js +234 -234
- package/_site/_test/index-builder-test.js +41 -41
- package/_site/_test/index-error-display-contract-test.js +10 -10
- package/_site/_test/index-writer-concurrency-test.js +135 -135
- package/_site/_test/integration-adapters-test.js +262 -262
- package/_site/_test/integration-auto-repair-test.js +113 -113
- package/_site/_test/integration-capture-setup-test.js +118 -118
- package/_site/_test/integration-intent-test.js +81 -81
- package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
- package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
- package/_site/_test/integration-runtime-binding-test.js +473 -473
- package/_site/_test/integration-status-scan-test.js +29 -29
- package/_site/_test/integration-surface-coverage-test.js +164 -164
- package/_site/_test/kb-framework-test.js +57 -57
- package/_site/_test/kbpath-follow-test.js +63 -63
- package/_site/_test/knowledge-db-test.js +144 -144
- package/_site/_test/knowledge-engine-client-test.js +120 -109
- package/_site/_test/knowledge-language-control-test.js +170 -170
- package/_site/_test/knowledge-maintenance-test.js +40 -40
- package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
- package/_site/_test/knowledge-query-test.js +54 -54
- package/_site/_test/knowledge-retrieval-service-test.js +77 -77
- package/_site/_test/knowledge-scopes-test.js +37 -37
- package/_site/_test/knowledge-storage-location-test.js +28 -28
- package/_site/_test/knowledge-storage-startup-test.js +52 -52
- package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
- package/_site/_test/launcher-readiness-test.js +80 -80
- package/_site/_test/legacy-claude-session-alias-test.js +106 -106
- package/_site/_test/legacy-routes-removed-test.js +47 -47
- package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
- package/_site/_test/live-gateway-test.js +48 -48
- package/_site/_test/local-marketplace-test.js +16 -16
- package/_site/_test/log-redaction-test.js +58 -58
- package/_site/_test/logging-api-test.js +89 -89
- package/_site/_test/logging-sse-no-gap-test.js +88 -88
- package/_site/_test/logging-ui-test.js +107 -107
- package/_site/_test/markdown-delta-overlay-test.js +85 -85
- package/_site/_test/markdown-maintenance-api-test.js +75 -75
- package/_site/_test/markdown-maintenance-test.js +11 -11
- package/_site/_test/mcp-server-test.js +149 -149
- package/_site/_test/model-context-windows-test.js +89 -89
- package/_site/_test/module-agent-start-proxy-test.js +65 -65
- package/_site/_test/module-artifact-integrity-test.js +27 -27
- package/_site/_test/module-boundary-test.js +30 -30
- package/_site/_test/module-bridge-eventbridge-test.js +202 -202
- package/_site/_test/module-dynamic-port-test.js +118 -118
- package/_site/_test/module-index-proxy-test.js +33 -33
- package/_site/_test/module-model-configuration-test.js +44 -44
- package/_site/_test/module-process-lifecycle-test.js +35 -35
- package/_site/_test/module-stream-proxy-test.js +34 -34
- package/_site/_test/module-terminal-restart-test.js +63 -63
- package/_site/_test/module-wiring-unbind-race-test.js +59 -59
- package/_site/_test/native-context-integrations-test.js +86 -86
- package/_site/_test/non-release-ci-test.js +34 -34
- package/_site/_test/offline-boundary-isolation-test.js +32 -32
- package/_site/_test/orphaned-claim-recovery-test.js +80 -80
- package/_site/_test/package-boundary-test.js +43 -43
- package/_site/_test/package-startup-test.js +56 -56
- package/_site/_test/packaged-ui-smoke-test.js +88 -88
- package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
- package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
- package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
- package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
- package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
- package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
- package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
- package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
- package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
- package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
- package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
- package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
- package/_site/_test/parallel-knowledge-engine-test.js +353 -0
- package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
- package/_site/_test/parallel-knowledge-vector-test.js +168 -0
- package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
- package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
- package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
- package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
- package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
- package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
- package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
- package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
- package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
- package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
- package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
- package/_site/_test/parallel-memory-candidate-test.js +301 -0
- package/_site/_test/parallel-memory-manual-test.js +197 -0
- package/_site/_test/parallel-memory-scope-test.js +228 -0
- package/_site/_test/parallel-memory-source-test.js +227 -0
- package/_site/_test/parallel-memory-sync-test.js +238 -0
- package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
- package/_site/_test/parallel-nav-failure-test.js +119 -0
- package/_site/_test/parallel-nav-isolation-test.js +308 -0
- package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
- package/_site/_test/parallel-nav-offline-test.js +272 -0
- package/_site/_test/parallel-nav-operability-test.js +257 -0
- package/_site/_test/parallel-nav-switch-test.js +247 -0
- package/_site/_test/parallel-nav-theme-test.js +130 -0
- package/_site/_test/parallel-nav-viewport-test.js +310 -0
- package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
- package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
- package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
- package/_site/_test/parallel-workbench-independent-host.js +262 -0
- package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
- package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
- package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
- package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
- package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
- package/_site/_test/parallel-workbench-model-probe.js +336 -0
- package/_site/_test/parallel-workbench-offline-test.js +169 -0
- package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
- package/_site/_test/parallel-workbench-source-match.js +206 -0
- package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
- package/_site/_test/path-consistency-test.js +151 -151
- package/_site/_test/pending-sweep-test.js +9 -9
- package/_site/_test/post-commit-automation-test.js +87 -87
- package/_site/_test/pr-consumer-contract-test.js +55 -55
- package/_site/_test/product-brand-contract-test.js +25 -25
- package/_site/_test/product-diagnostics-ui-test.js +93 -93
- package/_site/_test/product-empty-state-test.js +148 -0
- package/_site/_test/product-import-ui-test.js +219 -104
- package/_site/_test/product-navigation-test.js +152 -0
- package/_site/_test/product-progress-status-test.js +84 -84
- package/_site/_test/project-delete-recovery-test.js +64 -64
- package/_site/_test/project-domain-create-test.js +91 -0
- package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
- package/_site/_test/project-git-init-confirm-test.js +115 -0
- package/_site/_test/project-goal-editor-test.js +144 -144
- package/_site/_test/project-import-duplicate-alias-test.js +134 -0
- package/_site/_test/project-import-unicode-restart-test.js +140 -0
- package/_site/_test/project-lifecycle-transaction-test.js +106 -106
- package/_site/_test/project-module-partial-recovery-test.js +222 -0
- package/_site/_test/project-remove-running-guard-test.js +79 -79
- package/_site/_test/project-store-test.js +90 -90
- package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
- package/_site/_test/project-unbind-writer-guard-test.js +231 -0
- package/_site/_test/prompt-settings-test.js +115 -115
- package/_site/_test/protected-architecture-gate-test.js +122 -122
- package/_site/_test/refactor-characterization-test.js +36 -36
- package/_site/_test/registry-events-test.js +38 -38
- package/_site/_test/release-version-sync-test.js +115 -115
- package/_site/_test/requirement-binding-test.js +113 -113
- package/_site/_test/requirement-recorder-test.js +173 -173
- package/_site/_test/run-all-tests.js +173 -161
- package/_site/_test/runtime-endpoint-test.js +64 -64
- package/_site/_test/scanner-test.js +83 -83
- package/_site/_test/server-security-test.js +96 -96
- package/_site/_test/sessions-stream-test.js +93 -93
- package/_site/_test/shared-contracts-test.js +58 -58
- package/_site/_test/shared-scope-root-binding-test.js +74 -67
- package/_site/_test/simple-import-test.js +98 -98
- package/_site/_test/startup-analysis-disabled-test.js +16 -16
- package/_site/_test/storage-foundation-test.js +78 -78
- package/_site/_test/structured-logger-test.js +101 -101
- package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
- package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
- package/_site/_test/team-knowledge-store-test.js +132 -132
- package/_site/_test/test-server-cleanup-test.js +35 -35
- package/_site/_test/test-server-module-isolation-test.js +37 -37
- package/_site/_test/tracking-start-test.js +144 -144
- package/_site/_test/vector-storage-root-test.js +75 -75
- package/_site/_test/version-visibility-browser-probe.js +249 -0
- package/_site/_test/version-visibility-test.js +92 -0
- package/_site/_test/workbench-context-receipts-test.js +22 -22
- package/_site/_test/workbench-permission-test.js +117 -117
- package/_site/_test/zcode-capture-integration-test.js +112 -112
- package/_site/lib/ai-profile-resolver.js +78 -78
- package/_site/lib/ai-vendor-presets.js +83 -83
- package/_site/lib/ai-workspace.js +104 -104
- package/_site/lib/atomic-file.js +204 -204
- package/_site/lib/automation-config.js +140 -140
- package/_site/lib/bridge-adapter.js +260 -260
- package/_site/lib/bridge-consumer-service.js +402 -402
- package/_site/lib/capture-hook-config.js +108 -108
- package/_site/lib/claude-cli-runner.js +1599 -1599
- package/_site/lib/cli-instances.js +43 -43
- package/_site/lib/codex-context-status.js +17 -17
- package/_site/lib/codex-notify-config.js +193 -193
- package/_site/lib/commit-conversation-binder.js +247 -247
- package/_site/lib/commit-processing-ledger.js +27 -27
- package/_site/lib/commit-prompt.js +202 -202
- package/_site/lib/commit-reconciler.js +444 -444
- package/_site/lib/context-pack-builder.js +299 -299
- package/_site/lib/contracts.js +291 -291
- package/_site/lib/conversation-exclusions.js +204 -204
- package/_site/lib/conversation-query-service.js +161 -161
- package/_site/lib/conversation-store.js +387 -387
- package/_site/lib/data-dir.js +13 -13
- package/_site/lib/embedding-service.js +85 -85
- package/_site/lib/engine-connection.js +93 -93
- package/_site/lib/engine-knowledge-client.js +76 -72
- package/_site/lib/evidence-bundle.js +228 -228
- package/_site/lib/folder-picker-output.js +24 -24
- package/_site/lib/git-path.js +14 -14
- package/_site/lib/git-runner.js +62 -62
- package/_site/lib/github-team-store.js +1175 -1175
- package/_site/lib/hook-manager.js +203 -203
- package/_site/lib/index-builder.js +157 -157
- package/_site/lib/index-service.js +173 -173
- package/_site/lib/integration-auto-repair.js +77 -77
- package/_site/lib/integration-installer.js +1240 -1240
- package/_site/lib/integration-intent.js +88 -88
- package/_site/lib/integration-status-scan.js +99 -99
- package/_site/lib/integration-status-worker.js +14 -14
- package/_site/lib/kb-framework.js +191 -191
- package/_site/lib/kb-validator.js +124 -124
- package/_site/lib/knowledge-db.js +363 -363
- package/_site/lib/knowledge-promotion.js +435 -435
- package/_site/lib/knowledge-retrieval-service.js +401 -401
- package/_site/lib/knowledge-schema.js +121 -121
- package/_site/lib/knowledge-tool-runtime.js +366 -366
- package/_site/lib/live-gateway.js +78 -78
- package/_site/lib/llm-client.js +173 -173
- package/_site/lib/markdown-knowledge-indexer.js +230 -230
- package/_site/lib/model-context-windows.js +82 -82
- package/_site/lib/module-bridge.js +874 -874
- package/_site/lib/module-port-resolver.js +57 -57
- package/_site/lib/post-commit-automation.js +141 -141
- package/_site/lib/product-data.js +35 -35
- package/_site/lib/project-lifecycle-service.js +554 -554
- package/_site/lib/project-registry-store.js +129 -129
- package/_site/lib/project-store.js +268 -268
- package/_site/lib/prompt-registry.js +148 -148
- package/_site/lib/public-version-info.js +37 -0
- package/_site/lib/registry-events.js +16 -16
- package/_site/lib/requirement-adapters.js +10 -10
- package/_site/lib/requirement-binder.js +112 -112
- package/_site/lib/requirement-recorder.js +278 -278
- package/_site/lib/runtime-control.js +89 -89
- package/_site/lib/runtime-endpoint.js +160 -160
- package/_site/lib/scanner.js +298 -298
- package/_site/lib/server-app.js +1672 -1664
- package/_site/lib/settings-store.js +153 -153
- package/_site/lib/storage-layout.js +166 -166
- package/_site/lib/structured-logger.js +574 -574
- package/_site/lib/zcode-context-integration.js +60 -60
- package/_site/scripts/codex-notify-fanout.cjs +95 -95
- package/_site/scripts/folder-picker.ps1 +156 -156
- package/_site/scripts/hook-trigger.js +158 -158
- package/_site/scripts/install-module-candidates.js +69 -69
- package/_site/scripts/pack-module-candidates.js +89 -89
- package/_site/scripts/sync-product-content.js +28 -28
- package/_site/scripts/sync-release-version.js +164 -164
- package/_site/scripts/vendor-modules.js +49 -49
- package/_site/scripts/verify-module-runtime.js +50 -50
- package/_site/scripts/verify-product-runtime.js +36 -36
- package/_site/scripts/verify-retrieval-gold.js +83 -83
- package/_site/server.js +15 -15
- package/_site/start.bat +26 -26
- package/_site/stop.bat +11 -11
- package/bin/project-knowledge-integrations.js +254 -254
- package/bin/project-knowledge-kb.js +67 -67
- package/bin/project-knowledge-mcp.js +201 -201
- package/bin/project-knowledge.js +269 -170
- package/claude-prompts.json +21 -21
- package/docs/README.zh-CN.md +207 -205
- package/docs/assets/architecture.svg +181 -181
- package/docs/assets/logo.svg +49 -49
- package/docs/fresh-v5-start.md +44 -44
- package/docs/leader-glm-workbench-playbook.md +122 -122
- package/docs/project-registry-schema.md +33 -33
- package/docs/release-5.0.16-acceptance.md +176 -176
- package/docs/release-5.0.17-acceptance.md +117 -117
- package/docs/release-5.0.19-acceptance.md +39 -39
- package/docs/team-knowledge-mode-a-plan.md +73 -73
- package/docs/testing-strategy.md +33 -33
- package/module-runtime-manifest.json +68 -65
- package/package.json +113 -110
- package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.mcp.json +1 -1
- package/templates/change.md +30 -30
- package/templates/project-readme.md +52 -52
- package/ui/index.html +3 -2
- package/ui/version-info.css +44 -0
- package/ui/version-info.js +24 -0
- package/vendor-manifest.json +55 -55
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Lane-owned helpers for the Workbench provenance batch (WB001).
|
|
3
|
+
//
|
|
4
|
+
// They answer one question with bytes, never with version strings:
|
|
5
|
+
// module-runtime-manifest.json <-> the currently installed package
|
|
6
|
+
// module-runtime-manifest.json <-> the Workbench source checkout
|
|
7
|
+
//
|
|
8
|
+
// A file is compared three ways, in this order:
|
|
9
|
+
// identical the raw sha256 equals the manifest hash
|
|
10
|
+
// eolOnly the sha256 differs but the CRLF-normalised content matches
|
|
11
|
+
// (npm tarballs ship LF, a Windows checkout carries CRLF)
|
|
12
|
+
// different the content really differs
|
|
13
|
+
//
|
|
14
|
+
// Every path is resolved from the repository location or from an explicit
|
|
15
|
+
// environment override, so nothing here depends on a developer directory.
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const crypto = require('node:crypto');
|
|
19
|
+
const { execFileSync } = require('node:child_process');
|
|
20
|
+
|
|
21
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
22
|
+
const MANIFEST_FILE = path.join(REPO_ROOT, 'module-runtime-manifest.json');
|
|
23
|
+
|
|
24
|
+
function sha256Buffer(value) {
|
|
25
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The same content with the line endings normalised to LF.
|
|
29
|
+
function normalizedSha256(value) {
|
|
30
|
+
return sha256Buffer(Buffer.from(value.toString('utf8').replace(/\r\n/g, '\n'), 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sha256File(filePath) {
|
|
34
|
+
return fs.existsSync(filePath) ? sha256Buffer(fs.readFileSync(filePath)) : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadManifest() {
|
|
38
|
+
if (!fs.existsSync(MANIFEST_FILE)) throw new Error(`module manifest is missing: ${MANIFEST_FILE}`);
|
|
39
|
+
const manifest = JSON.parse(fs.readFileSync(MANIFEST_FILE, 'utf8'));
|
|
40
|
+
const terminal = manifest.modules && manifest.modules.terminal;
|
|
41
|
+
if (!terminal || !terminal.files || typeof terminal.files !== 'object') {
|
|
42
|
+
throw new Error('module manifest has no modules.terminal.files');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
file: MANIFEST_FILE,
|
|
46
|
+
schema: manifest.schema,
|
|
47
|
+
terminal,
|
|
48
|
+
files: terminal.files,
|
|
49
|
+
count: Object.keys(terminal.files).length,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function packageIdentity(root) {
|
|
54
|
+
const packageFile = path.join(root, 'package.json');
|
|
55
|
+
if (!fs.existsSync(packageFile)) return null;
|
|
56
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
57
|
+
return { root, packageFile, name: pkg.name, version: pkg.version };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Classify every manifest file against a candidate tree.
|
|
61
|
+
function compareTree(manifestFiles, root) {
|
|
62
|
+
const identical = [];
|
|
63
|
+
const eolOnly = [];
|
|
64
|
+
const different = [];
|
|
65
|
+
const missing = [];
|
|
66
|
+
for (const [relative, expected] of Object.entries(manifestFiles)) {
|
|
67
|
+
const target = path.join(root, relative);
|
|
68
|
+
if (!fs.existsSync(target)) { missing.push(relative); continue; }
|
|
69
|
+
const content = fs.readFileSync(target);
|
|
70
|
+
if (sha256Buffer(content) === expected) { identical.push(relative); continue; }
|
|
71
|
+
if (normalizedSha256(content) === expected) { eolOnly.push(relative); continue; }
|
|
72
|
+
different.push(relative);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
root,
|
|
76
|
+
total: Object.keys(manifestFiles).length,
|
|
77
|
+
identical,
|
|
78
|
+
eolOnly,
|
|
79
|
+
different,
|
|
80
|
+
missing,
|
|
81
|
+
identicalCount: identical.length,
|
|
82
|
+
eolOnlyCount: eolOnly.length,
|
|
83
|
+
differentCount: different.length,
|
|
84
|
+
missingCount: missing.length,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The package the repository pins: node_modules/claude-ai-workbench.
|
|
89
|
+
function resolveRepoTargetPackage() {
|
|
90
|
+
return path.join(REPO_ROOT, 'node_modules', 'claude-ai-workbench');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// The package the installed product (5.0.24) actually ships.
|
|
94
|
+
function resolveInstalledProductPackage() {
|
|
95
|
+
const override = String(process.env.PK_WB_INSTALLED_PRODUCT_MODULE || '').trim();
|
|
96
|
+
if (override) return fs.existsSync(override) ? override : null;
|
|
97
|
+
const appData = String(process.env.APPDATA || '').trim();
|
|
98
|
+
if (!appData) return null;
|
|
99
|
+
const candidate = path.join(appData, 'npm', 'node_modules', '@sanqianx', 'project-knowledge', 'node_modules', 'claude-ai-workbench');
|
|
100
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function resolveInstalledProductRoot() {
|
|
104
|
+
const override = String(process.env.PK_WB_INSTALLED_PRODUCT_ROOT || '').trim();
|
|
105
|
+
if (override) return fs.existsSync(override) ? override : null;
|
|
106
|
+
const appData = String(process.env.APPDATA || '').trim();
|
|
107
|
+
if (!appData) return null;
|
|
108
|
+
const candidate = path.join(appData, 'npm', 'node_modules', '@sanqianx', 'project-knowledge');
|
|
109
|
+
return fs.existsSync(path.join(candidate, 'package.json')) ? candidate : null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The Workbench source checkout: an explicit override, or a sibling of this
|
|
113
|
+
// repository.
|
|
114
|
+
function resolveSourceCheckout() {
|
|
115
|
+
const override = String(process.env.PK_WB_SOURCE_DIR || '').trim();
|
|
116
|
+
const candidates = override ? [override] : [path.join(path.dirname(REPO_ROOT), 'claude-ai-workbench')];
|
|
117
|
+
for (const candidate of candidates) {
|
|
118
|
+
if (fs.existsSync(path.join(candidate, 'packages', 'server', 'package.json'))) return path.resolve(candidate);
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The commit the checkout currently stands on. This reads the working state
|
|
124
|
+
// of the checkout, never a historical object.
|
|
125
|
+
function sourceCommitId(root) {
|
|
126
|
+
if (!root) return null;
|
|
127
|
+
try {
|
|
128
|
+
const out = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, windowsHide: true, encoding: 'utf8' });
|
|
129
|
+
return out.trim();
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function sourceWorkingTreeClean(root) {
|
|
136
|
+
if (!root) return null;
|
|
137
|
+
try {
|
|
138
|
+
const out = execFileSync('git', ['status', '--porcelain'], { cwd: root, windowsHide: true, encoding: 'utf8' });
|
|
139
|
+
return out.trim().length === 0;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Provenance trace against a historical commit of the source checkout.
|
|
146
|
+
// This reads Git objects and is therefore NOT part of the canonical test:
|
|
147
|
+
// it is evidence for the report, and it fails loudly when the commit or the
|
|
148
|
+
// Git history is unavailable.
|
|
149
|
+
function traceAgainstCommit(sourceRoot, commit, manifestFiles, packageRoot) {
|
|
150
|
+
if (!sourceRoot) throw new Error('source checkout is required to trace a commit');
|
|
151
|
+
const identical = [];
|
|
152
|
+
const different = [];
|
|
153
|
+
const absent = [];
|
|
154
|
+
for (const relative of Object.keys(manifestFiles)) {
|
|
155
|
+
let committed;
|
|
156
|
+
try {
|
|
157
|
+
committed = execFileSync('git', ['show', `${commit}:${relative}`], { cwd: sourceRoot, windowsHide: true, maxBuffer: 1 << 26 });
|
|
158
|
+
} catch {
|
|
159
|
+
absent.push(relative);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const shipped = fs.readFileSync(path.join(packageRoot, relative));
|
|
163
|
+
if (normalizedSha256(committed) === normalizedSha256(shipped)) identical.push(relative);
|
|
164
|
+
else different.push(relative);
|
|
165
|
+
}
|
|
166
|
+
return { commit, identical, different, absent, identicalCount: identical.length, differentCount: different.length, absentCount: absent.length };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Which commits of the source checkout touch a given file, newest first.
|
|
170
|
+
function commitsTouchingFile(sourceRoot, relative, limit = 8) {
|
|
171
|
+
try {
|
|
172
|
+
const out = execFileSync('git', ['log', `--format=%h %s`, `-${limit}`, '--', relative], { cwd: sourceRoot, windowsHide: true, encoding: 'utf8' });
|
|
173
|
+
return out.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
174
|
+
} catch {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
REPO_ROOT,
|
|
181
|
+
MANIFEST_FILE,
|
|
182
|
+
sha256Buffer,
|
|
183
|
+
normalizedSha256,
|
|
184
|
+
sha256File,
|
|
185
|
+
loadManifest,
|
|
186
|
+
packageIdentity,
|
|
187
|
+
compareTree,
|
|
188
|
+
resolveRepoTargetPackage,
|
|
189
|
+
resolveInstalledProductPackage,
|
|
190
|
+
resolveInstalledProductRoot,
|
|
191
|
+
resolveSourceCheckout,
|
|
192
|
+
sourceCommitId,
|
|
193
|
+
sourceWorkingTreeClean,
|
|
194
|
+
traceAgainstCommit,
|
|
195
|
+
commitsTouchingFile,
|
|
196
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// _site/_test/helpers/spawn-server.js
|
|
2
|
+
//
|
|
3
|
+
// Test helper: spawn _site/server.js with an isolated KB_DATA_DIR so each
|
|
4
|
+
// regression test runs against a fresh, empty user-data location. Without
|
|
5
|
+
// this, server.js would default to ~/.project-knowledge/ and tests would
|
|
6
|
+
// pollute (or read from) the real user data.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// const { server, dataDir, cleanup } = require('./helpers/spawn-server');
|
|
10
|
+
// const { server, dataDir, cleanup } = spawnServer({
|
|
11
|
+
// root: ROOT,
|
|
12
|
+
// port: 7891,
|
|
13
|
+
// extraEnv: { KB_AUTOMATION_FAKE_CLAUDE: '1' },
|
|
14
|
+
// tag: 'hook-trigger',
|
|
15
|
+
// });
|
|
16
|
+
// ...
|
|
17
|
+
// cleanup(); // kills server + removes temp dataDir
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const { spawn } = require('child_process');
|
|
23
|
+
const runtimeControl = require('../../lib/runtime-control');
|
|
24
|
+
const runtimeEndpoint = require('../../lib/runtime-endpoint');
|
|
25
|
+
|
|
26
|
+
function defaultDataDir(tag) {
|
|
27
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), `kb-data-${tag || 'test'}-${process.pid}-`));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function spawnServer({ root, port, host = '127.0.0.1', dataDir, extraEnv = {}, tag, analysisOwner = 'legacy', stdio = ['ignore', 'pipe', 'pipe'], cwd }) {
|
|
31
|
+
const dir = dataDir || defaultDataDir(tag);
|
|
32
|
+
// Isolate the Bridge journal home too: the runtime registers a
|
|
33
|
+
// 'project-knowledge' consumer at startup, and without this the test
|
|
34
|
+
// server would write into the developer's real ~/.ai-coding-event-bridge.
|
|
35
|
+
const bridgeHome = fs.mkdtempSync(path.join(os.tmpdir(), `kb-bridge-${tag || 'test'}-${process.pid}-`));
|
|
36
|
+
// The knowledge root is a second product-owned location. A test must never
|
|
37
|
+
// inherit the developer's real knowledge directory through the environment,
|
|
38
|
+
// so give every isolated instance its own one.
|
|
39
|
+
const knowledgeDir = fs.mkdtempSync(path.join(os.tmpdir(), `kb-knowledge-${tag || 'test'}-${process.pid}-`));
|
|
40
|
+
// Legacy behavior tests characterize the retired writer in isolation. The
|
|
41
|
+
// production entry point still defaults to engine ownership; product tests
|
|
42
|
+
// explicitly request it and run the real module services.
|
|
43
|
+
const entry = path.join(root, '_site', 'lib', 'server-app.js');
|
|
44
|
+
// CI 40075242420 (normalhook, linux24): a startup rejection used to vanish —
|
|
45
|
+
// exit 1 with empty stdout/stderr — because this catch swallowed the reason.
|
|
46
|
+
// The failure path below prints the child's stderr tail, so emit one bounded
|
|
47
|
+
// JSON line of own metadata (code / message head / first stack frame's file
|
|
48
|
+
// only; never logs, keys or transcripts) and keep exit 1.
|
|
49
|
+
const bootstrap = `const {startServer,installProcessHandlers}=require(${JSON.stringify(entry)});const instance=startServer({analysisOwner:${JSON.stringify(analysisOwner)}});installProcessHandlers(instance);instance.then(s=>process.stdout.write('Project Knowledge listening on http://'+s.host+':'+s.port+'\\n')).catch(e=>{const frame=String(e&&e.stack||'').split('\\n')[1]||'';const fm=/([^\\\\/]+\\.[a-z]+):/i.exec(frame);process.stderr.write(JSON.stringify({event:'server.startup-fatal',code:e&&e.code||null,message:String(e&&e.message||e).slice(0,300),stackFile:fm?fm[1]:null})+'\\n');process.exitCode=1});`;
|
|
50
|
+
const child = spawn(process.execPath, ['-e', bootstrap], {
|
|
51
|
+
cwd: cwd || root,
|
|
52
|
+
env: {
|
|
53
|
+
...process.env,
|
|
54
|
+
KB_SITE_PORT: String(port),
|
|
55
|
+
KB_SITE_HOST: host,
|
|
56
|
+
KB_DATA_DIR: dir,
|
|
57
|
+
KB_KNOWLEDGE_DATA_DIR: knowledgeDir,
|
|
58
|
+
KB_CLAUDE_RULES_DIR: dir,
|
|
59
|
+
KB_SKIP_MIGRATION: '1',
|
|
60
|
+
// 测试隔离:禁止测试服务器向真实模块(terminal/vector-hub)登记项目
|
|
61
|
+
KB_MODULES_REGISTER: '0',
|
|
62
|
+
KB_MODULES_AUTOSTART: '0',
|
|
63
|
+
// Disabling registration does not disable status/profile reads. Never
|
|
64
|
+
// let an isolated legacy test consult the developer's live modules.
|
|
65
|
+
// A test needing real modules supplies its own URLs in extraEnv below.
|
|
66
|
+
KB_TERMINAL_URL: 'http://127.0.0.1:0',
|
|
67
|
+
KB_VECTORHUB_URL: 'http://127.0.0.1:0',
|
|
68
|
+
KB_EVENTBRIDGE_URL: 'http://127.0.0.1:0',
|
|
69
|
+
KB_KNOWLEDGE_ENGINE_URL: 'http://127.0.0.1:0',
|
|
70
|
+
AI_CODING_EVENT_BRIDGE_HOME: bridgeHome,
|
|
71
|
+
...extraEnv,
|
|
72
|
+
},
|
|
73
|
+
stdio,
|
|
74
|
+
windowsHide: true,
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
child,
|
|
78
|
+
dataDir: dir,
|
|
79
|
+
bridgeHome,
|
|
80
|
+
knowledgeDir,
|
|
81
|
+
cleanup: async () => {
|
|
82
|
+
// On Windows SIGTERM is a force kill. Drain owned background work via
|
|
83
|
+
// the verified control channel first, so Git/Agent children release cwd.
|
|
84
|
+
const live = runtimeEndpoint.readEndpoint(dir);
|
|
85
|
+
if (live?.pid === child.pid && child.exitCode === null) {
|
|
86
|
+
try { await runtimeControl.request(dir, { action: 'stop', pid: live.pid, port: live.port, startedAt: live.startedAt }, 10000); } catch {}
|
|
87
|
+
await new Promise(resolve => {
|
|
88
|
+
const timer = setTimeout(resolve, 1500);
|
|
89
|
+
child.once('close', () => { clearTimeout(timer); resolve(); });
|
|
90
|
+
if (child.exitCode !== null) { clearTimeout(timer); resolve(); }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (child.exitCode === null) try { child.kill(); } catch {}
|
|
94
|
+
await new Promise(resolve => {
|
|
95
|
+
const timer = setTimeout(resolve, 5000);
|
|
96
|
+
child.once('close', () => { clearTimeout(timer); resolve(); });
|
|
97
|
+
if (child.exitCode !== null) { clearTimeout(timer); resolve(); }
|
|
98
|
+
});
|
|
99
|
+
await fs.promises.rm(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
100
|
+
await fs.promises.rm(bridgeHome, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
101
|
+
await fs.promises.rm(knowledgeDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { spawnServer, defaultDataDir };
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// FEATURE-VERSIONS-01-R2 fixture — the version-visibility lane's own isolation
|
|
3
|
+
// contract, borrowed from the accepted drafts-r5 fixture (read-only reuse of
|
|
4
|
+
// its patterns; that file stays untouched):
|
|
5
|
+
// * every directory lives under a repo-external temp root (PK_TEST_TEMP_ROOT
|
|
6
|
+
// → repo-parent shared root when it exists → TEMP/TMP/TMPDIR → os.tmpdir),
|
|
7
|
+
// never inside the repository, never on a hardcoded drive/user dir;
|
|
8
|
+
// * the full env whitelist (HOME/USERPROFILE/HOMEDRIVE/HOMEPATH/CODEX_HOME/
|
|
9
|
+
// CLAUDE_CONFIG_DIR/APPDATA/LOCALAPPDATA/TEMP/TMP/TMPDIR/双 KB_DATA_DIR/
|
|
10
|
+
// Bridge home/Workbench data) is absolute-pathed into one per-run layout
|
|
11
|
+
// root; ambient module addresses and Anthropic credentials are stripped;
|
|
12
|
+
// * the live product ports 5757/5760/5789/5790/8787/8790 are refused;
|
|
13
|
+
// * Chrome is resolved from the REAL machine before any isolation is applied
|
|
14
|
+
// (find-chrome reads the real environment), then run with an isolated
|
|
15
|
+
// --user-data-dir plus the Chrome>=136 empty-default-dir scaffold under the
|
|
16
|
+
// isolated home so remote debugging is accepted.
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const os = require('node:os');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const net = require('node:net');
|
|
21
|
+
const { findChromeDetailed } = require('./find-chrome');
|
|
22
|
+
|
|
23
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
24
|
+
const FORBIDDEN_PORTS = new Set([5757, 5760, 5789, 5790, 8787, 8790]);
|
|
25
|
+
|
|
26
|
+
const AMBIENT_MODULE_URLS = [
|
|
27
|
+
'KB_KNOWLEDGE_ENGINE_URL', 'KB_VECTORHUB_URL', 'KB_TERMINAL_URL', 'KB_EVENTBRIDGE_URL', 'KB_EVENT_BRIDGE_URL',
|
|
28
|
+
'KB_WORKBENCH_RUNTIME_TOKEN', 'KB_MODULES_AUTOSTART',
|
|
29
|
+
];
|
|
30
|
+
const AMBIENT_MODEL_ENV = [
|
|
31
|
+
'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_MODEL',
|
|
32
|
+
'ANTHROPIC_DEFAULT_THINKING_MODEL', 'ANTHROPIC_DEFAULT_HAIKU_MODEL', 'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
33
|
+
'ANTHROPIC_DEFAULT_OP_MODEL', 'API_TIMEOUT_MS', 'CLAUDE_CODE_OAUTH_TOKEN',
|
|
34
|
+
'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const ISOLATION_PATH_KEYS = [
|
|
38
|
+
'HOME', 'USERPROFILE', 'CODEX_HOME', 'CLAUDE_CONFIG_DIR', 'APPDATA', 'LOCALAPPDATA',
|
|
39
|
+
'TEMP', 'TMP', 'TMPDIR', 'KB_DATA_DIR', 'KB_KNOWLEDGE_DATA_DIR',
|
|
40
|
+
'AI_CODING_EVENT_BRIDGE_HOME', 'AGENT_TERMINAL_DATA_DIR', 'CLAUDE_AI_WORKBENCH_DATA_DIR',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Captured at module load, BEFORE any isolation could hide the real machine:
|
|
44
|
+
// the browser binary this lane's UI acceptance will drive.
|
|
45
|
+
const CHROME_PATH = findChromeDetailed().path;
|
|
46
|
+
|
|
47
|
+
function isInside(candidate, parent) {
|
|
48
|
+
const relative = path.relative(parent, candidate);
|
|
49
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveTempRoot() {
|
|
53
|
+
const sharedRoot = path.join(path.dirname(REPO_ROOT), '.project-knowledge-test-temp');
|
|
54
|
+
const candidates = [process.env.PK_TEST_TEMP_ROOT, fs.existsSync(sharedRoot) ? sharedRoot : null, process.env.TEMP, process.env.TMP, process.env.TMPDIR];
|
|
55
|
+
const chosen = candidates.find(value => value && String(value).trim());
|
|
56
|
+
const root = path.resolve(chosen ? String(chosen).trim() : os.tmpdir());
|
|
57
|
+
if (isInside(root, REPO_ROOT)) throw new Error(`refusing to use a temp root inside the repository: ${root}`);
|
|
58
|
+
return root;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function makeTempDir(prefix = 'version-visibility') {
|
|
62
|
+
const root = resolveTempRoot();
|
|
63
|
+
fs.mkdirSync(root, { recursive: true });
|
|
64
|
+
const dir = fs.mkdtempSync(path.join(root, `${prefix}-`));
|
|
65
|
+
if (isInside(dir, REPO_ROOT)) throw new Error(`refusing to create a fixture inside the repository: ${dir}`);
|
|
66
|
+
return dir;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function removeTree(dir) {
|
|
70
|
+
if (!dir) return;
|
|
71
|
+
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function freePort() {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const server = net.createServer();
|
|
77
|
+
server.unref();
|
|
78
|
+
server.on('error', reject);
|
|
79
|
+
server.listen(0, '127.0.0.1', () => {
|
|
80
|
+
const { port } = server.address();
|
|
81
|
+
server.close(() => {
|
|
82
|
+
if (FORBIDDEN_PORTS.has(Number(port))) { reject(new Error(`refusing to use the live port ${port}`)); return; }
|
|
83
|
+
resolve(Number(port));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function assertAllowedEndpoint(url, what = 'endpoint') {
|
|
90
|
+
const parsed = new URL(String(url));
|
|
91
|
+
const port = parsed.port ? Number(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80);
|
|
92
|
+
if (FORBIDDEN_PORTS.has(port)) throw new Error(`refusing to contact the live ${what} ${url}`);
|
|
93
|
+
return url;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function waitForHttp(url, timeoutMs = 45000) {
|
|
97
|
+
assertAllowedEndpoint(url, 'service');
|
|
98
|
+
const until = Date.now() + timeoutMs;
|
|
99
|
+
let lastError = null;
|
|
100
|
+
while (Date.now() < until) {
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
|
|
103
|
+
if (response.ok) return true;
|
|
104
|
+
lastError = new Error(`HTTP ${response.status}`);
|
|
105
|
+
} catch (error) { lastError = error; }
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, 120));
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`Timed out waiting for ${url}: ${lastError && lastError.message}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function sanitizedEnv(extra = {}, source = process.env) {
|
|
112
|
+
const env = {};
|
|
113
|
+
const systemKey = /^(?:PATH|PATHEXT|SYSTEMROOT|WINDIR|SYSTEMDRIVE|COMSPEC|OS|PROCESSOR_ARCHITECTURE|PROCESSOR_IDENTIFIER|PROCESSOR_LEVEL|PROCESSOR_REVISION|NUMBER_OF_PROCESSORS|PROGRAMFILES(?:\(X86\))?|PROGRAMW6432|PROGRAMDATA|LANG|LC_ALL|LC_CTYPE|TERM)$/i;
|
|
114
|
+
for (const [key, value] of Object.entries(source)) {
|
|
115
|
+
if (systemKey.test(key)) env[key] = value;
|
|
116
|
+
}
|
|
117
|
+
// Only the inherited environment is filtered. Explicit lane URLs and the
|
|
118
|
+
// disabled autostart flag must survive, or the server uses live defaults.
|
|
119
|
+
return { ...env, ...extra };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function spawnNode(args, env = {}) {
|
|
123
|
+
const { spawn } = require('node:child_process');
|
|
124
|
+
const child = spawn(process.execPath, args, { env: sanitizedEnv(env), windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
125
|
+
const output = { stdout: '', stderr: '' };
|
|
126
|
+
child.stdout.on('data', chunk => { output.stdout += chunk; });
|
|
127
|
+
child.stderr.on('data', chunk => { output.stderr += chunk; });
|
|
128
|
+
const exited = new Promise(resolve => child.once('close', code => resolve({ code, ...output })));
|
|
129
|
+
return { child, exited, output };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function createIsolationLayout(prefix = 'version-visibility') {
|
|
133
|
+
const root = makeTempDir(prefix);
|
|
134
|
+
const layout = {
|
|
135
|
+
root,
|
|
136
|
+
dataDir: path.join(root, 'kb-data'),
|
|
137
|
+
knowledgeDataDir: path.join(root, 'knowledge-data'),
|
|
138
|
+
bridgeHome: path.join(root, 'bridge-home'),
|
|
139
|
+
workbenchDataDir: path.join(root, 'workbench-data'),
|
|
140
|
+
home: path.join(root, 'home'),
|
|
141
|
+
claudeConfigDir: path.join(root, 'claude-config'),
|
|
142
|
+
codexHome: path.join(root, 'codex-home'),
|
|
143
|
+
appData: path.join(root, 'appdata'),
|
|
144
|
+
localAppData: path.join(root, 'localappdata'),
|
|
145
|
+
temp: path.join(root, 'temp'),
|
|
146
|
+
};
|
|
147
|
+
for (const dir of Object.values(layout)) fs.mkdirSync(dir, { recursive: true });
|
|
148
|
+
// Chrome >= 136 gate: an empty default-shaped "User Data" scaffold under the
|
|
149
|
+
// ISOLATED home lets the default-dir resolution succeed, so the separate
|
|
150
|
+
// --user-data-dir browser profile below this layout is accepted. Nothing of
|
|
151
|
+
// the real machine is touched.
|
|
152
|
+
fs.mkdirSync(path.join(layout.home, 'AppData', 'Local', 'Google', 'Chrome', 'User Data'), { recursive: true });
|
|
153
|
+
fs.mkdirSync(path.join(layout.home, 'AppData', 'Roaming'), { recursive: true });
|
|
154
|
+
return layout;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isolationEnvMap(layout, extraPaths = {}) {
|
|
158
|
+
const parsed = path.parse(layout.home);
|
|
159
|
+
return {
|
|
160
|
+
HOME: layout.home,
|
|
161
|
+
USERPROFILE: layout.home,
|
|
162
|
+
HOMEDRIVE: parsed.root,
|
|
163
|
+
HOMEPATH: parsed.root === path.sep ? layout.home.slice(1) : layout.home.slice(parsed.root.length),
|
|
164
|
+
CODEX_HOME: layout.codexHome,
|
|
165
|
+
CLAUDE_CONFIG_DIR: layout.claudeConfigDir,
|
|
166
|
+
APPDATA: layout.appData,
|
|
167
|
+
LOCALAPPDATA: layout.localAppData,
|
|
168
|
+
TEMP: layout.temp,
|
|
169
|
+
TMP: layout.temp,
|
|
170
|
+
TMPDIR: layout.temp,
|
|
171
|
+
PSModuleAnalysisCachePath: path.join(layout.temp, 'powershell-module-cache'),
|
|
172
|
+
KB_DATA_DIR: layout.dataDir,
|
|
173
|
+
KB_KNOWLEDGE_DATA_DIR: layout.knowledgeDataDir,
|
|
174
|
+
AI_CODING_EVENT_BRIDGE_HOME: layout.bridgeHome,
|
|
175
|
+
AGENT_TERMINAL_DATA_DIR: layout.workbenchDataDir,
|
|
176
|
+
CLAUDE_AI_WORKBENCH_DATA_DIR: layout.workbenchDataDir,
|
|
177
|
+
KB_MODULES_AUTOSTART: '0',
|
|
178
|
+
...extraPaths,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function hostEnv(layout, extra = {}) {
|
|
183
|
+
return sanitizedEnv(isolationEnvMap(layout, extra));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function applyIsolationEnv(layout) {
|
|
187
|
+
const env = hostEnv(layout);
|
|
188
|
+
for (const key of Object.keys(process.env)) delete process.env[key];
|
|
189
|
+
Object.assign(process.env, env);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// The product server under test: real _site/server.js, fully isolated, with
|
|
193
|
+
// every module address explicitly held on this run's own free ports (so the
|
|
194
|
+
// process can never reach for the live defaults 5760/8787/8790/5790).
|
|
195
|
+
async function startRootServer(layout, options = {}) {
|
|
196
|
+
const port = options.port || await freePort();
|
|
197
|
+
const [terminalPort, vectorHubPort, eventBridgePort, enginePort] = await Promise.all([freePort(), freePort(), freePort(), freePort()]);
|
|
198
|
+
const env = hostEnv(layout, {
|
|
199
|
+
KB_SITE_PORT: String(port),
|
|
200
|
+
KB_SITE_HOST: '127.0.0.1',
|
|
201
|
+
KB_TERMINAL_URL: `http://127.0.0.1:${terminalPort}`,
|
|
202
|
+
KB_VECTORHUB_URL: `http://127.0.0.1:${vectorHubPort}`,
|
|
203
|
+
KB_EVENTBRIDGE_URL: `http://127.0.0.1:${eventBridgePort}`,
|
|
204
|
+
KB_KNOWLEDGE_ENGINE_URL: `http://127.0.0.1:${enginePort}`,
|
|
205
|
+
});
|
|
206
|
+
const runner = spawnNode([path.join(REPO_ROOT, '_site', 'server.js')], env);
|
|
207
|
+
const base = `http://127.0.0.1:${port}`;
|
|
208
|
+
await waitForHttp(`${base}/api/system/version`);
|
|
209
|
+
return { layout, port, base, runner, modulePorts: { terminalPort, vectorHubPort, eventBridgePort, enginePort } };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function stopRootServer(server) {
|
|
213
|
+
if (!server || !server.runner) return;
|
|
214
|
+
server.runner.child.kill();
|
|
215
|
+
await server.runner.exited;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// The honest-unknown branch: serve the REAL ui/ assets from disk, but fail the
|
|
219
|
+
// version GET — the tooltip must then say 版本未知 instead of inventing one.
|
|
220
|
+
async function startVersionBrokenUiServer(layout, options = {}) {
|
|
221
|
+
const port = options.port || await freePort();
|
|
222
|
+
const uiRoot = path.join(REPO_ROOT, 'ui');
|
|
223
|
+
const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.svg': 'image/svg+xml' };
|
|
224
|
+
const http = require('node:http');
|
|
225
|
+
const server = http.createServer((req, res) => {
|
|
226
|
+
const pathname = new URL(req.url, 'http://127.0.0.1').pathname;
|
|
227
|
+
if (pathname === '/api/system/version') {
|
|
228
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
229
|
+
res.end(JSON.stringify({ ok: false }));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const rel = pathname === '/' ? 'index.html' : pathname.replace(/^\//, '');
|
|
233
|
+
const file = path.join(uiRoot, rel);
|
|
234
|
+
if (!isInside(path.resolve(file), uiRoot) || !fs.existsSync(file) || !fs.statSync(file).isFile()) {
|
|
235
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
236
|
+
res.end('Not found');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
res.writeHead(200, { 'Content-Type': types[path.extname(file)] || 'application/octet-stream' });
|
|
240
|
+
res.end(fs.readFileSync(file));
|
|
241
|
+
});
|
|
242
|
+
await new Promise(resolve => server.listen(port, '127.0.0.1', resolve));
|
|
243
|
+
return { layout, port, base: `http://127.0.0.1:${port}`, server };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
module.exports = {
|
|
247
|
+
REPO_ROOT, FORBIDDEN_PORTS, AMBIENT_MODULE_URLS, AMBIENT_MODEL_ENV, ISOLATION_PATH_KEYS,
|
|
248
|
+
CHROME_PATH, resolveTempRoot, makeTempDir, removeTree, freePort, assertAllowedEndpoint,
|
|
249
|
+
waitForHttp, sanitizedEnv, spawnNode, createIsolationLayout, isolationEnvMap, hostEnv, applyIsolationEnv,
|
|
250
|
+
startRootServer, stopRootServer, startVersionBrokenUiServer,
|
|
251
|
+
};
|