@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.
- 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 +66 -63
- 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,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// P-ENG-ADVANCED lane-exclusive harness (parallel-20261023 batch).
|
|
4
|
+
//
|
|
5
|
+
// Drives the VENDORED knowledge-engine (_modules/knowledge-engine, byte-identical
|
|
6
|
+
// to source e5573bb at batch time) inside real child processes with full
|
|
7
|
+
// isolation: ephemeral loopback ports, lane-owned data dirs under
|
|
8
|
+
// PK_TEST_TEMP_ROOT (or the OS temp dir), isolated HOME/TEMP, and a
|
|
9
|
+
// scrubbed child environment (no KB_*/KNOWLEDGE_*/ANTHROPIC_*/CLAUDE* leakage,
|
|
10
|
+
// no env -u dependency). The analyzer is a DECLARATIVE STAND-IN: it writes real
|
|
11
|
+
// staged Markdown + manifest bytes through the engine's real promotion path but
|
|
12
|
+
// does NOT cover real model output. The vector endpoint is a loopback HTTP
|
|
13
|
+
// double: engine-side index behavior is real, vector-hub itself is not covered.
|
|
14
|
+
|
|
15
|
+
const { spawn, execFileSync } = require('node:child_process');
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const http = require('node:http');
|
|
18
|
+
const os = require('node:os');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
|
|
21
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
22
|
+
const VENDORED_ENGINE_SERVER = path.join(REPO_ROOT, '_modules', 'knowledge-engine', 'src', 'server.js');
|
|
23
|
+
const ENGINE_CHILD = path.join(__dirname, 'parallel-engine-advanced-engine-child.js');
|
|
24
|
+
|
|
25
|
+
// Temp root: PK_TEST_TEMP_ROOT or the OS temp dir; never inside the repository.
|
|
26
|
+
const TEMP_ROOT = (() => {
|
|
27
|
+
const base = path.resolve(process.env.PK_TEST_TEMP_ROOT || path.join(os.tmpdir(), 'project-knowledge-engine-test', 'parallel-engine-advanced'));
|
|
28
|
+
if (base === REPO_ROOT || base.startsWith(REPO_ROOT + path.sep)) {
|
|
29
|
+
throw new Error(`temp root must live outside the repository (got ${base})`);
|
|
30
|
+
}
|
|
31
|
+
return base;
|
|
32
|
+
})();
|
|
33
|
+
|
|
34
|
+
function ensureTempRoot() {
|
|
35
|
+
fs.mkdirSync(TEMP_ROOT, { recursive: true });
|
|
36
|
+
return TEMP_ROOT;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function makeLaneDir(label) {
|
|
40
|
+
ensureTempRoot();
|
|
41
|
+
const dir = fs.mkdtempSync(path.join(TEMP_ROOT, `${label}-`));
|
|
42
|
+
return dir;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isolatedEnv(extra = {}) {
|
|
46
|
+
const home = extra.ISOLATED_HOME || path.join(ensureTempRoot(), 'isolated-home');
|
|
47
|
+
fs.mkdirSync(home, { recursive: true });
|
|
48
|
+
const env = { ...process.env };
|
|
49
|
+
for (const name of Object.keys(env)) {
|
|
50
|
+
if (/^(KB_|KNOWLEDGE_|ANTHROPIC_|CLAUDE|OPENAI_|CODEX|MINIMAX|GLM|PK_TEST_)/i.test(name)) delete env[name];
|
|
51
|
+
}
|
|
52
|
+
const tmp = path.join(ensureTempRoot(), 'tmp');
|
|
53
|
+
fs.mkdirSync(tmp, { recursive: true });
|
|
54
|
+
env.HOME = home;
|
|
55
|
+
env.USERPROFILE = home;
|
|
56
|
+
env.APPDATA = path.join(home, 'AppData', 'Roaming');
|
|
57
|
+
env.LOCALAPPDATA = path.join(home, 'AppData', 'Local');
|
|
58
|
+
env.TEMP = tmp;
|
|
59
|
+
env.TMP = tmp;
|
|
60
|
+
env.TMPDIR = tmp;
|
|
61
|
+
env.GIT_CONFIG_NOSYSTEM = '1';
|
|
62
|
+
env.GIT_CONFIG_GLOBAL = path.join(home, 'does-not-exist.gitconfig');
|
|
63
|
+
return { ...env, ...Object.fromEntries(Object.entries(extra).filter(([k]) => k !== 'ISOLATED_HOME')) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function git(repoPath, args, env) {
|
|
67
|
+
return execFileSync('git', args, { cwd: repoPath, windowsHide: true, encoding: 'utf8', env }).trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Real fixture git repository with a baseline plus one labeled commit per entry.
|
|
71
|
+
// Mirrors the canonical fixture shape from the engine suite (test/engine.test.js
|
|
72
|
+
// project()) so scanner.collectEvidence sees the same repo shape it supports.
|
|
73
|
+
function buildFixtureRepo(root, name, commits) {
|
|
74
|
+
const env = isolatedEnv();
|
|
75
|
+
const repoPath = path.join(root, `repo-${name}`);
|
|
76
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
77
|
+
git(repoPath, ['init', '-q'], env);
|
|
78
|
+
git(repoPath, ['config', 'user.email', 'eng-advanced@test.invalid'], env);
|
|
79
|
+
git(repoPath, ['config', 'user.name', 'P-ENG-ADVANCED'], env);
|
|
80
|
+
fs.writeFileSync(path.join(repoPath, 'README.md'), `# ${name}\n\nInitial fixture repository.\n`);
|
|
81
|
+
git(repoPath, ['add', '.'], env);
|
|
82
|
+
git(repoPath, ['commit', '-qm', 'baseline'], env);
|
|
83
|
+
const baseline = git(repoPath, ['rev-parse', 'HEAD'], env);
|
|
84
|
+
const shas = {};
|
|
85
|
+
for (const label of commits) {
|
|
86
|
+
fs.writeFileSync(path.join(repoPath, `${label}.txt`), `change ${name} ${label}\n` + 'x'.repeat(64) + '\n');
|
|
87
|
+
git(repoPath, ['add', '.'], env);
|
|
88
|
+
git(repoPath, ['commit', '-qm', `feature ${label}`], env);
|
|
89
|
+
shas[label] = git(repoPath, ['rev-parse', 'HEAD'], env);
|
|
90
|
+
}
|
|
91
|
+
return { repoPath, baseline, shas };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function waitUntil(fn, timeoutMs, describe) {
|
|
95
|
+
const started = Date.now();
|
|
96
|
+
for (;;) {
|
|
97
|
+
const value = await fn();
|
|
98
|
+
if (value) return value;
|
|
99
|
+
if (Date.now() - started > timeoutMs) throw new Error(`waitUntil timed out after ${timeoutMs}ms: ${describe}`);
|
|
100
|
+
await new Promise(resolve => setTimeout(resolve, 25));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function callJson(base, route, body, init = {}) {
|
|
105
|
+
const response = await fetch(`${base}${route}`, {
|
|
106
|
+
...init,
|
|
107
|
+
method: body === undefined && !init.method ? 'GET' : init.method || 'POST',
|
|
108
|
+
headers: { 'Content-Type': 'application/json', ...(init.headers || {}) },
|
|
109
|
+
body: body === undefined && (!init.method || init.method === 'GET') ? undefined : JSON.stringify(body),
|
|
110
|
+
});
|
|
111
|
+
let payload = null;
|
|
112
|
+
try { payload = await response.json(); } catch { payload = null; }
|
|
113
|
+
return { status: response.status, body: payload };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Loopback vector-service double. Engine-side requestIndex behavior is fully
|
|
117
|
+
// real; this double only stands in for vector-hub's HTTP contract.
|
|
118
|
+
async function startVectorDouble() {
|
|
119
|
+
const requests = [];
|
|
120
|
+
let mode = { kind: 'ok' };
|
|
121
|
+
const server = http.createServer((req, res) => {
|
|
122
|
+
let raw = '';
|
|
123
|
+
req.on('data', chunk => { raw += chunk; });
|
|
124
|
+
req.on('end', () => {
|
|
125
|
+
let body = null;
|
|
126
|
+
try { body = raw ? JSON.parse(raw) : null; } catch { body = null; }
|
|
127
|
+
requests.push({ at: new Date().toISOString(), url: req.url, body });
|
|
128
|
+
if (mode.kind === 'ok') {
|
|
129
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
130
|
+
res.end(JSON.stringify({ ok: true }));
|
|
131
|
+
} else {
|
|
132
|
+
res.writeHead(mode.httpStatus || 503, { 'Content-Type': 'application/json' });
|
|
133
|
+
res.end(JSON.stringify({ ok: false, error: { code: mode.code || 'EMBEDDING_UNAVAILABLE', message: mode.message || 'vector double scripted failure' } }));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
138
|
+
return {
|
|
139
|
+
url: `http://127.0.0.1:${server.address().port}`,
|
|
140
|
+
requests,
|
|
141
|
+
setMode(next) { mode = next; },
|
|
142
|
+
async close() { server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); },
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Real engine child process running the vendored server module with the
|
|
147
|
+
// declarative stand-in analyzer. Returns { base, proc, dataDir, killHard, stop }.
|
|
148
|
+
async function startEngineChild({ label, dataDir, vectorUrl, planFile, eventsFile, concurrency }) {
|
|
149
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
150
|
+
const proc = spawn(process.execPath, [ENGINE_CHILD], {
|
|
151
|
+
windowsHide: true,
|
|
152
|
+
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
153
|
+
env: isolatedEnv({
|
|
154
|
+
ENGADV_DATA_DIR: dataDir,
|
|
155
|
+
ENGADV_VECTOR_URL: vectorUrl,
|
|
156
|
+
ENGADV_PLAN_FILE: planFile,
|
|
157
|
+
ENGADV_EVENTS_FILE: eventsFile,
|
|
158
|
+
...(concurrency !== undefined ? { ENGADV_CONCURRENCY: String(concurrency) } : {}),
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
let stderr = '';
|
|
162
|
+
proc.stderr.on('data', chunk => { stderr += chunk; });
|
|
163
|
+
const base = await new Promise((resolve, reject) => {
|
|
164
|
+
let out = '';
|
|
165
|
+
const timer = setTimeout(() => reject(new Error(`engine child not ready; stderr: ${stderr}`)), 30000);
|
|
166
|
+
proc.stdout.on('data', chunk => {
|
|
167
|
+
out += chunk;
|
|
168
|
+
const match = out.match(/^ENGADV-READY (\{.*\})\n/m);
|
|
169
|
+
if (match) {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
resolve(JSON.parse(match[1]).base);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
proc.on('exit', code => { clearTimeout(timer); reject(new Error(`engine child exited early (${code}); stderr: ${stderr}`)); });
|
|
175
|
+
});
|
|
176
|
+
async function killHard() {
|
|
177
|
+
if (proc.exitCode !== null || proc.signalCode !== null) return proc.exitCode;
|
|
178
|
+
// A HARD crash must not run the engine's graceful shutdown. On POSIX the
|
|
179
|
+
// default signal of kill() is SIGTERM, and the engine child traps SIGTERM
|
|
180
|
+
// to close the server gracefully - which releases the single-writer lock
|
|
181
|
+
// file, so the residual-lock state under test would never exist (root CI
|
|
182
|
+
// 39927630516 failed on Linux with ENOENT on engine-runtime.lock). SIGKILL
|
|
183
|
+
// cannot be trapped, so the process dies with the lock still on disk; on
|
|
184
|
+
// Windows Node maps SIGKILL to TerminateProcess, which is what kill()
|
|
185
|
+
// already did there.
|
|
186
|
+
proc.kill('SIGKILL');
|
|
187
|
+
return await new Promise(resolve => {
|
|
188
|
+
if (proc.exitCode !== null || proc.signalCode !== null) return resolve(proc.exitCode);
|
|
189
|
+
proc.on('exit', code => resolve(code));
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async function stop() {
|
|
193
|
+
if (proc.exitCode !== null) return;
|
|
194
|
+
proc.send({ type: 'kb:shutdown' });
|
|
195
|
+
await new Promise(resolve => {
|
|
196
|
+
const timer = setTimeout(() => { try { proc.kill(); } catch {} resolve(); }, 15000);
|
|
197
|
+
proc.on('exit', () => { clearTimeout(timer); resolve(); });
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return { base, proc, dataDir, killHard, stop, stderr: () => stderr };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readEventsFile(eventsFile) {
|
|
204
|
+
if (!fs.existsSync(eventsFile)) return [];
|
|
205
|
+
return fs.readFileSync(eventsFile, 'utf8').split('\n').filter(Boolean).map(line => JSON.parse(line));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function writePlan(planFile, plan) {
|
|
209
|
+
fs.mkdirSync(path.dirname(planFile), { recursive: true });
|
|
210
|
+
fs.writeFileSync(planFile, JSON.stringify(plan, null, 1));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function listFilesRecursive(root) {
|
|
214
|
+
const out = [];
|
|
215
|
+
if (!fs.existsSync(root)) return out;
|
|
216
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
217
|
+
const absolute = path.join(root, entry.name);
|
|
218
|
+
if (entry.isDirectory()) out.push(...listFilesRecursive(absolute).map(sub => `${entry.name}/${sub}`));
|
|
219
|
+
else out.push(entry.name);
|
|
220
|
+
}
|
|
221
|
+
return out.sort();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function saveEvidence(laneDir, name, value) {
|
|
225
|
+
fs.mkdirSync(laneDir, { recursive: true });
|
|
226
|
+
fs.writeFileSync(path.join(laneDir, name), JSON.stringify(value, null, 2));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = {
|
|
230
|
+
REPO_ROOT,
|
|
231
|
+
VENDORED_ENGINE_SERVER,
|
|
232
|
+
TEMP_ROOT,
|
|
233
|
+
makeLaneDir,
|
|
234
|
+
isolatedEnv,
|
|
235
|
+
git,
|
|
236
|
+
buildFixtureRepo,
|
|
237
|
+
waitUntil,
|
|
238
|
+
callJson,
|
|
239
|
+
startVectorDouble,
|
|
240
|
+
startEngineChild,
|
|
241
|
+
readEventsFile,
|
|
242
|
+
writePlan,
|
|
243
|
+
listFilesRecursive,
|
|
244
|
+
saveEvidence,
|
|
245
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// P-ENG-ADVANCED declarative stand-in analyzer.
|
|
4
|
+
//
|
|
5
|
+
// HONEST SCOPE LABEL: this analyzer produces real staged Markdown bytes and a
|
|
6
|
+
// valid knowledge-staging-manifest through the engine's REAL promotion path,
|
|
7
|
+
// but it does NOT cover real model output, real prompt adherence, or the
|
|
8
|
+
// headless Claude Agent SDK worker. Every case that relies on it is evidence
|
|
9
|
+
// about engine scheduling/promotion/indexing machinery, never about model
|
|
10
|
+
// quality. The debug simulateFailure flag is handled by the engine itself
|
|
11
|
+
// BEFORE this analyzer runs (DEBUG_SIMULATED_FAILURE).
|
|
12
|
+
//
|
|
13
|
+
// Plan file shape (JSON, read per claim):
|
|
14
|
+
// {
|
|
15
|
+
// "default": { "delayMs": 300, "markdownHeader": "..." },
|
|
16
|
+
// "rules": [{ "projectId": "...", "commitSha": "...", "delayMs": 5000 }]
|
|
17
|
+
// }
|
|
18
|
+
// Behavior: sleep delayMs (timing control), then write
|
|
19
|
+
// changes/<sha12>.md plus the manifest with the frozen claim evidence refs.
|
|
20
|
+
// Every start/finish appends a JSONL record to the events file for
|
|
21
|
+
// cross-process invocation-count and overlap evidence.
|
|
22
|
+
|
|
23
|
+
const fs = require('node:fs');
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
const crypto = require('node:crypto');
|
|
26
|
+
const AtomicFile = require('../../../_modules/knowledge-engine/src/runtime/atomic-file.js');
|
|
27
|
+
|
|
28
|
+
function hashBuffer(value) {
|
|
29
|
+
return `sha256:${crypto.createHash('sha256').update(value).digest('hex')}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function appendEvent(eventsFile, record) {
|
|
33
|
+
if (!eventsFile) return;
|
|
34
|
+
fs.mkdirSync(path.dirname(eventsFile), { recursive: true });
|
|
35
|
+
fs.appendFileSync(eventsFile, `${JSON.stringify({ ...record, at: new Date().toISOString(), pid: process.pid })}\n`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readPlan(planFile) {
|
|
39
|
+
if (!planFile || !fs.existsSync(planFile)) return { default: {} };
|
|
40
|
+
try { return JSON.parse(fs.readFileSync(planFile, 'utf8')); } catch { return { default: {} }; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createStandinAnalyzer({ planFile, eventsFile } = {}) {
|
|
44
|
+
return {
|
|
45
|
+
sourceReadMode: 'isolated-frozen-evidence',
|
|
46
|
+
async runClaim(input) {
|
|
47
|
+
const plan = readPlan(planFile);
|
|
48
|
+
const rule = (plan.rules || []).find(r => (!r.projectId || r.projectId === input.projectId) && (!r.commitSha || r.commitSha === input.claim.commitSha)) || {};
|
|
49
|
+
const effective = { ...(plan.default || {}), ...rule };
|
|
50
|
+
appendEvent(eventsFile, { kind: 'start', projectId: input.projectId, commitSha: input.claim.commitSha, runId: input.claim.runId, delayMs: effective.delayMs || 0 });
|
|
51
|
+
if (effective.delayMs) await new Promise(resolve => setTimeout(resolve, effective.delayMs));
|
|
52
|
+
const relative = `changes/${input.claim.commitSha.slice(0, 12)}.md`;
|
|
53
|
+
const content = `${effective.markdownHeader || '# Verified maintenance'} ${input.projectId}\n\nCommit ${input.claim.commitSha} implements a fixture change.\n\n- Project: ${input.projectId}\n- Staged by: declarative stand-in analyzer (no real model output)\n`;
|
|
54
|
+
AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', relative), content);
|
|
55
|
+
const refs = [
|
|
56
|
+
`commit:${input.claim.commitSha}`,
|
|
57
|
+
input.claim.patchHash && `patch:${input.claim.patchHash}`,
|
|
58
|
+
input.claim.conversationSnapshotHash && `conversation:${input.claim.conversationSnapshotHash}`,
|
|
59
|
+
input.claim.retrievalManifestHash && `retrieval:${input.claim.retrievalManifestHash}`,
|
|
60
|
+
].filter(Boolean);
|
|
61
|
+
AtomicFile.writeJsonAtomic(input.manifestPath, {
|
|
62
|
+
schema: 'knowledge-staging-manifest/v1',
|
|
63
|
+
projectId: input.projectId,
|
|
64
|
+
runId: input.claim.runId,
|
|
65
|
+
commitSha: input.claim.commitSha,
|
|
66
|
+
operations: [{
|
|
67
|
+
path: relative,
|
|
68
|
+
operation: 'create',
|
|
69
|
+
sha256: hashBuffer(content),
|
|
70
|
+
reason: 'Frozen Git evidence from fixture commit',
|
|
71
|
+
evidenceReferences: refs,
|
|
72
|
+
}],
|
|
73
|
+
});
|
|
74
|
+
appendEvent(eventsFile, { kind: 'finish', projectId: input.projectId, commitSha: input.claim.commitSha, runId: input.claim.runId });
|
|
75
|
+
return { ok: true };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { createStandinAnalyzer };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// P-ENGINE-REVIEW-R2/R3 lane-exclusive candidate host for the knowledge-engine UI.
|
|
4
|
+
//
|
|
5
|
+
// Purpose: let a REAL browser load the REAL product UI bytes from a directory
|
|
6
|
+
// the caller chooses (the vendored copy = as shipped, or the module source
|
|
7
|
+
// tree = candidate fix), while /api/* is reverse-proxied to the REAL vendored
|
|
8
|
+
// engine - mirroring the product shell wiring where the UI is embedded
|
|
9
|
+
// same-origin behind the shell proxy. The engine refuses cross-origin
|
|
10
|
+
// requests (src/server.js FORBIDDEN_ORIGIN), so the browser origin and the
|
|
11
|
+
// API origin must be this one host.
|
|
12
|
+
//
|
|
13
|
+
// Harness-only fault injection (never touches the engine or the UI bytes):
|
|
14
|
+
// * retryDelayMs - hold retry POST responses so a second native click can
|
|
15
|
+
// land while the first intent is still in flight.
|
|
16
|
+
// * dropRetry - forward the retry POST upstream, let the engine really
|
|
17
|
+
// execute it, then destroy the browser socket without
|
|
18
|
+
// any response bytes (lost response -> the product
|
|
19
|
+
// client faces an UNKNOWN result).
|
|
20
|
+
// * rejectRetryStatus - answer the retry POST with an explicit error status
|
|
21
|
+
// (e.g. 409) WITHOUT forwarding it (the API clearly
|
|
22
|
+
// refused the intent).
|
|
23
|
+
// * holdRetryForward - accept the retry POST but do NOT forward it yet, so
|
|
24
|
+
// the engine still reports the run as failed and every
|
|
25
|
+
// detail re-render rebuilds a retry button for the SAME
|
|
26
|
+
// in-flight intent (the re-render counter-proof).
|
|
27
|
+
// releaseHeldRetries() forwards the parked requests.
|
|
28
|
+
|
|
29
|
+
const crypto = require('node:crypto');
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const http = require('node:http');
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
|
|
34
|
+
const STATIC_FILES = new Map([
|
|
35
|
+
['/', ['index.html', 'text/html; charset=utf-8']],
|
|
36
|
+
['/index.html', ['index.html', 'text/html; charset=utf-8']],
|
|
37
|
+
['/app.js', ['app.js', 'text/javascript; charset=utf-8']],
|
|
38
|
+
['/app.css', ['app.css', 'text/css; charset=utf-8']],
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
function fileSha(file) {
|
|
42
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function startUiHost({ uiDir, engineBase, retryDelayMs = 0, dropRetry = false, rejectRetryStatus = 0, holdRetryForward = false }) {
|
|
46
|
+
const settings = { retryDelayMs, dropRetry, rejectRetryStatus, holdRetryForward };
|
|
47
|
+
const state = {
|
|
48
|
+
proxiedApiRequests: 0,
|
|
49
|
+
receivedRetryPosts: 0,
|
|
50
|
+
forwardedRetryPosts: 0,
|
|
51
|
+
rejectedRetryResponses: 0,
|
|
52
|
+
heldRetryPosts: 0,
|
|
53
|
+
droppedRetryResponses: 0,
|
|
54
|
+
};
|
|
55
|
+
const held = [];
|
|
56
|
+
|
|
57
|
+
const forward = ({ url, method, headers, body }, res, isRetryPost) => {
|
|
58
|
+
const target = new URL(url.pathname + url.search, engineBase);
|
|
59
|
+
const outHeaders = { ...headers, host: target.host };
|
|
60
|
+
delete outHeaders.origin;
|
|
61
|
+
delete outHeaders.connection;
|
|
62
|
+
delete outHeaders['proxy-connection'];
|
|
63
|
+
delete outHeaders['content-length'];
|
|
64
|
+
if (body.length) outHeaders['content-length'] = String(body.length);
|
|
65
|
+
if (isRetryPost) state.forwardedRetryPosts += 1;
|
|
66
|
+
const upstream = http.request(target, { method, headers: outHeaders }, up => {
|
|
67
|
+
const responseChunks = [];
|
|
68
|
+
up.on('data', chunk => responseChunks.push(chunk));
|
|
69
|
+
up.on('end', () => {
|
|
70
|
+
const payload = Buffer.concat(responseChunks);
|
|
71
|
+
if (isRetryPost && settings.dropRetry) {
|
|
72
|
+
// The engine has fully executed the retry. Start the response (so
|
|
73
|
+
// the browser cannot transparently retry the request on a fresh
|
|
74
|
+
// keep-alive connection) and then destroy the socket mid-flight -
|
|
75
|
+
// the product client faces a truncated answer, i.e. an UNKNOWN
|
|
76
|
+
// result it must reconcile by querying, never by re-dispatching.
|
|
77
|
+
state.droppedRetryResponses += 1;
|
|
78
|
+
const out = { ...up.headers };
|
|
79
|
+
delete out['content-length'];
|
|
80
|
+
delete out['transfer-encoding'];
|
|
81
|
+
delete out.connection;
|
|
82
|
+
res.writeHead(up.statusCode, out);
|
|
83
|
+
res.flushHeaders();
|
|
84
|
+
setTimeout(() => res.destroy(), 30);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const reply = () => {
|
|
88
|
+
const out = { ...up.headers };
|
|
89
|
+
delete out['content-length'];
|
|
90
|
+
delete out['transfer-encoding'];
|
|
91
|
+
delete out.connection;
|
|
92
|
+
if (payload.length) out['content-length'] = String(payload.length);
|
|
93
|
+
res.writeHead(up.statusCode, out);
|
|
94
|
+
res.end(payload);
|
|
95
|
+
};
|
|
96
|
+
if (isRetryPost && settings.retryDelayMs > 0) setTimeout(reply, settings.retryDelayMs);
|
|
97
|
+
else reply();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
upstream.on('error', () => res.destroy());
|
|
101
|
+
upstream.end(body);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const server = http.createServer((req, res) => {
|
|
105
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
106
|
+
const staticEntry = req.method === 'GET' ? STATIC_FILES.get(url.pathname) : null;
|
|
107
|
+
if (staticEntry) {
|
|
108
|
+
res.writeHead(200, { 'Content-Type': staticEntry[1], 'Cache-Control': 'no-store' });
|
|
109
|
+
res.end(fs.readFileSync(path.join(uiDir, staticEntry[0])));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!url.pathname.startsWith('/api/')) {
|
|
113
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
114
|
+
res.end('not found');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
state.proxiedApiRequests += 1;
|
|
118
|
+
const chunks = [];
|
|
119
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
120
|
+
req.on('end', () => {
|
|
121
|
+
const body = Buffer.concat(chunks);
|
|
122
|
+
const headers = { ...req.headers };
|
|
123
|
+
// The shell proxy terminates the browser origin; the engine would refuse
|
|
124
|
+
// any mismatched Origin header (FORBIDDEN_ORIGIN).
|
|
125
|
+
delete headers.origin;
|
|
126
|
+
delete headers.connection;
|
|
127
|
+
delete headers['proxy-connection'];
|
|
128
|
+
delete headers['content-length'];
|
|
129
|
+
const isRetryPost = req.method === 'POST' && /\/api\/runs\/[^/]+\/retry$/.test(url.pathname);
|
|
130
|
+
if (isRetryPost) state.receivedRetryPosts += 1;
|
|
131
|
+
|
|
132
|
+
if (isRetryPost && settings.rejectRetryStatus) {
|
|
133
|
+
state.rejectedRetryResponses += 1;
|
|
134
|
+
res.writeHead(settings.rejectRetryStatus, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
135
|
+
res.end(JSON.stringify({ ok: false, error: 'RUN_NOT_RETRYABLE', message: `ui-host injected explicit refusal (HTTP ${settings.rejectRetryStatus})` }));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (isRetryPost && settings.holdRetryForward) {
|
|
139
|
+
state.heldRetryPosts += 1;
|
|
140
|
+
held.push({ url, method: req.method, headers, body, res });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
forward({ url, method: req.method, headers, body }, res, isRetryPost);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
state,
|
|
149
|
+
uiDir,
|
|
150
|
+
uiHashes: {
|
|
151
|
+
'index.html': fileSha(path.join(uiDir, 'index.html')),
|
|
152
|
+
'app.js': fileSha(path.join(uiDir, 'app.js')),
|
|
153
|
+
'app.css': fileSha(path.join(uiDir, 'app.css')),
|
|
154
|
+
},
|
|
155
|
+
configure(next) { Object.assign(settings, next); },
|
|
156
|
+
// Forward every parked retry POST and answer it (the held intent finally
|
|
157
|
+
// reaches the engine).
|
|
158
|
+
releaseHeldRetries() {
|
|
159
|
+
const parked = held.splice(0, held.length);
|
|
160
|
+
for (const request of parked) {
|
|
161
|
+
const isRetryPost = request.method === 'POST' && /\/api\/runs\/[^/]+\/retry$/.test(request.url.pathname);
|
|
162
|
+
forward(request, request.res, isRetryPost);
|
|
163
|
+
}
|
|
164
|
+
return parked.length;
|
|
165
|
+
},
|
|
166
|
+
async listen() {
|
|
167
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
168
|
+
return `http://127.0.0.1:${server.address().port}`;
|
|
169
|
+
},
|
|
170
|
+
async close() {
|
|
171
|
+
server.closeAllConnections();
|
|
172
|
+
await new Promise(resolve => server.close(resolve));
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = { startUiHost };
|