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,223 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, readdir, readFile, realpath, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { AnchorDefinitionSchema, KnowledgeBundleIndexSchema, ScreenDefinitionSchema, hashKnowledge } from "../../domain/knowledge.js";
|
|
5
|
+
import { KNOWLEDGE_DIR, KNOWLEDGE_INDEX_PATH } from "../../domain/workspace.js";
|
|
6
|
+
import { isErrnoException } from "../../shared/errors.js";
|
|
7
|
+
import { isContained } from "../../shared/paths.js";
|
|
8
|
+
const MAX_KNOWLEDGE_FILE_BYTES = 1024 * 1024;
|
|
9
|
+
function sha256(bytes) {
|
|
10
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
11
|
+
}
|
|
12
|
+
function serializedDocument(value) {
|
|
13
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
14
|
+
}
|
|
15
|
+
async function readBoundedJson(path) {
|
|
16
|
+
const before = await lstat(path);
|
|
17
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
18
|
+
throw new Error(`Knowledge document is not a regular file: ${path}`);
|
|
19
|
+
}
|
|
20
|
+
if (before.size > MAX_KNOWLEDGE_FILE_BYTES) {
|
|
21
|
+
throw new Error(`Knowledge document exceeds the size limit: ${path}`);
|
|
22
|
+
}
|
|
23
|
+
const bytes = await readFile(path);
|
|
24
|
+
const after = await lstat(path);
|
|
25
|
+
if (before.dev !== after.dev
|
|
26
|
+
|| before.ino !== after.ino
|
|
27
|
+
|| before.size !== after.size
|
|
28
|
+
|| after.isSymbolicLink()) {
|
|
29
|
+
throw new Error(`Knowledge document changed while loading: ${path}`);
|
|
30
|
+
}
|
|
31
|
+
let value;
|
|
32
|
+
try {
|
|
33
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw new Error(`Knowledge document is not valid JSON: ${path}`, {
|
|
37
|
+
cause: error
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return { bytes, value };
|
|
41
|
+
}
|
|
42
|
+
async function atomicWrite(path, value) {
|
|
43
|
+
const temporary = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`);
|
|
44
|
+
await writeFile(temporary, serializedDocument(value), {
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
mode: 0o644,
|
|
47
|
+
flag: "wx"
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
await rename(temporary, path);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
await unlink(temporary).catch(() => undefined);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function expectedPath(kind, id) {
|
|
57
|
+
return `${KNOWLEDGE_DIR}/${kind}/${id}.json`;
|
|
58
|
+
}
|
|
59
|
+
function assertReferencePath(kind, id, path) {
|
|
60
|
+
if (path !== expectedPath(kind, id)) {
|
|
61
|
+
throw new Error(`Knowledge reference ${id} must use ${expectedPath(kind, id)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function assertBundleReferences(input) {
|
|
65
|
+
const anchorsById = new Map(input.anchors.map((anchor) => [anchor.id, anchor]));
|
|
66
|
+
for (const screen of input.screens) {
|
|
67
|
+
const references = [
|
|
68
|
+
...screen.requiredAnchors,
|
|
69
|
+
...screen.optionalAnchors,
|
|
70
|
+
...screen.forbiddenAnchors,
|
|
71
|
+
...screen.predicates
|
|
72
|
+
.filter((predicate) => predicate.kind !== "activityIs")
|
|
73
|
+
.map((predicate) => predicate.anchorId)
|
|
74
|
+
];
|
|
75
|
+
for (const anchorId of references) {
|
|
76
|
+
if (!anchorsById.has(anchorId)) {
|
|
77
|
+
throw new Error(`Screen ${screen.id} references unknown Anchor ${anchorId}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const predicate of screen.predicates) {
|
|
81
|
+
if (predicate.kind === "windowPresent"
|
|
82
|
+
&& anchorsById.get(predicate.anchorId)?.identity.kind !== "window") {
|
|
83
|
+
throw new Error(`Screen ${screen.id} window predicate requires a window Anchor`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function bundleHash(indexSha256, index) {
|
|
89
|
+
return hashKnowledge({
|
|
90
|
+
indexSha256,
|
|
91
|
+
anchors: index.anchors.map(({ id, sha256: hash }) => ({ id, sha256: hash })),
|
|
92
|
+
screens: index.screens.map(({ id, sha256: hash }) => ({ id, sha256: hash }))
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function sameReferences(left, right) {
|
|
96
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Knowledge is a committed, hand- or agent-authored library of semantic
|
|
100
|
+
* Anchors and Screens. `index.json` binds every document by content hash;
|
|
101
|
+
* `rehash` is the only writer and rebuilds the index from the documents.
|
|
102
|
+
*/
|
|
103
|
+
export class FileSystemKnowledgeRegistry {
|
|
104
|
+
load = async (projectRoot) => {
|
|
105
|
+
const canonicalProject = await realpath(projectRoot);
|
|
106
|
+
const indexPath = resolve(canonicalProject, KNOWLEDGE_INDEX_PATH);
|
|
107
|
+
if (!isContained(canonicalProject, indexPath)) {
|
|
108
|
+
throw new Error("Knowledge index escapes the project root");
|
|
109
|
+
}
|
|
110
|
+
const loadedIndex = await readBoundedJson(indexPath);
|
|
111
|
+
const index = KnowledgeBundleIndexSchema.parse(loadedIndex.value);
|
|
112
|
+
const anchors = await this.readDocuments(canonicalProject, "anchors", index.anchors, AnchorDefinitionSchema);
|
|
113
|
+
const screens = await this.readDocuments(canonicalProject, "screens", index.screens, ScreenDefinitionSchema);
|
|
114
|
+
assertBundleReferences({ anchors, screens });
|
|
115
|
+
const indexSha256 = sha256(loadedIndex.bytes);
|
|
116
|
+
return {
|
|
117
|
+
index,
|
|
118
|
+
indexSha256,
|
|
119
|
+
knowledgeHash: bundleHash(indexSha256, index),
|
|
120
|
+
anchors,
|
|
121
|
+
screens
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
rehash = async (projectRoot, packageName) => {
|
|
125
|
+
const canonicalProject = await realpath(projectRoot);
|
|
126
|
+
const indexPath = resolve(canonicalProject, KNOWLEDGE_INDEX_PATH);
|
|
127
|
+
let existing;
|
|
128
|
+
try {
|
|
129
|
+
existing = KnowledgeBundleIndexSchema.parse((await readBoundedJson(indexPath)).value);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (!isErrnoException(error) || error.code !== "ENOENT")
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
if (existing !== undefined && existing.packageName !== packageName) {
|
|
136
|
+
throw new Error(`Knowledge package ${existing.packageName} does not match configured package ${packageName}`);
|
|
137
|
+
}
|
|
138
|
+
const anchors = await this.scanDocuments(canonicalProject, "anchors", AnchorDefinitionSchema);
|
|
139
|
+
const screens = await this.scanDocuments(canonicalProject, "screens", ScreenDefinitionSchema);
|
|
140
|
+
assertBundleReferences({
|
|
141
|
+
anchors: anchors.map(({ document }) => document),
|
|
142
|
+
screens: screens.map(({ document }) => document)
|
|
143
|
+
});
|
|
144
|
+
const references = {
|
|
145
|
+
anchors: anchors.map(({ reference }) => reference),
|
|
146
|
+
screens: screens.map(({ reference }) => reference)
|
|
147
|
+
};
|
|
148
|
+
const changed = existing === undefined
|
|
149
|
+
|| !sameReferences(existing.anchors, references.anchors)
|
|
150
|
+
|| !sameReferences(existing.screens, references.screens);
|
|
151
|
+
const index = KnowledgeBundleIndexSchema.parse({
|
|
152
|
+
version: 1,
|
|
153
|
+
packageName,
|
|
154
|
+
revision: existing === undefined
|
|
155
|
+
? 1
|
|
156
|
+
: existing.revision + (changed ? 1 : 0),
|
|
157
|
+
...references
|
|
158
|
+
});
|
|
159
|
+
if (changed) {
|
|
160
|
+
await atomicWrite(indexPath, index);
|
|
161
|
+
}
|
|
162
|
+
const indexSha256 = sha256(await readFile(indexPath));
|
|
163
|
+
return {
|
|
164
|
+
indexPath: relative(canonicalProject, indexPath).replaceAll("\\", "/"),
|
|
165
|
+
knowledgeHash: bundleHash(indexSha256, index),
|
|
166
|
+
revision: index.revision,
|
|
167
|
+
changed,
|
|
168
|
+
anchors: index.anchors.length,
|
|
169
|
+
screens: index.screens.length
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
async scanDocuments(canonicalProject, kind, schema) {
|
|
173
|
+
const directory = resolve(canonicalProject, KNOWLEDGE_DIR, kind);
|
|
174
|
+
let names;
|
|
175
|
+
try {
|
|
176
|
+
names = await readdir(directory);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (isErrnoException(error) && error.code === "ENOENT")
|
|
180
|
+
return [];
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
const scanned = [];
|
|
184
|
+
for (const name of names.filter((entry) => entry.endsWith(".json")).sort()) {
|
|
185
|
+
const loaded = await readBoundedJson(join(directory, name));
|
|
186
|
+
const document = schema.parse(loaded.value);
|
|
187
|
+
if (`${document.id}.json` !== name) {
|
|
188
|
+
throw new Error(`Knowledge document ${KNOWLEDGE_DIR}/${kind}/${name} must be named ${document.id}.json`);
|
|
189
|
+
}
|
|
190
|
+
scanned.push({
|
|
191
|
+
document,
|
|
192
|
+
reference: {
|
|
193
|
+
id: document.id,
|
|
194
|
+
path: expectedPath(kind, document.id),
|
|
195
|
+
sha256: sha256(loaded.bytes),
|
|
196
|
+
status: document.status
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return scanned;
|
|
201
|
+
}
|
|
202
|
+
async readDocuments(canonicalProject, kind, references, schema) {
|
|
203
|
+
const documents = [];
|
|
204
|
+
for (const reference of references) {
|
|
205
|
+
assertReferencePath(kind, reference.id, reference.path);
|
|
206
|
+
const path = resolve(canonicalProject, reference.path);
|
|
207
|
+
if (!isContained(canonicalProject, path)) {
|
|
208
|
+
throw new Error(`Knowledge reference escapes the project: ${reference.id}`);
|
|
209
|
+
}
|
|
210
|
+
const loaded = await readBoundedJson(path);
|
|
211
|
+
if (sha256(loaded.bytes) !== reference.sha256) {
|
|
212
|
+
throw new Error(`Knowledge document is stale: ${reference.path} (run taphound knowledge rehash)`);
|
|
213
|
+
}
|
|
214
|
+
const document = schema.parse(loaded.value);
|
|
215
|
+
if (document.id !== reference.id
|
|
216
|
+
|| document.status !== reference.status) {
|
|
217
|
+
throw new Error(`Knowledge reference identity mismatch: ${reference.path}`);
|
|
218
|
+
}
|
|
219
|
+
documents.push(document);
|
|
220
|
+
}
|
|
221
|
+
return documents;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { constants, lstat, mkdir, open, realpath, rename, unlink } from "node:fs/promises";
|
|
3
3
|
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { isErrnoException } from "../../shared/errors.js";
|
|
4
5
|
function sameIdentity(left, right) {
|
|
5
6
|
return left.dev === right.dev && left.ino === right.ino;
|
|
6
7
|
}
|
|
7
|
-
function isNodeError(error) {
|
|
8
|
-
return error instanceof Error && "code" in error;
|
|
9
|
-
}
|
|
10
8
|
async function captureDirectory(path) {
|
|
11
9
|
const stats = await lstat(path, { bigint: true });
|
|
12
10
|
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
@@ -31,7 +29,7 @@ async function captureOptionalFile(path) {
|
|
|
31
29
|
return await captureFile(path);
|
|
32
30
|
}
|
|
33
31
|
catch (error) {
|
|
34
|
-
if (
|
|
32
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
35
33
|
return null;
|
|
36
34
|
}
|
|
37
35
|
throw error;
|
|
@@ -129,7 +127,7 @@ async function prepareBoundOutput(input, createParents) {
|
|
|
129
127
|
}
|
|
130
128
|
catch (error) {
|
|
131
129
|
if (!createParents
|
|
132
|
-
|| !
|
|
130
|
+
|| !isErrnoException(error)
|
|
133
131
|
|| error.code !== "ENOENT") {
|
|
134
132
|
throw error;
|
|
135
133
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ProjectFileInspection, type ProjectFileInspector } from "../../ports/project-file-inspector.js";
|
|
2
2
|
export declare class NodeProjectFileInspector implements ProjectFileInspector {
|
|
3
|
+
private rootCache;
|
|
4
|
+
private readonly resolveRoot;
|
|
3
5
|
readonly inspectProjectFile: (input: {
|
|
4
6
|
projectRoot: string;
|
|
5
7
|
relativePath: string;
|
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { open, realpath, stat } from "node:fs/promises";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
&& error !== null
|
|
8
|
-
&& "code" in error
|
|
9
|
-
&& typeof error.code === "string")
|
|
10
|
-
? error.code
|
|
11
|
-
: undefined;
|
|
12
|
-
}
|
|
13
|
-
function isContained(root, path) {
|
|
14
|
-
const fromRoot = relative(root, path);
|
|
15
|
-
return (fromRoot === ""
|
|
16
|
-
|| (fromRoot !== ".."
|
|
17
|
-
&& !fromRoot.startsWith(`..${sep}`)
|
|
18
|
-
&& !isAbsolute(fromRoot)));
|
|
19
|
-
}
|
|
4
|
+
import { relative, resolve } from "node:path";
|
|
5
|
+
import { errnoCode } from "../../shared/errors.js";
|
|
6
|
+
import { isContained } from "../../shared/paths.js";
|
|
20
7
|
function sameIdentity(left, right) {
|
|
21
8
|
return (left.dev === right.dev
|
|
22
9
|
&& left.ino === right.ino
|
|
@@ -48,40 +35,78 @@ function normalizedRelativePath(root, path) {
|
|
|
48
35
|
return relative(root, path).replaceAll("\\", "/");
|
|
49
36
|
}
|
|
50
37
|
function evidenceFailure(error) {
|
|
51
|
-
return
|
|
38
|
+
return errnoCode(error) === "ENOENT" || errnoCode(error) === "ENOTDIR"
|
|
52
39
|
? { status: "notFound" }
|
|
53
40
|
: { status: "unreadable" };
|
|
54
41
|
}
|
|
55
42
|
export class NodeProjectFileInspector {
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
rootCache;
|
|
44
|
+
resolveRoot = async (projectRoot) => {
|
|
45
|
+
const cached = this.rootCache?.get(projectRoot);
|
|
46
|
+
if (cached !== undefined) {
|
|
47
|
+
try {
|
|
48
|
+
const stats = await stat(cached.canonical, { bigint: true });
|
|
49
|
+
if (stats.isDirectory()) {
|
|
50
|
+
const identity = {
|
|
51
|
+
dev: stats.dev,
|
|
52
|
+
ino: stats.ino,
|
|
53
|
+
size: stats.size,
|
|
54
|
+
mtimeNs: stats.mtimeNs,
|
|
55
|
+
ctimeNs: stats.ctimeNs
|
|
56
|
+
};
|
|
57
|
+
if (sameIdentity(cached.identity, identity)) {
|
|
58
|
+
return {
|
|
59
|
+
status: "resolved",
|
|
60
|
+
canonical: cached.canonical,
|
|
61
|
+
identity: cached.identity
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// cache stale, re-resolve below
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
let canonical;
|
|
58
71
|
try {
|
|
59
|
-
|
|
72
|
+
canonical = await realpath(projectRoot);
|
|
60
73
|
}
|
|
61
74
|
catch (error) {
|
|
62
|
-
return
|
|
75
|
+
return errnoCode(error) === "ENOENT" || errnoCode(error) === "ENOTDIR"
|
|
63
76
|
? { status: "rootNotFound" }
|
|
64
77
|
: { status: "rootUnreadable" };
|
|
65
78
|
}
|
|
66
|
-
let rootBefore;
|
|
67
79
|
try {
|
|
68
|
-
const rootStats = await stat(
|
|
80
|
+
const rootStats = await stat(canonical, { bigint: true });
|
|
69
81
|
if (!rootStats.isDirectory()) {
|
|
70
82
|
return { status: "rootNotDirectory" };
|
|
71
83
|
}
|
|
72
|
-
|
|
84
|
+
const identity = {
|
|
73
85
|
dev: rootStats.dev,
|
|
74
86
|
ino: rootStats.ino,
|
|
75
87
|
size: rootStats.size,
|
|
76
88
|
mtimeNs: rootStats.mtimeNs,
|
|
77
89
|
ctimeNs: rootStats.ctimeNs
|
|
78
90
|
};
|
|
91
|
+
if (this.rootCache === undefined) {
|
|
92
|
+
this.rootCache = new Map();
|
|
93
|
+
}
|
|
94
|
+
this.rootCache.set(projectRoot, { canonical, identity });
|
|
95
|
+
return { status: "resolved", canonical, identity };
|
|
79
96
|
}
|
|
80
97
|
catch (error) {
|
|
81
|
-
return
|
|
98
|
+
return errnoCode(error) === "ENOENT"
|
|
82
99
|
? { status: "rootNotFound" }
|
|
83
100
|
: { status: "rootUnreadable" };
|
|
84
101
|
}
|
|
102
|
+
};
|
|
103
|
+
inspectProjectFile = async (input) => {
|
|
104
|
+
const root = await this.resolveRoot(input.projectRoot);
|
|
105
|
+
if (root.status !== "resolved") {
|
|
106
|
+
return root;
|
|
107
|
+
}
|
|
108
|
+
const canonicalRoot = root.canonical;
|
|
109
|
+
const rootBefore = root.identity;
|
|
85
110
|
const normalizedPath = input.relativePath.replaceAll("\\", "/");
|
|
86
111
|
if (normalizedPath.startsWith("/")
|
|
87
112
|
|| /^[A-Za-z]:/.test(normalizedPath)
|
|
@@ -113,7 +138,7 @@ export class NodeProjectFileInspector {
|
|
|
113
138
|
handle = await open(canonicalBefore, openFlags);
|
|
114
139
|
}
|
|
115
140
|
catch (error) {
|
|
116
|
-
return
|
|
141
|
+
return errnoCode(error) === "ELOOP"
|
|
117
142
|
? { status: "changedIdentity", resolvedRelativePath }
|
|
118
143
|
: evidenceFailure(error);
|
|
119
144
|
}
|
|
@@ -142,6 +167,7 @@ export class NodeProjectFileInspector {
|
|
|
142
167
|
}
|
|
143
168
|
const hash = createHash("sha256");
|
|
144
169
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
170
|
+
const chunks = [];
|
|
145
171
|
let totalBytes = 0;
|
|
146
172
|
let tooLarge = false;
|
|
147
173
|
while (totalBytes <= input.maximumBytes) {
|
|
@@ -152,7 +178,9 @@ export class NodeProjectFileInspector {
|
|
|
152
178
|
break;
|
|
153
179
|
}
|
|
154
180
|
totalBytes += readResult.bytesRead;
|
|
155
|
-
|
|
181
|
+
const chunk = Buffer.from(buffer.subarray(0, readResult.bytesRead));
|
|
182
|
+
hash.update(chunk);
|
|
183
|
+
chunks.push(chunk);
|
|
156
184
|
if (totalBytes > input.maximumBytes) {
|
|
157
185
|
tooLarge = true;
|
|
158
186
|
break;
|
|
@@ -185,7 +213,8 @@ export class NodeProjectFileInspector {
|
|
|
185
213
|
: {
|
|
186
214
|
status: "inspected",
|
|
187
215
|
resolvedRelativePath,
|
|
188
|
-
sha256: hash.digest("hex")
|
|
216
|
+
sha256: hash.digest("hex"),
|
|
217
|
+
bytes: Buffer.concat(chunks)
|
|
189
218
|
};
|
|
190
219
|
}
|
|
191
220
|
catch {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProjectIdentityInspection, ProjectIdentityInspector } from "../../ports/project-identity-inspector.js";
|
|
2
|
+
export declare class AndroidProjectIdentityInspector implements ProjectIdentityInspector {
|
|
3
|
+
readonly inspectIdentity: (input: {
|
|
4
|
+
readonly projectRoot: string;
|
|
5
|
+
readonly moduleDir: string;
|
|
6
|
+
}) => Promise<ProjectIdentityInspection>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { errnoCode } from "../../shared/errors.js";
|
|
4
|
+
const PACKAGE_PATTERN = /package\s*=\s*"([^"]+)"/;
|
|
5
|
+
const MAIN_ACTION = "android.intent.action.MAIN";
|
|
6
|
+
const LAUNCHER_CATEGORY = "android.intent.category.LAUNCHER";
|
|
7
|
+
async function readBuildFile(moduleDir) {
|
|
8
|
+
for (const name of ["build.gradle.kts", "build.gradle"]) {
|
|
9
|
+
const candidate = resolve(moduleDir, name);
|
|
10
|
+
try {
|
|
11
|
+
const stats = await stat(candidate);
|
|
12
|
+
if (stats.isFile()) {
|
|
13
|
+
return await readFile(candidate, "utf8");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// continue
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
function escapeRegExp(value) {
|
|
23
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24
|
+
}
|
|
25
|
+
function parseApplicationIdLiteral(buildContent) {
|
|
26
|
+
const groovyMatch = buildContent.match(/applicationId\s+['"]([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+)['"]/);
|
|
27
|
+
if (groovyMatch !== null) {
|
|
28
|
+
return groovyMatch[1];
|
|
29
|
+
}
|
|
30
|
+
const kotlinMatch = buildContent.match(/applicationId\s*=\s*['"]([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+)['"]/);
|
|
31
|
+
if (kotlinMatch !== null) {
|
|
32
|
+
return kotlinMatch[1];
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function parseApplicationIdVariable(buildContent) {
|
|
37
|
+
const varMatch = buildContent.match(/applicationId\s*=\s*([A-Za-z_$][\w$]*)[ \t]*(?:\/\/.*)?$/m);
|
|
38
|
+
if (varMatch === null) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const variableName = varMatch[1];
|
|
42
|
+
if (variableName === undefined) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const propPattern = new RegExp(`val\\s+${escapeRegExp(variableName)}\\s*=\\s*providers\\.gradleProperty\\s*\\(\\s*["']([^"']+)["']\\s*\\)\\.get\\(\\)`);
|
|
46
|
+
const propMatch = buildContent.match(propPattern);
|
|
47
|
+
return propMatch?.[1];
|
|
48
|
+
}
|
|
49
|
+
async function readGradleProperty(projectRoot, propertyName) {
|
|
50
|
+
const propertiesPath = resolve(projectRoot, "gradle.properties");
|
|
51
|
+
let content;
|
|
52
|
+
try {
|
|
53
|
+
content = await readFile(propertiesPath, "utf8");
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
for (const line of content.split(/\r?\n/)) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (trimmed.startsWith("#") || trimmed.startsWith("!")) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const eqIndex = trimmed.indexOf("=");
|
|
64
|
+
if (eqIndex === -1) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
68
|
+
if (key === propertyName) {
|
|
69
|
+
return trimmed.slice(eqIndex + 1).trim();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
async function resolveApplicationId(buildContent, projectRoot) {
|
|
75
|
+
if (buildContent === undefined) {
|
|
76
|
+
return { applicationId: undefined, namespace: undefined };
|
|
77
|
+
}
|
|
78
|
+
const literal = parseApplicationIdLiteral(buildContent);
|
|
79
|
+
if (literal !== undefined) {
|
|
80
|
+
const namespace = parseNamespace(buildContent);
|
|
81
|
+
return { applicationId: literal, namespace };
|
|
82
|
+
}
|
|
83
|
+
const propertyName = parseApplicationIdVariable(buildContent);
|
|
84
|
+
if (propertyName !== undefined) {
|
|
85
|
+
const value = await readGradleProperty(projectRoot, propertyName);
|
|
86
|
+
if (value !== undefined) {
|
|
87
|
+
const namespace = parseNamespace(buildContent);
|
|
88
|
+
return { applicationId: value, namespace };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const namespace = parseNamespace(buildContent);
|
|
92
|
+
return { applicationId: namespace, namespace };
|
|
93
|
+
}
|
|
94
|
+
function parseNamespace(buildContent) {
|
|
95
|
+
const namespaceMatch = buildContent.match(/namespace\s*=?\s*['"]([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+)['"]/);
|
|
96
|
+
return namespaceMatch?.[1];
|
|
97
|
+
}
|
|
98
|
+
function parseManifest(manifestContent) {
|
|
99
|
+
const packageMatch = manifestContent.match(PACKAGE_PATTERN);
|
|
100
|
+
const packageName = packageMatch?.[1];
|
|
101
|
+
const activityBlocks = [];
|
|
102
|
+
const activityPattern = /<activity\b[^>]*>/g;
|
|
103
|
+
let activityMatch;
|
|
104
|
+
while ((activityMatch = activityPattern.exec(manifestContent)) !== null) {
|
|
105
|
+
const tag = activityMatch[0];
|
|
106
|
+
const nameMatch = tag.match(/android:name\s*=\s*"([^"]+)"/);
|
|
107
|
+
if (nameMatch === null) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const name = nameMatch[1];
|
|
111
|
+
if (name === undefined) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const filters = [];
|
|
115
|
+
if (!tag.endsWith("/>")) {
|
|
116
|
+
const filterStart = activityMatch.index + tag.length;
|
|
117
|
+
const closingIndex = manifestContent.indexOf("</activity>", filterStart);
|
|
118
|
+
const activityBody = closingIndex === -1
|
|
119
|
+
? manifestContent.slice(filterStart)
|
|
120
|
+
: manifestContent.slice(filterStart, closingIndex);
|
|
121
|
+
const filterPattern = /<intent-filter\b[\s\S]*?<\/intent-filter>/g;
|
|
122
|
+
let filterMatch;
|
|
123
|
+
while ((filterMatch = filterPattern.exec(activityBody)) !== null) {
|
|
124
|
+
filters.push(filterMatch[0]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
activityBlocks.push({ name, filters });
|
|
128
|
+
}
|
|
129
|
+
let launchActivity;
|
|
130
|
+
for (const block of activityBlocks) {
|
|
131
|
+
for (const filter of block.filters) {
|
|
132
|
+
if (filter.includes(MAIN_ACTION) && filter.includes(LAUNCHER_CATEGORY)) {
|
|
133
|
+
launchActivity = block.name;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (launchActivity !== undefined) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { packageName, launchActivity };
|
|
142
|
+
}
|
|
143
|
+
function resolveActivity(packageName, activity) {
|
|
144
|
+
if (activity.startsWith(".")) {
|
|
145
|
+
return `${packageName}${activity}`;
|
|
146
|
+
}
|
|
147
|
+
if (!activity.includes(".")) {
|
|
148
|
+
return `${packageName}.${activity}`;
|
|
149
|
+
}
|
|
150
|
+
return activity;
|
|
151
|
+
}
|
|
152
|
+
export class AndroidProjectIdentityInspector {
|
|
153
|
+
inspectIdentity = async (input) => {
|
|
154
|
+
let root;
|
|
155
|
+
try {
|
|
156
|
+
root = await realpath(input.projectRoot);
|
|
157
|
+
const rootStats = await stat(root);
|
|
158
|
+
if (!rootStats.isDirectory()) {
|
|
159
|
+
return { status: "rootNotDirectory" };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return errnoCode(error) === "ENOENT"
|
|
164
|
+
? { status: "rootNotFound" }
|
|
165
|
+
: { status: "identityNotFound", message: "Project root is not accessible" };
|
|
166
|
+
}
|
|
167
|
+
const modulePath = resolve(root, input.moduleDir);
|
|
168
|
+
let moduleCanonical;
|
|
169
|
+
try {
|
|
170
|
+
moduleCanonical = await realpath(modulePath);
|
|
171
|
+
const moduleStats = await stat(moduleCanonical);
|
|
172
|
+
if (!moduleStats.isDirectory()) {
|
|
173
|
+
return { status: "moduleNotFound" };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return errnoCode(error) === "ENOENT"
|
|
178
|
+
? { status: "moduleNotFound" }
|
|
179
|
+
: { status: "identityNotFound", message: "Module directory is not accessible" };
|
|
180
|
+
}
|
|
181
|
+
const buildContent = await readBuildFile(moduleCanonical);
|
|
182
|
+
const { applicationId, namespace } = await resolveApplicationId(buildContent, root);
|
|
183
|
+
const manifestPath = resolve(moduleCanonical, "src", "main", "AndroidManifest.xml");
|
|
184
|
+
let manifestContent;
|
|
185
|
+
try {
|
|
186
|
+
manifestContent = await readFile(manifestPath, "utf8");
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (errnoCode(error) === "ENOENT") {
|
|
190
|
+
return { status: "manifestNotFound" };
|
|
191
|
+
}
|
|
192
|
+
return { status: "manifestUnreadable" };
|
|
193
|
+
}
|
|
194
|
+
const { packageName: manifestPackage, launchActivity } = parseManifest(manifestContent);
|
|
195
|
+
const packageName = applicationId ?? manifestPackage;
|
|
196
|
+
if (packageName === undefined) {
|
|
197
|
+
return {
|
|
198
|
+
status: "identityNotFound",
|
|
199
|
+
message: "Unable to resolve applicationId or package from build file or manifest"
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (launchActivity === undefined) {
|
|
203
|
+
return {
|
|
204
|
+
status: "identityNotFound",
|
|
205
|
+
message: "Unable to resolve launch activity from manifest"
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const activityBase = namespace ?? manifestPackage ?? packageName;
|
|
209
|
+
return {
|
|
210
|
+
status: "inspected",
|
|
211
|
+
packageName,
|
|
212
|
+
launchActivity: resolveActivity(activityBase, launchActivity)
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProjectInventoryCategory, ProjectInventoryInspection, ProjectInventoryInspector } from "../../ports/project-inventory-inspector.js";
|
|
2
|
+
export declare class NodeProjectInventoryInspector implements ProjectInventoryInspector {
|
|
3
|
+
readonly inspectProjectInventory: (input: {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
projectDir: string;
|
|
6
|
+
categories: ProjectInventoryCategory[];
|
|
7
|
+
}) => Promise<ProjectInventoryInspection>;
|
|
8
|
+
}
|