@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,148 @@
|
|
|
1
|
+
// _site/_test/helpers/parallel-nav-proxy.js
|
|
2
|
+
//
|
|
3
|
+
// A pass-through proxy in front of the product server that injects transport
|
|
4
|
+
// faults (a held response = a late read, a failed response) and carries the
|
|
5
|
+
// product's live WebSocket channel.
|
|
6
|
+
//
|
|
7
|
+
// The shared product proxy cannot be used for these cases: the shell's live
|
|
8
|
+
// channel is a WebSocket upgrade (/api/live), and a plain HTTP proxy answers
|
|
9
|
+
// an upgrade request with 404. Without the upgrade relay the shell loses the
|
|
10
|
+
// live registry/progress streams, and a product that only fills its pages
|
|
11
|
+
// through those streams cannot be observed faithfully. This file is the lane's
|
|
12
|
+
// own fixture for that reason; the shared helper is left untouched.
|
|
13
|
+
//
|
|
14
|
+
// The relay forwards the upgrade request verbatim, so the product sees the
|
|
15
|
+
// browser's own Host and Origin headers and applies its own upgrade checks.
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const http = require('node:http');
|
|
20
|
+
|
|
21
|
+
function readBody(req) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const chunks = [];
|
|
24
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
25
|
+
req.on('end', () => resolve(Buffer.concat(chunks)));
|
|
26
|
+
req.on('error', reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sendJson(res, status, payload) {
|
|
31
|
+
const body = JSON.stringify(payload);
|
|
32
|
+
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(body) });
|
|
33
|
+
res.end(body);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function createNavProductProxy({ target, tag = 'product' }) {
|
|
37
|
+
const state = { requests: [], held: [], faults: [], upgrades: 0 };
|
|
38
|
+
const upstream = new URL(target);
|
|
39
|
+
|
|
40
|
+
const server = http.createServer(async (req, res) => {
|
|
41
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
42
|
+
const raw = await readBody(req);
|
|
43
|
+
let body = null;
|
|
44
|
+
if (raw.length) { try { body = JSON.parse(raw.toString('utf8')); } catch { body = null; } }
|
|
45
|
+
const entry = { method: req.method, path: url.pathname + url.search, body, at: Date.now(), status: null };
|
|
46
|
+
state.requests.push(entry);
|
|
47
|
+
|
|
48
|
+
const fault = state.faults.find(item => !item.used && item.match(entry));
|
|
49
|
+
if (fault && fault.once) fault.used = true;
|
|
50
|
+
if (fault && fault.kind === 'fail') {
|
|
51
|
+
entry.status = fault.status;
|
|
52
|
+
sendJson(res, fault.status, { ok: false, error: { code: 'TRANSPORT_FAULT', message: fault.message || '注入的传输故障' } });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const headers = { 'Content-Type': req.headers['content-type'] || 'application/json', 'Content-Length': String(raw.length) };
|
|
57
|
+
if (req.headers.authorization) headers.Authorization = req.headers.authorization;
|
|
58
|
+
const upstreamRequest = http.request({
|
|
59
|
+
hostname: upstream.hostname,
|
|
60
|
+
port: upstream.port,
|
|
61
|
+
path: url.pathname + url.search,
|
|
62
|
+
method: req.method,
|
|
63
|
+
headers,
|
|
64
|
+
}, upstreamResponse => {
|
|
65
|
+
entry.status = upstreamResponse.statusCode;
|
|
66
|
+
entry.respondedAt = Date.now();
|
|
67
|
+
if (fault && fault.kind === 'hold') {
|
|
68
|
+
const chunks = [];
|
|
69
|
+
upstreamResponse.on('data', chunk => chunks.push(chunk));
|
|
70
|
+
upstreamResponse.on('end', () => {
|
|
71
|
+
state.held.push({ response: res, entry, status: upstreamResponse.statusCode, headers: upstreamResponse.headers, payload: Buffer.concat(chunks) });
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
res.writeHead(upstreamResponse.statusCode, upstreamResponse.headers);
|
|
76
|
+
upstreamResponse.pipe(res);
|
|
77
|
+
});
|
|
78
|
+
upstreamRequest.on('error', error => {
|
|
79
|
+
entry.status = 502;
|
|
80
|
+
if (!res.headersSent) sendJson(res, 502, { ok: false, error: { code: 'UPSTREAM_UNREACHABLE', message: String(error.message || error) } });
|
|
81
|
+
else res.destroy();
|
|
82
|
+
});
|
|
83
|
+
if (raw.length) upstreamRequest.write(raw);
|
|
84
|
+
upstreamRequest.end();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// The live channel: relay the upgrade to the product unchanged.
|
|
88
|
+
server.on('upgrade', (req, socket, head) => {
|
|
89
|
+
state.upgrades += 1;
|
|
90
|
+
const upstreamRequest = http.request({
|
|
91
|
+
hostname: upstream.hostname,
|
|
92
|
+
port: upstream.port,
|
|
93
|
+
path: req.url,
|
|
94
|
+
method: req.method,
|
|
95
|
+
headers: req.headers,
|
|
96
|
+
});
|
|
97
|
+
upstreamRequest.on('upgrade', (upstreamResponse, upstreamSocket, upstreamHead) => {
|
|
98
|
+
const lines = Object.entries(upstreamResponse.headers)
|
|
99
|
+
.map(([name, value]) => `${name}: ${Array.isArray(value) ? value.join(', ') : value}`);
|
|
100
|
+
socket.write(`HTTP/1.1 101 Switching Protocols\r\n${lines.join('\r\n')}\r\n\r\n`);
|
|
101
|
+
if (upstreamHead && upstreamHead.length) socket.write(upstreamHead);
|
|
102
|
+
upstreamSocket.on('error', () => socket.destroy());
|
|
103
|
+
socket.on('error', () => upstreamSocket.destroy());
|
|
104
|
+
upstreamSocket.pipe(socket);
|
|
105
|
+
socket.pipe(upstreamSocket);
|
|
106
|
+
});
|
|
107
|
+
upstreamRequest.on('error', () => socket.destroy());
|
|
108
|
+
upstreamRequest.end(head);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
tag,
|
|
113
|
+
state,
|
|
114
|
+
requests: state.requests,
|
|
115
|
+
url: null,
|
|
116
|
+
/** Hold the response of every request that matches the predicate. */
|
|
117
|
+
hold(match, { once = false } = {}) { state.faults.push({ kind: 'hold', match, once }); },
|
|
118
|
+
/** Fail every matching request with an HTTP status. */
|
|
119
|
+
fail(match, status = 503, message = '注入的传输故障', { once = false } = {}) { state.faults.push({ kind: 'fail', match, status, message, once }); },
|
|
120
|
+
clearFaults() { state.faults = []; },
|
|
121
|
+
heldCount() { return state.held.length; },
|
|
122
|
+
/** How many live-channel upgrades the proxy really relayed. */
|
|
123
|
+
upgradeCount() { return state.upgrades; },
|
|
124
|
+
/** Deliver every held response to the browser that is still waiting. */
|
|
125
|
+
releaseHeld() {
|
|
126
|
+
const held = state.held.splice(0);
|
|
127
|
+
for (const item of held) {
|
|
128
|
+
try {
|
|
129
|
+
item.response.writeHead(item.status, item.headers);
|
|
130
|
+
item.response.end(item.payload);
|
|
131
|
+
} catch {}
|
|
132
|
+
}
|
|
133
|
+
return held.length;
|
|
134
|
+
},
|
|
135
|
+
async start() {
|
|
136
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
137
|
+
this.url = `http://127.0.0.1:${server.address().port}`;
|
|
138
|
+
return this.url;
|
|
139
|
+
},
|
|
140
|
+
async close() {
|
|
141
|
+
state.held.length = 0;
|
|
142
|
+
server.closeAllConnections();
|
|
143
|
+
await new Promise(resolve => server.close(resolve));
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = { createNavProductProxy };
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// _site/_test/helpers/parallel-nav-shell.js
|
|
2
|
+
//
|
|
3
|
+
// Shared harness for the P-TEST-NAV regressions (NAV-002/004/005/006/007/008).
|
|
4
|
+
//
|
|
5
|
+
// Every case runs against a real, isolated product instance:
|
|
6
|
+
// * its own port, data dir, knowledge root and Bridge journal home
|
|
7
|
+
// (the shared spawn-server helper already isolates those);
|
|
8
|
+
// * the REAL vendored knowledge engine on its own port, reached through a
|
|
9
|
+
// pass-through proxy, so snapshots and runs are real engine state;
|
|
10
|
+
// * a test double for the embedded module UI, so the shell really talks
|
|
11
|
+
// the kb:* protocol to a real frame;
|
|
12
|
+
// * a real Chrome (CDP) in its own profile, reaching the product through a
|
|
13
|
+
// pass-through proxy that can hold or fail a single answer.
|
|
14
|
+
//
|
|
15
|
+
// The harness owns the native-input helpers: pointer clicks and key presses
|
|
16
|
+
// go through CDP Input events, never through element.click(), and every
|
|
17
|
+
// pointer click proves the hit test first (document.elementFromPoint must
|
|
18
|
+
// return the target, so a transparent overlay cannot fake a click).
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const assert = require('node:assert/strict');
|
|
23
|
+
const crypto = require('node:crypto');
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const { spawnSync } = require('node:child_process');
|
|
27
|
+
|
|
28
|
+
const { spawnServer } = require('../helpers/spawn-server');
|
|
29
|
+
const { findChrome } = require('../helpers/find-chrome');
|
|
30
|
+
const { launchCdpBrowser, waitFor } = require('../helpers/cdp-browser');
|
|
31
|
+
const { attachNetworkCapture } = require('../helpers/cdp-network-capture');
|
|
32
|
+
const { createUpstreamProxy } = require('../helpers/module-fixture');
|
|
33
|
+
const { createNavProductProxy } = require('./parallel-nav-proxy');
|
|
34
|
+
const { createModuleUiFixture } = require('./parallel-nav-module-ui');
|
|
35
|
+
const { enterNavIsolation } = require('./parallel-nav-isolation-env');
|
|
36
|
+
const freePort = require('../helpers/free-port');
|
|
37
|
+
|
|
38
|
+
const ROOT = path.resolve(__dirname, '../../..');
|
|
39
|
+
const ENGINE_ENTRY = path.join(ROOT, '_modules', 'knowledge-engine', 'src', 'server.js');
|
|
40
|
+
|
|
41
|
+
const THEMES = ['light', 'dark'];
|
|
42
|
+
|
|
43
|
+
function git(cwd, args) {
|
|
44
|
+
const result = spawnSync('git', ['-C', cwd, ...args], { encoding: 'utf8', windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
45
|
+
assert.equal(result.status, 0, `git ${args.join(' ')} failed: ${result.stderr}`);
|
|
46
|
+
return String(result.stdout || '').trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A real Git repository with one real commit: the product imports it for real. */
|
|
50
|
+
function makeRepo(root, name) {
|
|
51
|
+
const repo = path.join(root, name);
|
|
52
|
+
fs.mkdirSync(repo, { recursive: true });
|
|
53
|
+
git(repo, ['init', '-q', '-b', 'main']);
|
|
54
|
+
git(repo, ['config', 'user.email', 'parallel-nav@local.invalid']);
|
|
55
|
+
git(repo, ['config', 'user.name', 'Parallel NAV Regression']);
|
|
56
|
+
fs.writeFileSync(path.join(repo, 'README.md'), `# ${name}\n`);
|
|
57
|
+
git(repo, ['add', '.']);
|
|
58
|
+
git(repo, ['commit', '-qm', 'baseline']);
|
|
59
|
+
return { path: repo, headCommit: git(repo, ['rev-parse', 'HEAD']) };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Boot the isolated product instance, the real engine, the module-UI double
|
|
64
|
+
* and a real browser. The caller owns the cleanup through `harness.cleanup()`.
|
|
65
|
+
*/
|
|
66
|
+
async function startHarness({ tag, viewport = { width: 1920, height: 1080 }, evidenceDir, withEngine = true, browserDirect = false } = {}) {
|
|
67
|
+
// R7 incident premise: Chrome resolves under the REAL environment (read-only
|
|
68
|
+
// discovery) BEFORE isolation; the engine entry and every child load AFTER
|
|
69
|
+
// the driver enters the explicit environment whitelist.
|
|
70
|
+
const chromeExec = findChrome();
|
|
71
|
+
const isolation = enterNavIsolation({ tag: `pk-parallel-nav-${tag}` });
|
|
72
|
+
const fixture = isolation.root;
|
|
73
|
+
const evidence = evidenceDir || path.join(fixture, 'evidence');
|
|
74
|
+
fs.mkdirSync(evidence, { recursive: true });
|
|
75
|
+
const dataDir = path.join(fixture, 'data');
|
|
76
|
+
const knowledgeRoot = path.join(fixture, 'knowledge');
|
|
77
|
+
fs.mkdirSync(knowledgeRoot, { recursive: true });
|
|
78
|
+
|
|
79
|
+
const handles = { fixture, evidence, dataDir, knowledgeRoot };
|
|
80
|
+
let engine = null;
|
|
81
|
+
let engineProxy = null;
|
|
82
|
+
let engineToken = null;
|
|
83
|
+
let engineUrl = null;
|
|
84
|
+
|
|
85
|
+
// The vector stand-in starts BEFORE the engine: the engine constructor gets
|
|
86
|
+
// its URL explicitly, so its built-in default (127.0.0.1:8787 — a LIVE
|
|
87
|
+
// desktop hub here) is never reachable (R7 root-cause fix).
|
|
88
|
+
const moduleUi = createModuleUiFixture({ tag: `${tag}-module-ui` });
|
|
89
|
+
await moduleUi.start();
|
|
90
|
+
|
|
91
|
+
if (withEngine) {
|
|
92
|
+
engineToken = crypto.randomBytes(24).toString('hex');
|
|
93
|
+
const { createEngineServer } = require(ENGINE_ENTRY);
|
|
94
|
+
engine = createEngineServer({ dataDir: path.join(fixture, 'engine'), runtimeToken: engineToken, debug: true, consume: false, vectorUrl: moduleUi.url, bridgeUrl: moduleUi.url });
|
|
95
|
+
assert.equal(engine.coordinator.vectorUrl, moduleUi.url, 'the engine must run against the lane vector stand-in, never its 8787 default');
|
|
96
|
+
assert.equal(engine.coordinator.bridge.url, moduleUi.url, 'the engine bridge must use the lane stand-in even if a case enables consumption');
|
|
97
|
+
await engine.ready;
|
|
98
|
+
await new Promise(resolve => engine.server.listen(0, '127.0.0.1', resolve));
|
|
99
|
+
engineUrl = `http://127.0.0.1:${engine.server.address().port}`;
|
|
100
|
+
engineProxy = createUpstreamProxy({ target: engineUrl, token: engineToken, tag: 'engine' });
|
|
101
|
+
await engineProxy.start();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const port = await freePort();
|
|
105
|
+
const base = `http://127.0.0.1:${port}`;
|
|
106
|
+
const server = spawnServer({
|
|
107
|
+
root: ROOT,
|
|
108
|
+
port,
|
|
109
|
+
dataDir,
|
|
110
|
+
tag: `parallel-nav-${tag}`,
|
|
111
|
+
analysisOwner: 'engine',
|
|
112
|
+
extraEnv: {
|
|
113
|
+
KB_MODULES_REGISTER: '1',
|
|
114
|
+
KB_MODULES_AUTOSTART: '0',
|
|
115
|
+
KB_TERMINAL_URL: moduleUi.url,
|
|
116
|
+
KB_VECTORHUB_URL: moduleUi.url,
|
|
117
|
+
KB_EVENTBRIDGE_URL: moduleUi.url,
|
|
118
|
+
...(engineProxy ? { KB_KNOWLEDGE_ENGINE_URL: engineProxy.url } : {}),
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const productProxy = createNavProductProxy({ target: base, tag: `parallel-nav-${tag}` });
|
|
123
|
+
await productProxy.start();
|
|
124
|
+
|
|
125
|
+
const api = async (method, route, body) => {
|
|
126
|
+
const response = await fetch(base + route, {
|
|
127
|
+
method,
|
|
128
|
+
headers: body === undefined ? undefined : { 'Content-Type': 'application/json' },
|
|
129
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
130
|
+
signal: AbortSignal.timeout(20000),
|
|
131
|
+
});
|
|
132
|
+
return { status: response.status, body: await response.json().catch(() => null) };
|
|
133
|
+
};
|
|
134
|
+
const engineApi = async (method, route, body) => {
|
|
135
|
+
if (!engineUrl) throw new Error('the harness runs without the engine');
|
|
136
|
+
const response = await fetch(engineUrl + route, {
|
|
137
|
+
method,
|
|
138
|
+
headers: {
|
|
139
|
+
'Content-Type': 'application/json',
|
|
140
|
+
...(engineToken ? { Authorization: `Bearer ${engineToken}` } : {}),
|
|
141
|
+
},
|
|
142
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
143
|
+
signal: AbortSignal.timeout(20000),
|
|
144
|
+
});
|
|
145
|
+
return { status: response.status, body: await response.json().catch(() => null) };
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
await waitFor(async () => {
|
|
149
|
+
try { return (await api('GET', '/api/health')).status === 200; } catch { return false; }
|
|
150
|
+
}, 'the isolated product instance', 30000);
|
|
151
|
+
const settings = await api('PATCH', '/api/settings', { knowledge: { rootPath: knowledgeRoot } });
|
|
152
|
+
assert.equal(settings.status, 200, `the isolated instance accepts its own knowledge root: ${JSON.stringify(settings.body)}`);
|
|
153
|
+
|
|
154
|
+
let browser = null;
|
|
155
|
+
try {
|
|
156
|
+
browser = await launchCdpBrowser({
|
|
157
|
+
chrome: chromeExec,
|
|
158
|
+
profileDir: path.join(fixture, 'browser'),
|
|
159
|
+
// The live channel is a WebSocket upgrade: a plain HTTP proxy cannot
|
|
160
|
+
// carry it, so a case that needs the real live stream talks to the
|
|
161
|
+
// product directly and emulates the network failure in the browser.
|
|
162
|
+
url: (browserDirect ? base : productProxy.url) + '/',
|
|
163
|
+
width: viewport.width,
|
|
164
|
+
height: viewport.height,
|
|
165
|
+
});
|
|
166
|
+
await browser.setViewport(viewport.width, viewport.height);
|
|
167
|
+
await browser.send('Network.enable');
|
|
168
|
+
} catch (error) {
|
|
169
|
+
await server.cleanup();
|
|
170
|
+
await productProxy.close();
|
|
171
|
+
await moduleUi.close();
|
|
172
|
+
if (engineProxy) await engineProxy.close();
|
|
173
|
+
if (engine) await engine.close().catch(() => {});
|
|
174
|
+
fs.rmSync(fixture, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
175
|
+
isolation.restore();
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const capture = attachNetworkCapture(browser);
|
|
180
|
+
await capture.enable();
|
|
181
|
+
|
|
182
|
+
const run = expression => browser.evaluate(expression);
|
|
183
|
+
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
184
|
+
|
|
185
|
+
/** Import a real project through the real product API. */
|
|
186
|
+
const importProject = async (repo, displayName) => {
|
|
187
|
+
const knowledgePath = path.join(knowledgeRoot, displayName);
|
|
188
|
+
fs.mkdirSync(knowledgePath, { recursive: true });
|
|
189
|
+
const result = await api('POST', '/api/projects/import', { localPath: repo.path, knowledgePath, displayName });
|
|
190
|
+
assert.equal(result.status, 201, `the project import must succeed: ${JSON.stringify(result.body)}`);
|
|
191
|
+
return { projectId: result.body.projectId, project: result.body.project, knowledgePath };
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A native pointer click: the hit test runs first and must resolve to the
|
|
196
|
+
* target, so no transparent layer can stand in for a real click.
|
|
197
|
+
*/
|
|
198
|
+
const nativeClick = async (selector, { expectHit = true } = {}) => {
|
|
199
|
+
const probe = await run(`(() => {
|
|
200
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
201
|
+
if (!el) return { missing: true };
|
|
202
|
+
const rect = el.getBoundingClientRect();
|
|
203
|
+
if (rect.width <= 0 || rect.height <= 0) return { invisible: true, rect: { width: rect.width, height: rect.height } };
|
|
204
|
+
const x = rect.left + rect.width / 2;
|
|
205
|
+
const y = rect.top + rect.height / 2;
|
|
206
|
+
const hit = document.elementFromPoint(x, y);
|
|
207
|
+
return {
|
|
208
|
+
x, y,
|
|
209
|
+
width: rect.width, height: rect.height,
|
|
210
|
+
hitTag: hit ? hit.tagName : '',
|
|
211
|
+
hitId: hit && hit.id ? hit.id : '',
|
|
212
|
+
hitClass: hit && hit.className && typeof hit.className === 'string' ? hit.className : '',
|
|
213
|
+
hitIsTarget: Boolean(hit && (hit === el || el.contains(hit))),
|
|
214
|
+
label: (el.textContent || '').trim().slice(0, 40),
|
|
215
|
+
};
|
|
216
|
+
})()`);
|
|
217
|
+
assert.ok(probe && !probe.missing, `the control ${selector} must exist in the page`);
|
|
218
|
+
assert.ok(!probe.invisible, `the control ${selector} must be visible: ${JSON.stringify(probe)}`);
|
|
219
|
+
if (expectHit) {
|
|
220
|
+
assert.ok(probe.hitIsTarget, `the control ${selector} must be the hit target at (${probe.x}, ${probe.y}); something else is on top: ${probe.hitTag}#${probe.hitId}.${probe.hitClass}`);
|
|
221
|
+
}
|
|
222
|
+
const common = { x: probe.x, y: probe.y, button: 'left', clickCount: 1 };
|
|
223
|
+
await browser.send('Input.dispatchMouseEvent', { type: 'mouseMoved', x: probe.x, y: probe.y, button: 'none', clickCount: 0 });
|
|
224
|
+
await browser.send('Input.dispatchMouseEvent', { type: 'mousePressed', ...common });
|
|
225
|
+
await browser.send('Input.dispatchMouseEvent', { type: 'mouseReleased', ...common });
|
|
226
|
+
return probe;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/** A native key press (raw key events, no synthesized DOM events). */
|
|
230
|
+
const pressKey = async (key, { code, keyCode, text } = {}) => {
|
|
231
|
+
const base = { key, code: code || key, windowsVirtualKeyCode: keyCode, nativeVirtualKeyCode: keyCode };
|
|
232
|
+
await browser.send('Input.dispatchKeyEvent', { type: text ? 'keyDown' : 'rawKeyDown', ...base, text });
|
|
233
|
+
await browser.send('Input.dispatchKeyEvent', { type: 'keyUp', ...base });
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const pressTab = () => pressKey('Tab', { code: 'Tab', keyCode: 9 });
|
|
237
|
+
const pressEnter = () => pressKey('Enter', { code: 'Enter', keyCode: 13, text: '\r' });
|
|
238
|
+
|
|
239
|
+
/** The real browser network goes offline / online (CDP, not a mock). */
|
|
240
|
+
const setOffline = async offline => {
|
|
241
|
+
await browser.send('Network.emulateNetworkConditions', {
|
|
242
|
+
offline,
|
|
243
|
+
latency: 0,
|
|
244
|
+
downloadThroughput: offline ? 0 : -1,
|
|
245
|
+
uploadThroughput: offline ? 0 : -1,
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const setViewport = (width, height) => browser.setViewport(width, height);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The kb:* messages one embedded frame really received, in order. The
|
|
253
|
+
* module double records them inside the frame, so this is the protocol
|
|
254
|
+
* traffic itself and not a shell-side guess.
|
|
255
|
+
*/
|
|
256
|
+
const frameMessages = id => run(`(() => {
|
|
257
|
+
const frame = document.getElementById(${JSON.stringify(id)});
|
|
258
|
+
const messages = frame && frame.contentWindow && frame.contentWindow.__navMessages;
|
|
259
|
+
if (!Array.isArray(messages)) return null;
|
|
260
|
+
return messages.map(entry => ({
|
|
261
|
+
type: String(entry.type || ''),
|
|
262
|
+
theme: entry.payload ? entry.payload.theme : undefined,
|
|
263
|
+
visible: entry.payload ? entry.payload.visible : undefined,
|
|
264
|
+
project: entry.payload ? entry.payload.project : undefined,
|
|
265
|
+
projectId: entry.payload ? entry.payload.projectId : undefined,
|
|
266
|
+
requestId: entry.payload ? entry.payload.requestId : undefined,
|
|
267
|
+
}));
|
|
268
|
+
})()`);
|
|
269
|
+
|
|
270
|
+
/** The number of embedded-UI documents the module double really served. */
|
|
271
|
+
const modulePageLoads = () => moduleUi.pageLoads();
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Ask the shell to read the registry again, through the real event the
|
|
275
|
+
* shell itself listens for when the page becomes visible.
|
|
276
|
+
*/
|
|
277
|
+
const requestRegistryRefresh = () => run("document.dispatchEvent(new Event('visibilitychange'))");
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Make the shell read the registry again and wait until a new registry
|
|
281
|
+
* read really left the browser. The shell joins a registry read that is
|
|
282
|
+
* already running, so the visibility event is repeated until a new read
|
|
283
|
+
* shows up in the proxy.
|
|
284
|
+
*/
|
|
285
|
+
const refreshRegistryAndWait = async (match = entry => entry.path.startsWith('/api/projects/projection'), timeoutMs = 20000) => {
|
|
286
|
+
const before = productProxy.requests.filter(match).length;
|
|
287
|
+
const deadline = Date.now() + timeoutMs;
|
|
288
|
+
while (Date.now() < deadline) {
|
|
289
|
+
await requestRegistryRefresh();
|
|
290
|
+
await wait(300);
|
|
291
|
+
const now = productProxy.requests.filter(match).length;
|
|
292
|
+
if (now > before) return now;
|
|
293
|
+
}
|
|
294
|
+
throw new Error('the shell did not read the registry again');
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The engine's vector traffic stayed inside this run: every /api/sync and
|
|
299
|
+
* /api/import body the stand-in received must name a sourceDir inside the
|
|
300
|
+
* fixture root. The stand-in is the ONLY vector endpoint the engine can
|
|
301
|
+
* reach (constructor vectorUrl + whitelisted env), so this proves the run
|
|
302
|
+
* never touched the real 8787 hub's write surface.
|
|
303
|
+
*/
|
|
304
|
+
const assertVectorIsolation = () => {
|
|
305
|
+
const calls = [...moduleUi.vectorSyncs(), ...moduleUi.vectorImports()];
|
|
306
|
+
const violations = [];
|
|
307
|
+
for (const entry of calls) {
|
|
308
|
+
const sourceDir = entry.body && entry.body.sourceDir;
|
|
309
|
+
if (!sourceDir) continue;
|
|
310
|
+
const inside = sourceDir === fixture || sourceDir.startsWith(fixture + path.sep);
|
|
311
|
+
if (!inside) violations.push({ path: entry.path, sourceDir });
|
|
312
|
+
}
|
|
313
|
+
assert.deepEqual(violations, [], `vector calls must stay inside the fixture root (${fixture})`);
|
|
314
|
+
return { calls: calls.length, bodies: calls.map(entry => entry.body), violations };
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const cleanup = async () => {
|
|
318
|
+
await browser?.close();
|
|
319
|
+
await server.cleanup();
|
|
320
|
+
await productProxy.close();
|
|
321
|
+
await moduleUi.close();
|
|
322
|
+
if (engineProxy) await engineProxy.close();
|
|
323
|
+
if (engine) await engine.close().catch(() => {});
|
|
324
|
+
fs.rmSync(fixture, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
325
|
+
isolation.restore();
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
...handles,
|
|
330
|
+
base,
|
|
331
|
+
server,
|
|
332
|
+
productProxy,
|
|
333
|
+
moduleUi,
|
|
334
|
+
engine,
|
|
335
|
+
engineUrl,
|
|
336
|
+
engineApi,
|
|
337
|
+
engineProxy,
|
|
338
|
+
api,
|
|
339
|
+
browser,
|
|
340
|
+
capture,
|
|
341
|
+
run,
|
|
342
|
+
wait,
|
|
343
|
+
waitFor,
|
|
344
|
+
importProject,
|
|
345
|
+
nativeClick,
|
|
346
|
+
pressKey,
|
|
347
|
+
pressTab,
|
|
348
|
+
pressEnter,
|
|
349
|
+
setOffline,
|
|
350
|
+
setViewport,
|
|
351
|
+
frameMessages,
|
|
352
|
+
modulePageLoads,
|
|
353
|
+
requestRegistryRefresh,
|
|
354
|
+
refreshRegistryAndWait,
|
|
355
|
+
assertVectorIsolation,
|
|
356
|
+
isolation,
|
|
357
|
+
cleanup,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
module.exports = { startHarness, makeRepo, THEMES, ROOT };
|