taphound 0.2.0-dev.1 → 0.2.0-dev.12
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/README.md +49 -149
- package/README.zh-CN.md +47 -147
- package/assets/skills/taphound-case-suite/SKILL.md +363 -0
- package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
- package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
- package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
- package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
- package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
- package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
- package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
- package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
- package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
- package/assets/skills/taphound-flash/SKILL.md +115 -0
- package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
- package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
- package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
- package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
- package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
- package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
- package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
- package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
- package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
- package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
- package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
- package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
- package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
- package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
- package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
- package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
- package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
- package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
- package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
- package/assets/skills/taphound-verify-change/SKILL.md +91 -0
- package/assets/skills/taphound-verify-change/references/accept.md +38 -0
- package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
- package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
- package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
- package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
- package/dist/adapters/adb/adb-adapter.d.ts +16 -2
- package/dist/adapters/adb/adb-adapter.js +199 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
- package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
- package/dist/adapters/adb/ui-automator-parser.js +160 -0
- package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
- package/dist/adapters/adb/window-topology-parser.js +101 -0
- package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
- package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
- package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
- package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
- package/dist/adapters/android-cli/layout-parser.js +12 -8
- package/dist/adapters/appium/appium-doctor.d.ts +7 -0
- package/dist/adapters/appium/appium-doctor.js +128 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
- package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
- package/dist/adapters/camera/camera-probe-adapter.js +286 -0
- package/dist/adapters/filesystem/artifact-store.js +40 -1
- package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
- package/dist/adapters/filesystem/context-document-writer.js +112 -0
- package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
- package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
- package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
- package/dist/adapters/filesystem/external-flow-registry.js +325 -0
- package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
- package/dist/adapters/filesystem/generation-session-store.js +281 -855
- package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
- package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
- package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
- package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
- package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
- package/dist/adapters/filesystem/journey-composition-store.js +151 -0
- package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
- package/dist/adapters/filesystem/knowledge-registry.js +223 -0
- package/dist/adapters/filesystem/project-bound-file.js +3 -5
- package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
- package/dist/adapters/filesystem/project-file-inspector.js +57 -28
- package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
- package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
- package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
- package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
- package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
- package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
- package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
- package/dist/adapters/filesystem/skill-installer.js +40 -11
- package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
- package/dist/adapters/filesystem/workspace-layout.js +57 -0
- package/dist/adapters/git/node-git-diff.d.ts +17 -0
- package/dist/adapters/git/node-git-diff.js +114 -0
- package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
- package/dist/adapters/process/node-detached-process-launcher.js +96 -0
- package/dist/adapters/process/node-process-runner.js +11 -2
- package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
- package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
- package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
- package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
- package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
- package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
- package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
- package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
- package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
- package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
- package/dist/adapters/runtime/session-adb-view.js +193 -0
- package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
- package/dist/adapters/runtime/session-backed-ports.js +43 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
- package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
- package/dist/adapters/ui/device-layout-temp.js +18 -0
- package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
- package/dist/adapters/ui/device-ui-environment.js +81 -0
- package/dist/adapters/ui/layout-normalization.d.ts +9 -0
- package/dist/adapters/ui/layout-normalization.js +44 -0
- package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
- package/dist/adapters/ui/ui-snapshot-error.js +12 -0
- package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
- package/dist/adapters/ui/ui-snapshot-support.js +12 -0
- package/dist/application/align/align-service.d.ts +42 -0
- package/dist/application/align/align-service.js +106 -0
- package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
- package/dist/application/assertion/expectation-evaluator.js +156 -18
- package/dist/application/assertion/guarded-expectation.d.ts +45 -0
- package/dist/application/assertion/guarded-expectation.js +74 -0
- package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
- package/dist/application/checkpoint/baseline-capturer.js +163 -0
- package/dist/application/checkpoint/baseline-error.d.ts +5 -0
- package/dist/application/checkpoint/baseline-error.js +8 -0
- package/dist/application/checkpoint/baseline-service.d.ts +31 -0
- package/dist/application/checkpoint/baseline-service.js +147 -0
- package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
- package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
- package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
- package/dist/application/checkpoint/regression-comparator.js +192 -0
- package/dist/application/collector/logcat-collector.d.ts +22 -1
- package/dist/application/collector/logcat-collector.js +103 -15
- package/dist/application/collector/logcat-event.d.ts +8 -0
- package/dist/application/collector/logcat-event.js +79 -0
- package/dist/application/collector/logcat-stop.d.ts +2 -0
- package/dist/application/collector/logcat-stop.js +11 -0
- package/dist/application/context/context-generator.d.ts +45 -0
- package/dist/application/context/context-generator.js +228 -0
- package/dist/application/context/context-loader.d.ts +40 -0
- package/dist/application/context/context-loader.js +223 -0
- package/dist/application/context/context-refresher.d.ts +59 -0
- package/dist/application/context/context-refresher.js +349 -0
- package/dist/application/context/context-rehasher.d.ts +36 -0
- package/dist/application/context/context-rehasher.js +110 -0
- package/dist/application/context/context-validator.d.ts +23 -1
- package/dist/application/context/context-validator.js +116 -9
- package/dist/application/context/evidence-hash.d.ts +1 -0
- package/dist/application/context/evidence-hash.js +125 -0
- package/dist/application/context/evidence-match.d.ts +9 -0
- package/dist/application/context/evidence-match.js +8 -0
- package/dist/application/context/shard-identity.d.ts +2 -0
- package/dist/application/context/shard-identity.js +7 -0
- package/dist/application/contract/contract-loader.d.ts +25 -0
- package/dist/application/contract/contract-loader.js +68 -0
- package/dist/application/contract/contract-review.d.ts +25 -0
- package/dist/application/contract/contract-review.js +51 -0
- package/dist/application/contract/contract-verifier.d.ts +53 -0
- package/dist/application/contract/contract-verifier.js +645 -0
- package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
- package/dist/application/devices/resolve-device-assignments.js +60 -0
- package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
- package/dist/application/diagnosis/failure-classifier.js +179 -0
- package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
- package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
- package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
- package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
- package/dist/application/doctor/doctor-service.d.ts +36 -3
- package/dist/application/doctor/doctor-service.js +255 -25
- package/dist/application/generation/generation-app-preparer.d.ts +20 -0
- package/dist/application/generation/generation-app-preparer.js +28 -0
- package/dist/application/generation/generation-config-service.d.ts +21 -0
- package/dist/application/generation/generation-config-service.js +53 -0
- package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
- package/dist/application/generation/generation-confirmation-service.js +109 -24
- package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
- package/dist/application/generation/generation-context-snapshot.js +29 -0
- package/dist/application/generation/generation-finalizer.d.ts +15 -9
- package/dist/application/generation/generation-finalizer.js +167 -70
- package/dist/application/generation/generation-publisher.js +12 -8
- package/dist/application/generation/generation-recovery-service.d.ts +35 -0
- package/dist/application/generation/generation-recovery-service.js +144 -0
- package/dist/application/generation/generation-reopen-service.d.ts +15 -0
- package/dist/application/generation/generation-reopen-service.js +37 -0
- package/dist/application/generation/generation-replace-service.d.ts +32 -0
- package/dist/application/generation/generation-replace-service.js +116 -0
- package/dist/application/generation/generation-starter.d.ts +44 -4
- package/dist/application/generation/generation-starter.js +175 -8
- package/dist/application/generation/generation-step-executor.d.ts +67 -5
- package/dist/application/generation/generation-step-executor.js +471 -108
- package/dist/application/generation/proposed-step-validator.js +96 -30
- package/dist/application/generation/replay-policy-loader.d.ts +16 -0
- package/dist/application/generation/replay-policy-loader.js +29 -0
- package/dist/application/generation/risk-evaluator.d.ts +4 -1
- package/dist/application/generation/risk-evaluator.js +118 -3
- package/dist/application/generation/runtime-observer.d.ts +39 -7
- package/dist/application/generation/runtime-observer.js +193 -24
- package/dist/application/impact/impact-resolver.d.ts +32 -0
- package/dist/application/impact/impact-resolver.js +160 -0
- package/dist/application/init/init-service.js +26 -13
- package/dist/application/interaction/action-executor.d.ts +3 -2
- package/dist/application/interaction/action-executor.js +11 -1
- package/dist/application/interaction/action-target.d.ts +17 -0
- package/dist/application/interaction/action-target.js +40 -0
- package/dist/application/interaction/external-step-runner.d.ts +153 -0
- package/dist/application/interaction/external-step-runner.js +332 -0
- package/dist/application/interaction/fallback-resolver.d.ts +6 -3
- package/dist/application/interaction/fallback-resolver.js +7 -5
- package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
- package/dist/application/interaction/scroll-to-executor.js +118 -36
- package/dist/application/journey/external-flow-resolver.d.ts +25 -0
- package/dist/application/journey/external-flow-resolver.js +22 -0
- package/dist/application/journey/journey-check-service.d.ts +54 -0
- package/dist/application/journey/journey-check-service.js +207 -0
- package/dist/application/journey/journey-promoter.d.ts +26 -0
- package/dist/application/journey/journey-promoter.js +123 -0
- package/dist/application/journey/journey-resolver.d.ts +42 -0
- package/dist/application/journey/journey-resolver.js +239 -0
- package/dist/application/journey/journey-retirer.d.ts +25 -0
- package/dist/application/journey/journey-retirer.js +87 -0
- package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
- package/dist/application/knowledge/anchor-resolver.js +110 -0
- package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
- package/dist/application/knowledge/knowledge-loader.js +36 -0
- package/dist/application/locator/layout-failure-summary.d.ts +2 -0
- package/dist/application/locator/layout-failure-summary.js +67 -0
- package/dist/application/locator/layout-traversal.d.ts +11 -0
- package/dist/application/locator/layout-traversal.js +18 -0
- package/dist/application/locator/locator-resolver.d.ts +13 -1
- package/dist/application/locator/locator-resolver.js +182 -18
- package/dist/application/observe/observe-service.d.ts +20 -0
- package/dist/application/observe/observe-service.js +91 -0
- package/dist/application/project/project-describer.d.ts +14 -6
- package/dist/application/project/project-describer.js +38 -5
- package/dist/application/recognition/screen-detector.d.ts +28 -0
- package/dist/application/recognition/screen-detector.js +111 -0
- package/dist/application/recorder/locator-selector.js +151 -17
- package/dist/application/recorder/recorder-service.d.ts +16 -4
- package/dist/application/recorder/recorder-service.js +420 -128
- package/dist/application/report/report-writer.d.ts +5 -0
- package/dist/application/report/report-writer.js +17 -1
- package/dist/application/runtime/cold-launch.d.ts +28 -0
- package/dist/application/runtime/cold-launch.js +59 -0
- package/dist/application/runtime/step-runner.d.ts +49 -4
- package/dist/application/runtime/step-runner.js +721 -130
- package/dist/application/runtime/verify-runtime.d.ts +57 -7
- package/dist/application/runtime/verify-runtime.js +577 -175
- package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
- package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
- package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
- package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
- package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
- package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
- package/dist/application/ui/ui-stability-probe.d.ts +9 -0
- package/dist/application/ui/ui-stability-probe.js +24 -0
- package/dist/application/wait/idle-advice.d.ts +6 -0
- package/dist/application/wait/idle-advice.js +27 -0
- package/dist/application/wait/idle-profiles.d.ts +15 -0
- package/dist/application/wait/idle-profiles.js +75 -0
- package/dist/application/wait/idle-waiter.d.ts +28 -4
- package/dist/application/wait/idle-waiter.js +201 -15
- package/dist/cli/commands/align.d.ts +3 -0
- package/dist/cli/commands/align.js +124 -0
- package/dist/cli/commands/baseline.d.ts +3 -0
- package/dist/cli/commands/baseline.js +130 -0
- package/dist/cli/commands/context.js +300 -27
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.js +120 -0
- package/dist/cli/commands/diagnose.d.ts +3 -0
- package/dist/cli/commands/diagnose.js +84 -0
- package/dist/cli/commands/doctor.js +16 -9
- package/dist/cli/commands/failure.d.ts +3 -0
- package/dist/cli/commands/failure.js +63 -0
- package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
- package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
- package/dist/cli/commands/generation/session-commands.d.ts +11 -0
- package/dist/cli/commands/generation/session-commands.js +472 -0
- package/dist/cli/commands/generation/shared.d.ts +1414 -0
- package/dist/cli/commands/generation/shared.js +291 -0
- package/dist/cli/commands/generation/step-commands.d.ts +8 -0
- package/dist/cli/commands/generation/step-commands.js +315 -0
- package/dist/cli/commands/generation.js +10 -507
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.js +87 -0
- package/dist/cli/commands/init.js +13 -6
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.js +382 -0
- package/dist/cli/commands/knowledge.d.ts +3 -0
- package/dist/cli/commands/knowledge.js +93 -0
- package/dist/cli/commands/observe.d.ts +3 -0
- package/dist/cli/commands/observe.js +127 -0
- package/dist/cli/commands/project.js +2 -1
- package/dist/cli/commands/record.js +18 -5
- package/dist/cli/commands/verify.js +326 -64
- package/dist/cli/dependencies.d.ts +136 -1
- package/dist/cli/dependencies.js +504 -52
- package/dist/cli/diagnostics-recorder.d.ts +31 -0
- package/dist/cli/diagnostics-recorder.js +82 -0
- package/dist/cli/diff-verification.d.ts +41 -0
- package/dist/cli/diff-verification.js +192 -0
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.js +107 -2
- package/dist/cli/output.d.ts +5 -3
- package/dist/cli/output.js +12 -0
- package/dist/cli/program.js +21 -1
- package/dist/cli/project-root.d.ts +1 -0
- package/dist/cli/project-root.js +6 -0
- package/dist/cli/runtime-selection.d.ts +22 -0
- package/dist/cli/runtime-selection.js +92 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/workspace-guard.d.ts +2 -0
- package/dist/cli/workspace-guard.js +3 -0
- package/dist/domain/binding-reference.d.ts +4 -0
- package/dist/domain/binding-reference.js +23 -0
- package/dist/domain/checkpoint.d.ts +404 -0
- package/dist/domain/checkpoint.js +297 -0
- package/dist/domain/config.d.ts +107 -1
- package/dist/domain/config.js +52 -6
- package/dist/domain/contract.d.ts +413 -0
- package/dist/domain/contract.js +196 -0
- package/dist/domain/diagnostics.d.ts +1396 -0
- package/dist/domain/diagnostics.js +193 -0
- package/dist/domain/external-flow.d.ts +493 -0
- package/dist/domain/external-flow.js +15 -0
- package/dist/domain/failure-classification.d.ts +176 -0
- package/dist/domain/failure-classification.js +169 -0
- package/dist/domain/failure.d.ts +2 -1
- package/dist/domain/failure.js +123 -0
- package/dist/domain/generation.d.ts +1671 -132
- package/dist/domain/generation.js +202 -13
- package/dist/domain/geometry.d.ts +13 -0
- package/dist/domain/geometry.js +16 -0
- package/dist/domain/impact.d.ts +71 -0
- package/dist/domain/impact.js +76 -0
- package/dist/domain/init.d.ts +12 -4
- package/dist/domain/init.js +24 -18
- package/dist/domain/journey-composition.d.ts +2285 -0
- package/dist/domain/journey-composition.js +85 -0
- package/dist/domain/journey-lifecycle.d.ts +10 -0
- package/dist/domain/journey-lifecycle.js +9 -0
- package/dist/domain/journey.d.ts +2419 -212
- package/dist/domain/journey.js +384 -14
- package/dist/domain/knowledge.d.ts +165 -0
- package/dist/domain/knowledge.js +188 -0
- package/dist/domain/layout.d.ts +34 -5
- package/dist/domain/layout.js +64 -6
- package/dist/domain/locator-evidence.d.ts +3 -0
- package/dist/domain/locator-evidence.js +36 -0
- package/dist/domain/logcat-event.d.ts +54 -0
- package/dist/domain/logcat-event.js +40 -0
- package/dist/domain/observation.d.ts +33 -0
- package/dist/domain/observation.js +18 -0
- package/dist/domain/project-context.d.ts +219 -1
- package/dist/domain/project-context.js +209 -6
- package/dist/domain/proposed-step.d.ts +340 -60
- package/dist/domain/proposed-step.js +23 -3
- package/dist/domain/report.d.ts +1455 -28
- package/dist/domain/report.js +211 -14
- package/dist/domain/runtime-snapshot.d.ts +58 -1
- package/dist/domain/runtime-snapshot.js +14 -3
- package/dist/domain/runtime.d.ts +50 -0
- package/dist/domain/runtime.js +36 -0
- package/dist/domain/system-app-profiles.d.ts +6 -0
- package/dist/domain/system-app-profiles.js +43 -0
- package/dist/domain/ui-backend.d.ts +28 -0
- package/dist/domain/ui-backend.js +33 -0
- package/dist/domain/ui-cache.d.ts +11 -0
- package/dist/domain/ui-cache.js +10 -0
- package/dist/domain/verify-receipt.d.ts +17 -0
- package/dist/domain/verify-receipt.js +17 -0
- package/dist/domain/window-hierarchy.d.ts +84 -0
- package/dist/domain/window-hierarchy.js +172 -0
- package/dist/domain/workflow-manifest.d.ts +77 -0
- package/dist/domain/workflow-manifest.js +136 -0
- package/dist/domain/workspace.d.ts +30 -0
- package/dist/domain/workspace.js +74 -0
- package/dist/ports/adb.d.ts +34 -1
- package/dist/ports/align-prompt.d.ts +11 -0
- package/dist/ports/align-prompt.js +6 -0
- package/dist/ports/anchor-resolver.d.ts +30 -0
- package/dist/ports/anchor-resolver.js +1 -0
- package/dist/ports/annotated-screen-resolver.d.ts +4 -0
- package/dist/ports/annotated-screen-resolver.js +1 -0
- package/dist/ports/camera-probe.d.ts +23 -0
- package/dist/ports/camera-probe.js +8 -0
- package/dist/ports/context-document-writer.d.ts +24 -0
- package/dist/ports/context-document-writer.js +1 -0
- package/dist/ports/detached-process-launcher.d.ts +12 -0
- package/dist/ports/detached-process-launcher.js +1 -0
- package/dist/ports/diagnostics.d.ts +15 -0
- package/dist/ports/diagnostics.js +1 -0
- package/dist/ports/external-flow-registry.d.ts +37 -0
- package/dist/ports/external-flow-registry.js +1 -0
- package/dist/ports/generation-prompt.d.ts +2 -1
- package/dist/ports/generation-session-store.d.ts +12 -2
- package/dist/ports/git-diff.d.ts +9 -0
- package/dist/ports/git-diff.js +1 -0
- package/dist/ports/journey-composition-store.d.ts +17 -0
- package/dist/ports/journey-composition-store.js +1 -0
- package/dist/ports/knowledge-registry.d.ts +20 -0
- package/dist/ports/knowledge-registry.js +1 -0
- package/dist/ports/process-runner.d.ts +7 -0
- package/dist/ports/project-file-inspector.d.ts +1 -0
- package/dist/ports/project-identity-inspector.d.ts +24 -0
- package/dist/ports/project-identity-inspector.js +1 -0
- package/dist/ports/project-inventory-inspector.d.ts +21 -0
- package/dist/ports/project-inventory-inspector.js +1 -0
- package/dist/ports/project-module-discoverer.d.ts +26 -0
- package/dist/ports/project-module-discoverer.js +1 -0
- package/dist/ports/recorder-prompt.d.ts +10 -1
- package/dist/ports/runtime-backend.d.ts +89 -0
- package/dist/ports/runtime-backend.js +13 -0
- package/dist/ports/runtime-capability.d.ts +12 -0
- package/dist/ports/runtime-capability.js +20 -0
- package/dist/ports/runtime-session-ports.d.ts +25 -0
- package/dist/ports/runtime-session-ports.js +1 -0
- package/dist/ports/screenshot.d.ts +11 -0
- package/dist/ports/screenshot.js +1 -0
- package/dist/ports/skill-installer.d.ts +2 -2
- package/dist/ports/ui-snapshot.d.ts +35 -0
- package/dist/ports/ui-snapshot.js +1 -0
- package/dist/ports/ui-stability.d.ts +19 -0
- package/dist/ports/ui-stability.js +1 -0
- package/dist/ports/workspace-layout.d.ts +4 -0
- package/dist/ports/workspace-layout.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/paths.d.ts +2 -0
- package/dist/shared/paths.js +24 -0
- package/dist/shared/strings.d.ts +2 -0
- package/dist/shared/strings.js +16 -0
- package/package.json +7 -2
- package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
- package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
- package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
- package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
- package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
- package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
- package/dist/ports/android-cli.d.ts +0 -21
- /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// Filesystem primitives of the session store: identity, evidence paths, and atomic state.
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { constants, lstat, mkdir, open, realpath, rename, rm } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, join, posix, relative } from "node:path";
|
|
5
|
+
import { GenerationSessionStoreError } from "../../../ports/generation-session-store.js";
|
|
6
|
+
import { isErrnoException } from "../../../shared/errors.js";
|
|
7
|
+
import { compareStrings } from "../../../shared/strings.js";
|
|
8
|
+
import { parseSession, sessionId } from "./session-transitions.js";
|
|
9
|
+
export function snapshotMetadata(stats) {
|
|
10
|
+
return {
|
|
11
|
+
dev: stats.dev,
|
|
12
|
+
ino: stats.ino,
|
|
13
|
+
size: stats.size,
|
|
14
|
+
mtimeNs: stats.mtimeNs,
|
|
15
|
+
ctimeNs: stats.ctimeNs
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function sameSnapshotMetadata(left, right) {
|
|
19
|
+
return (sameIdentity(left, right)
|
|
20
|
+
&& left.size === right.size
|
|
21
|
+
&& left.mtimeNs === right.mtimeNs
|
|
22
|
+
&& left.ctimeNs === right.ctimeNs);
|
|
23
|
+
}
|
|
24
|
+
export function evidenceEntryType(entry) {
|
|
25
|
+
if (entry.isFile())
|
|
26
|
+
return "file";
|
|
27
|
+
if (entry.isDirectory())
|
|
28
|
+
return "directory";
|
|
29
|
+
if (entry.isSymbolicLink())
|
|
30
|
+
return "symlink";
|
|
31
|
+
return "other";
|
|
32
|
+
}
|
|
33
|
+
export function evidenceEntrySnapshot(entries) {
|
|
34
|
+
return entries
|
|
35
|
+
.map((entry) => `${entry.name}\0${evidenceEntryType(entry)}`)
|
|
36
|
+
.sort();
|
|
37
|
+
}
|
|
38
|
+
export function sameIdentity(left, right) {
|
|
39
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
40
|
+
}
|
|
41
|
+
export async function captureStoreDirectory(path) {
|
|
42
|
+
const stats = await lstat(path, { bigint: true });
|
|
43
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
44
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation store path is not a real directory: ${path}`);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
path,
|
|
48
|
+
canonicalPath: await realpath(path),
|
|
49
|
+
identity: { dev: stats.dev, ino: stats.ino }
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function verifyStoreDirectory(expected) {
|
|
53
|
+
const current = await captureStoreDirectory(expected.path);
|
|
54
|
+
if (current.canonicalPath !== expected.canonicalPath
|
|
55
|
+
|| !sameIdentity(current.identity, expected.identity)) {
|
|
56
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation store directory identity changed: ${expected.path}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function captureStoreFile(path) {
|
|
60
|
+
const stats = await lstat(path, { bigint: true });
|
|
61
|
+
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
62
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation store path is not a real file: ${path}`);
|
|
63
|
+
}
|
|
64
|
+
return { dev: stats.dev, ino: stats.ino };
|
|
65
|
+
}
|
|
66
|
+
export async function captureOptionalStoreFile(path) {
|
|
67
|
+
try {
|
|
68
|
+
return await captureStoreFile(path);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export async function verifyOptionalStoreFile(path, expected) {
|
|
78
|
+
const current = await captureOptionalStoreFile(path);
|
|
79
|
+
if ((current === null) !== (expected === null)
|
|
80
|
+
|| (current !== null
|
|
81
|
+
&& expected !== null
|
|
82
|
+
&& !sameIdentity(current, expected))) {
|
|
83
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation state file identity changed: ${path}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function asStoreIoError(error, action) {
|
|
87
|
+
if (error instanceof GenerationSessionStoreError) {
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
90
|
+
return new GenerationSessionStoreError("IO_ERROR", `Generation session store failed to ${action}`, { cause: error });
|
|
91
|
+
}
|
|
92
|
+
export async function pathExists(path) {
|
|
93
|
+
try {
|
|
94
|
+
await lstat(path);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function requireRealDirectory(path) {
|
|
105
|
+
const stats = await lstat(path);
|
|
106
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
107
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation store path is not a real directory: ${path}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function createOrRequireDirectory(path) {
|
|
111
|
+
let created = false;
|
|
112
|
+
try {
|
|
113
|
+
await mkdir(path);
|
|
114
|
+
created = true;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (!isErrnoException(error) || error.code !== "EEXIST") {
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
await requireRealDirectory(path);
|
|
122
|
+
return created;
|
|
123
|
+
}
|
|
124
|
+
export async function syncDirectory(path) {
|
|
125
|
+
const handle = await open(path, constants.O_RDONLY);
|
|
126
|
+
try {
|
|
127
|
+
await handle.sync();
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
await handle.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function serializeJson(value) {
|
|
134
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
135
|
+
}
|
|
136
|
+
export function canonicalizeEvidenceValue(value, ancestors = new Set()) {
|
|
137
|
+
if (value === null
|
|
138
|
+
|| typeof value === "string"
|
|
139
|
+
|| typeof value === "boolean") {
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
if (typeof value === "number") {
|
|
143
|
+
if (Number.isFinite(value)) {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE", "Evidence JSON cannot contain non-finite numbers");
|
|
147
|
+
}
|
|
148
|
+
if (typeof value !== "object") {
|
|
149
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE", "Evidence value must be JSON serializable");
|
|
150
|
+
}
|
|
151
|
+
if (ancestors.has(value)) {
|
|
152
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE", "Evidence JSON cannot contain cycles");
|
|
153
|
+
}
|
|
154
|
+
ancestors.add(value);
|
|
155
|
+
try {
|
|
156
|
+
if (Array.isArray(value)) {
|
|
157
|
+
return value.map((item) => canonicalizeEvidenceValue(item, ancestors));
|
|
158
|
+
}
|
|
159
|
+
const prototype = Object.getPrototypeOf(value);
|
|
160
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
161
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE", "Evidence value must contain only JSON objects");
|
|
162
|
+
}
|
|
163
|
+
return Object.fromEntries(Object.keys(value)
|
|
164
|
+
.sort((left, right) => compareStrings(left, right))
|
|
165
|
+
.map((key) => [
|
|
166
|
+
key,
|
|
167
|
+
canonicalizeEvidenceValue(value[key], ancestors)
|
|
168
|
+
]));
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
ancestors.delete(value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export function canonicalizeEvidence(value) {
|
|
175
|
+
try {
|
|
176
|
+
return canonicalizeEvidenceValue(value);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof GenerationSessionStoreError) {
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE", "Evidence value cannot be safely inspected", { cause: error });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function validateEvidencePath(relativePath) {
|
|
186
|
+
if (typeof relativePath !== "string"
|
|
187
|
+
|| relativePath.length === 0
|
|
188
|
+
|| relativePath.includes("\\")
|
|
189
|
+
|| relativePath.includes("\0")
|
|
190
|
+
|| isAbsolute(relativePath)
|
|
191
|
+
|| posix.isAbsolute(relativePath)) {
|
|
192
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", "Invalid generation evidence path");
|
|
193
|
+
}
|
|
194
|
+
const segments = relativePath.split("/");
|
|
195
|
+
if (segments.some((segment) => (segment.length === 0
|
|
196
|
+
|| segment === "."
|
|
197
|
+
|| segment === ".."))
|
|
198
|
+
|| posix.normalize(relativePath) !== relativePath) {
|
|
199
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", "Invalid generation evidence path");
|
|
200
|
+
}
|
|
201
|
+
return segments;
|
|
202
|
+
}
|
|
203
|
+
export function assertContained(root, candidate, allowRoot = false) {
|
|
204
|
+
const fromRoot = relative(root, candidate);
|
|
205
|
+
if ((!allowRoot && fromRoot.length === 0)
|
|
206
|
+
|| fromRoot.startsWith("..")
|
|
207
|
+
|| isAbsolute(fromRoot)) {
|
|
208
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", `Evidence path escapes generation session: ${candidate}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export async function captureDirectoryEvidence(path) {
|
|
212
|
+
const stats = await lstat(path, { bigint: true });
|
|
213
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
214
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", `Evidence parent is not a real directory: ${path}`);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
path,
|
|
218
|
+
canonicalPath: await realpath(path),
|
|
219
|
+
identity: { dev: stats.dev, ino: stats.ino }
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export async function verifyDirectoryEvidence(activeCanonicalPath, directories) {
|
|
223
|
+
for (const evidence of directories) {
|
|
224
|
+
const current = await captureDirectoryEvidence(evidence.path);
|
|
225
|
+
assertContained(activeCanonicalPath, current.canonicalPath, true);
|
|
226
|
+
if (current.canonicalPath !== evidence.canonicalPath
|
|
227
|
+
|| current.identity.dev !== evidence.identity.dev
|
|
228
|
+
|| current.identity.ino !== evidence.identity.ino) {
|
|
229
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", `Evidence parent identity changed: ${evidence.path}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
export async function assertEvidenceNamespaceAvailable(activeDirectory, activeEvidence, inFlight) {
|
|
234
|
+
const relativePath = `evidence/steps/${String(inFlight.stepIndex)}-${inFlight.attemptId}`;
|
|
235
|
+
const segments = validateEvidencePath(relativePath);
|
|
236
|
+
await verifyStoreDirectory(activeEvidence);
|
|
237
|
+
let parent = activeDirectory;
|
|
238
|
+
const directories = [activeEvidence];
|
|
239
|
+
for (const [index, segment] of segments.entries()) {
|
|
240
|
+
const candidate = join(parent, segment);
|
|
241
|
+
assertContained(activeDirectory, candidate);
|
|
242
|
+
let stats;
|
|
243
|
+
try {
|
|
244
|
+
stats = await lstat(candidate);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
248
|
+
await verifyDirectoryEvidence(activeEvidence.canonicalPath, directories);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
if (index === segments.length - 1) {
|
|
254
|
+
throw new GenerationSessionStoreError("EVIDENCE_ALREADY_EXISTS", `Generation attempt evidence namespace already exists: ${relativePath}`);
|
|
255
|
+
}
|
|
256
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
257
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", `Attempt evidence parent is not a safe directory: ${relativePath}`);
|
|
258
|
+
}
|
|
259
|
+
const evidence = await captureDirectoryEvidence(candidate);
|
|
260
|
+
assertContained(activeEvidence.canonicalPath, evidence.canonicalPath, true);
|
|
261
|
+
directories.push(evidence);
|
|
262
|
+
parent = candidate;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
export async function fileIdentity(path) {
|
|
266
|
+
const stats = await lstat(path, { bigint: true });
|
|
267
|
+
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
268
|
+
throw new GenerationSessionStoreError("INVALID_EVIDENCE_PATH", `Evidence output is not a regular file: ${path}`);
|
|
269
|
+
}
|
|
270
|
+
return { dev: stats.dev, ino: stats.ino };
|
|
271
|
+
}
|
|
272
|
+
export function evidenceNotFound(relativePath, cause) {
|
|
273
|
+
return new GenerationSessionStoreError("EVIDENCE_NOT_FOUND", `Generation evidence does not exist: ${relativePath}`, { cause });
|
|
274
|
+
}
|
|
275
|
+
export async function readStateFromDirectory(directory, afterOpen, expectedDirectory) {
|
|
276
|
+
const directoryEvidence = expectedDirectory
|
|
277
|
+
?? await captureStoreDirectory(directory);
|
|
278
|
+
await verifyStoreDirectory(directoryEvidence);
|
|
279
|
+
const statePath = join(directory, "state.json");
|
|
280
|
+
let text;
|
|
281
|
+
try {
|
|
282
|
+
const handle = await open(statePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
283
|
+
try {
|
|
284
|
+
const openedStats = await handle.stat({ bigint: true });
|
|
285
|
+
if (!openedStats.isFile()) {
|
|
286
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation state is not a regular file: ${statePath}`);
|
|
287
|
+
}
|
|
288
|
+
const openedIdentity = {
|
|
289
|
+
dev: openedStats.dev,
|
|
290
|
+
ino: openedStats.ino
|
|
291
|
+
};
|
|
292
|
+
await afterOpen?.();
|
|
293
|
+
await verifyStoreDirectory(directoryEvidence);
|
|
294
|
+
const pathIdentity = await captureStoreFile(statePath);
|
|
295
|
+
if (!sameIdentity(openedIdentity, pathIdentity)) {
|
|
296
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation state file identity changed: ${statePath}`);
|
|
297
|
+
}
|
|
298
|
+
text = await handle.readFile("utf8");
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
await handle.close();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
if (error instanceof GenerationSessionStoreError) {
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Unable to read generation session state: ${statePath}`, { cause: error });
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
return parseSession(JSON.parse(text));
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (error instanceof GenerationSessionStoreError) {
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
throw new GenerationSessionStoreError("INVALID_SESSION", `Unable to parse generation session state: ${statePath}`, { cause: error });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
export async function readBoundState(directory, id, afterOpen, expectedDirectory) {
|
|
321
|
+
const session = await readStateFromDirectory(directory, afterOpen, expectedDirectory);
|
|
322
|
+
if (sessionId(session) !== id) {
|
|
323
|
+
throw new GenerationSessionStoreError("INVALID_SESSION", "Persisted generation session id does not match its directory");
|
|
324
|
+
}
|
|
325
|
+
return session;
|
|
326
|
+
}
|
|
327
|
+
export async function writeStateAtomically(directory, session, sync = syncDirectory, beforeRename, expectedDirectory, beforeInstall) {
|
|
328
|
+
const directoryEvidence = expectedDirectory
|
|
329
|
+
?? await captureStoreDirectory(directory);
|
|
330
|
+
await verifyStoreDirectory(directoryEvidence);
|
|
331
|
+
const statePath = join(directory, "state.json");
|
|
332
|
+
const originalStateIdentity = await captureOptionalStoreFile(statePath);
|
|
333
|
+
const temporaryPath = join(directory, `.state.json.${randomUUID()}.tmp`);
|
|
334
|
+
let handle;
|
|
335
|
+
try {
|
|
336
|
+
handle = await open(temporaryPath, constants.O_WRONLY
|
|
337
|
+
| constants.O_CREAT
|
|
338
|
+
| constants.O_EXCL
|
|
339
|
+
| constants.O_NOFOLLOW, 0o600);
|
|
340
|
+
await handle.writeFile(serializeJson(session), "utf8");
|
|
341
|
+
await handle.sync();
|
|
342
|
+
await handle.close();
|
|
343
|
+
handle = undefined;
|
|
344
|
+
const temporaryIdentity = await captureStoreFile(temporaryPath);
|
|
345
|
+
await beforeRename?.();
|
|
346
|
+
await verifyStoreDirectory(directoryEvidence);
|
|
347
|
+
await verifyOptionalStoreFile(statePath, originalStateIdentity);
|
|
348
|
+
await beforeInstall?.();
|
|
349
|
+
await rename(temporaryPath, statePath);
|
|
350
|
+
const installedIdentity = await captureStoreFile(statePath);
|
|
351
|
+
if (!sameIdentity(temporaryIdentity, installedIdentity)) {
|
|
352
|
+
throw new GenerationSessionStoreError("IO_ERROR", `Generation state install identity changed: ${statePath}`);
|
|
353
|
+
}
|
|
354
|
+
await sync(directory);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (handle !== undefined) {
|
|
358
|
+
await handle.close().catch(() => undefined);
|
|
359
|
+
}
|
|
360
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JourneyCompositionStore } from "../../ports/journey-composition-store.js";
|
|
2
|
+
export declare class FileSystemJourneyCompositionStore implements JourneyCompositionStore {
|
|
3
|
+
private readonly inspector;
|
|
4
|
+
readonly read: (input: {
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
relativePath: string;
|
|
7
|
+
}) => Promise<Buffer>;
|
|
8
|
+
readonly listFlowPaths: (projectRoot: string) => Promise<readonly string[]>;
|
|
9
|
+
readonly listJourneyPaths: (projectRoot: string) => Promise<readonly string[]>;
|
|
10
|
+
readonly readJourneyMeta: (input: {
|
|
11
|
+
projectRoot: string;
|
|
12
|
+
journeyPath: string;
|
|
13
|
+
}) => Promise<Buffer | null>;
|
|
14
|
+
readonly writeText: (input: {
|
|
15
|
+
projectRoot: string;
|
|
16
|
+
relativePath: string;
|
|
17
|
+
content: string;
|
|
18
|
+
}) => Promise<void>;
|
|
19
|
+
readonly readOutput: (input: {
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
relativePath: string;
|
|
22
|
+
}) => Promise<Buffer>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { lstat, readdir, realpath } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { BUILD_DIR, EXTERNAL_FLOWS_DIR, FLOWS_DIR, JOURNEYS_DIR } from "../../domain/workspace.js";
|
|
4
|
+
import { NodeProjectFileInspector } from "./project-file-inspector.js";
|
|
5
|
+
import { readProjectBoundFile, writeProjectBoundText } from "./project-bound-file.js";
|
|
6
|
+
import { ensureBuildLayout } from "./workspace-layout.js";
|
|
7
|
+
const MAX_COMPOSITION_BYTES = 1024 * 1024;
|
|
8
|
+
function contained(root, candidate) {
|
|
9
|
+
const fromRoot = relative(root, candidate);
|
|
10
|
+
return fromRoot === ""
|
|
11
|
+
|| (fromRoot !== ".."
|
|
12
|
+
&& !fromRoot.startsWith(`..${sep}`)
|
|
13
|
+
&& !isAbsolute(fromRoot));
|
|
14
|
+
}
|
|
15
|
+
export class FileSystemJourneyCompositionStore {
|
|
16
|
+
inspector = new NodeProjectFileInspector();
|
|
17
|
+
read = async (input) => {
|
|
18
|
+
const inspected = await this.inspector.inspectProjectFile({
|
|
19
|
+
projectRoot: input.projectRoot,
|
|
20
|
+
relativePath: input.relativePath,
|
|
21
|
+
maximumBytes: MAX_COMPOSITION_BYTES
|
|
22
|
+
});
|
|
23
|
+
if (inspected.status !== "inspected" || inspected.bytes === undefined) {
|
|
24
|
+
throw new Error(`Unable to safely read ${input.relativePath}: ${inspected.status}`);
|
|
25
|
+
}
|
|
26
|
+
return inspected.bytes;
|
|
27
|
+
};
|
|
28
|
+
listFlowPaths = async (projectRoot) => {
|
|
29
|
+
const canonicalRoot = await realpath(projectRoot);
|
|
30
|
+
const flowRoot = resolve(canonicalRoot, FLOWS_DIR);
|
|
31
|
+
try {
|
|
32
|
+
const canonicalFlowRoot = await realpath(flowRoot);
|
|
33
|
+
if (!contained(canonicalRoot, canonicalFlowRoot)) {
|
|
34
|
+
throw new Error("Flow directory escapes the project root");
|
|
35
|
+
}
|
|
36
|
+
const rootStats = await lstat(flowRoot);
|
|
37
|
+
if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
|
|
38
|
+
throw new Error("Flow path is not a safe directory");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof Error
|
|
43
|
+
&& "code" in error
|
|
44
|
+
&& error.code === "ENOENT") {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
const paths = [];
|
|
50
|
+
const externalDir = resolve(canonicalRoot, EXTERNAL_FLOWS_DIR);
|
|
51
|
+
const visit = async (directory) => {
|
|
52
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const path = join(directory, entry.name);
|
|
55
|
+
if (entry.isSymbolicLink()) {
|
|
56
|
+
throw new Error(`Flow catalog cannot contain symlinks: ${path}`);
|
|
57
|
+
}
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
if (path === externalDir) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
await visit(path);
|
|
63
|
+
}
|
|
64
|
+
else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
65
|
+
paths.push(relative(canonicalRoot, path).replaceAll("\\", "/"));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
await visit(flowRoot);
|
|
70
|
+
return paths.sort();
|
|
71
|
+
};
|
|
72
|
+
listJourneyPaths = async (projectRoot) => {
|
|
73
|
+
const canonicalRoot = await realpath(projectRoot);
|
|
74
|
+
const journeyRoot = resolve(canonicalRoot, JOURNEYS_DIR);
|
|
75
|
+
try {
|
|
76
|
+
const canonicalJourneyRoot = await realpath(journeyRoot);
|
|
77
|
+
if (!contained(canonicalRoot, canonicalJourneyRoot)) {
|
|
78
|
+
throw new Error("Journey directory escapes the project root");
|
|
79
|
+
}
|
|
80
|
+
const rootStats = await lstat(journeyRoot);
|
|
81
|
+
if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
|
|
82
|
+
throw new Error("Journey path is not a safe directory");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error instanceof Error
|
|
87
|
+
&& "code" in error
|
|
88
|
+
&& error.code === "ENOENT") {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
const paths = [];
|
|
94
|
+
const visit = async (directory) => {
|
|
95
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
const path = join(directory, entry.name);
|
|
98
|
+
if (entry.isSymbolicLink()) {
|
|
99
|
+
throw new Error(`Journey catalog cannot contain symlinks: ${path}`);
|
|
100
|
+
}
|
|
101
|
+
if (entry.isDirectory()) {
|
|
102
|
+
await visit(path);
|
|
103
|
+
}
|
|
104
|
+
else if (entry.isFile()
|
|
105
|
+
&& entry.name.endsWith(".json")
|
|
106
|
+
&& !entry.name.endsWith(".meta.json")
|
|
107
|
+
&& !entry.name.endsWith(".resolve.json")) {
|
|
108
|
+
paths.push(relative(canonicalRoot, path).replaceAll("\\", "/"));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
await visit(journeyRoot);
|
|
113
|
+
return paths.sort();
|
|
114
|
+
};
|
|
115
|
+
readJourneyMeta = async (input) => {
|
|
116
|
+
if (!input.journeyPath.endsWith(".json")
|
|
117
|
+
|| input.journeyPath.endsWith(".meta.json")
|
|
118
|
+
|| input.journeyPath.endsWith(".resolve.json")) {
|
|
119
|
+
throw new Error(`Journey path must be a normalized JSON file: ${input.journeyPath}`);
|
|
120
|
+
}
|
|
121
|
+
const metaPath = `${input.journeyPath.slice(0, -".json".length)}.meta.json`;
|
|
122
|
+
const inspected = await this.inspector.inspectProjectFile({
|
|
123
|
+
projectRoot: input.projectRoot,
|
|
124
|
+
relativePath: metaPath,
|
|
125
|
+
maximumBytes: MAX_COMPOSITION_BYTES
|
|
126
|
+
});
|
|
127
|
+
if (inspected.status === "notFound") {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (inspected.status !== "inspected" || inspected.bytes === undefined) {
|
|
131
|
+
throw new Error(`Unable to safely read ${metaPath}: ${inspected.status}`);
|
|
132
|
+
}
|
|
133
|
+
return inspected.bytes;
|
|
134
|
+
};
|
|
135
|
+
writeText = async (input) => {
|
|
136
|
+
await ensureBuildLayout(input.projectRoot);
|
|
137
|
+
await writeProjectBoundText({
|
|
138
|
+
projectRoot: input.projectRoot,
|
|
139
|
+
authorityRoot: resolve(input.projectRoot, BUILD_DIR),
|
|
140
|
+
outputPath: resolve(input.projectRoot, input.relativePath)
|
|
141
|
+
}, input.content);
|
|
142
|
+
};
|
|
143
|
+
readOutput = async (input) => {
|
|
144
|
+
await ensureBuildLayout(input.projectRoot);
|
|
145
|
+
return readProjectBoundFile({
|
|
146
|
+
projectRoot: input.projectRoot,
|
|
147
|
+
authorityRoot: resolve(input.projectRoot, BUILD_DIR),
|
|
148
|
+
outputPath: resolve(input.projectRoot, input.relativePath)
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { KnowledgeRegistryPort, KnowledgeRehashResult, LoadedKnowledgeBundle } from "../../ports/knowledge-registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Knowledge is a committed, hand- or agent-authored library of semantic
|
|
4
|
+
* Anchors and Screens. `index.json` binds every document by content hash;
|
|
5
|
+
* `rehash` is the only writer and rebuilds the index from the documents.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FileSystemKnowledgeRegistry implements KnowledgeRegistryPort {
|
|
8
|
+
readonly load: (projectRoot: string) => Promise<LoadedKnowledgeBundle>;
|
|
9
|
+
readonly rehash: (projectRoot: string, packageName: string) => Promise<KnowledgeRehashResult>;
|
|
10
|
+
private scanDocuments;
|
|
11
|
+
private readDocuments;
|
|
12
|
+
}
|