@adhdev/daemon-core 0.9.82-rc.43 → 0.9.82-rc.430
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/dist/boot/daemon-lifecycle.d.ts +9 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/build-info.d.ts +37 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +68 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +200 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +126 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
- package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/cli-adapters/resolve-executable.d.ts +69 -0
- package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
- package/dist/cli-adapters/terminal-screen.d.ts +4 -11
- package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
- package/dist/commands/chat-commands-read.d.ts +7 -0
- package/dist/commands/chat-commands-scope.d.ts +39 -0
- package/dist/commands/chat-commands-shared.d.ts +33 -0
- package/dist/commands/chat-commands-write.d.ts +14 -0
- package/dist/commands/chat-commands.d.ts +10 -23
- package/dist/commands/cli-manager.d.ts +15 -1
- package/dist/commands/handler.d.ts +125 -0
- package/dist/commands/high-family/index.d.ts +3 -0
- package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
- package/dist/commands/high-family/mesh-events.d.ts +2 -0
- package/dist/commands/high-family/mesh-status.d.ts +2 -0
- package/dist/commands/high-family/types.d.ts +60 -0
- package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
- package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
- package/dist/commands/low-family/diagnostics.d.ts +2 -0
- package/dist/commands/low-family/index.d.ts +3 -0
- package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
- package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
- package/dist/commands/low-family/notification.d.ts +2 -0
- package/dist/commands/low-family/refine-config.d.ts +2 -0
- package/dist/commands/low-family/session-host.d.ts +2 -0
- package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
- package/dist/commands/low-family/status-meta.d.ts +2 -0
- package/dist/commands/low-family/types.d.ts +33 -0
- package/dist/commands/med-family/cli-agent.d.ts +2 -0
- package/dist/commands/med-family/fast-forward.d.ts +2 -0
- package/dist/commands/med-family/ide.d.ts +10 -0
- package/dist/commands/med-family/index.d.ts +3 -0
- package/dist/commands/med-family/mesh-crud.d.ts +29 -0
- package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
- package/dist/commands/med-family/mesh-queue.d.ts +2 -0
- package/dist/commands/med-family/mesh-restart.d.ts +2 -0
- package/dist/commands/med-family/types.d.ts +142 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +276 -2
- package/dist/commands/upgrade-helper.d.ts +41 -1
- package/dist/config/chat-history.d.ts +9 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +100 -1
- package/dist/config/mesh-json-config.d.ts +199 -0
- package/dist/config/repo-settings.d.ts +77 -0
- package/dist/daemon/dev-server.d.ts +0 -2
- package/dist/detection/ide-detector.d.ts +13 -0
- package/dist/detection/win32-ide-version.d.ts +37 -0
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-commands.d.ts +11 -1
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +57 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/git-worktree.d.ts +71 -1
- package/dist/git/index.d.ts +3 -1
- package/dist/index.d.ts +64 -14
- package/dist/index.js +49391 -19228
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48574 -18570
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/logging/log-redactor.d.ts +24 -0
- package/dist/logging/log-tail-reader.d.ts +81 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +89 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +174 -0
- package/dist/mesh/mesh-clone-grace.d.ts +8 -0
- package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
- package/dist/mesh/mesh-event-classify.d.ts +5 -0
- package/dist/mesh/mesh-event-forwarding.d.ts +41 -0
- package/dist/mesh/mesh-event-trace.d.ts +21 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
- package/dist/mesh/mesh-events-pending.d.ts +69 -0
- package/dist/mesh/mesh-events-stale.d.ts +61 -0
- package/dist/mesh/mesh-events-utils.d.ts +75 -0
- package/dist/mesh/mesh-events.d.ts +6 -49
- package/dist/mesh/mesh-fast-forward.d.ts +81 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-init.d.ts +86 -0
- package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
- package/dist/mesh/mesh-ledger.d.ts +77 -1
- package/dist/mesh/mesh-magi-status.d.ts +63 -0
- package/dist/mesh/mesh-missions.d.ts +189 -0
- package/dist/mesh/mesh-node-identity.d.ts +289 -0
- package/dist/mesh/mesh-queue-assignment.d.ts +113 -0
- package/dist/mesh/mesh-reconcile-loop.d.ts +65 -0
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-gates.d.ts +428 -0
- package/dist/mesh/mesh-refine-status.d.ts +62 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-routing.d.ts +70 -0
- package/dist/mesh/mesh-runtime-store.d.ts +450 -0
- package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
- package/dist/mesh/mesh-task-inflight.d.ts +46 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
- package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
- package/dist/mesh/mesh-work-queue.d.ts +290 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +216 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
- package/dist/providers/acp-provider-instance.d.ts +5 -0
- package/dist/providers/approval-utils.d.ts +20 -0
- package/dist/providers/chat-message-normalization.d.ts +31 -1
- package/dist/providers/cli-provider-instance.d.ts +221 -3
- package/dist/providers/contracts.d.ts +139 -6
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/manual-attendance.d.ts +63 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/constants.d.ts +12 -0
- package/dist/providers/native-history/dispatcher.d.ts +27 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +29 -0
- package/dist/providers/provider-instance.d.ts +23 -1
- package/dist/providers/provider-loader.d.ts +26 -4
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +91 -0
- package/dist/providers/spec/cli-adapter.d.ts +223 -0
- package/dist/providers/spec/evaluator.d.ts +45 -0
- package/dist/providers/spec/fsm-driver.d.ts +422 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +76 -0
- package/dist/providers/spec/fsm-loader.d.ts +14 -0
- package/dist/providers/spec/fsm-types.d.ts +174 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +222 -0
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +84 -0
- package/dist/providers/working-dir.d.ts +17 -0
- package/dist/repo-mesh-types.d.ts +501 -13
- package/dist/runtime-defaults.d.ts +2 -0
- package/dist/sessions/registry.d.ts +6 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +59 -2
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +26 -0
- package/dist/system/hash.d.ts +8 -0
- package/dist/system/load-better-sqlite3.d.ts +21 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -3
- package/src/agent-stream/poller.ts +2 -3
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/boot/daemon-lifecycle.ts +63 -12
- package/src/boot/process-hardening.ts +89 -0
- package/src/build-info.ts +73 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +20 -1
- package/src/cli-adapter-types.d.ts +3 -1
- package/src/cli-adapter-types.ts +68 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1246 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +914 -1144
- package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
- package/src/cli-adapters/provider-cli-parse.ts +19 -0
- package/src/cli-adapters/provider-cli-runtime.ts +56 -7
- package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/src/cli-adapters/provider-cli-shared.ts +121 -11
- package/src/cli-adapters/pty-transport.ts +2 -1
- package/src/cli-adapters/pty-write-chunking.ts +106 -0
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/resolve-executable.ts +204 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
- package/src/cli-adapters/terminal-backends/types.ts +10 -3
- package/src/cli-adapters/terminal-screen.ts +16 -81
- package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
- package/src/commands/chat-commands-debug-bundle.ts +398 -0
- package/src/commands/chat-commands-read.ts +2327 -0
- package/src/commands/chat-commands-scope.ts +54 -0
- package/src/commands/chat-commands-shared.ts +114 -0
- package/src/commands/chat-commands-write.ts +891 -0
- package/src/commands/chat-commands.ts +19 -1841
- package/src/commands/cli-manager.ts +544 -26
- package/src/commands/handler.ts +841 -2
- package/src/commands/high-family/index.ts +28 -0
- package/src/commands/high-family/mesh-coordinator-launch.ts +678 -0
- package/src/commands/high-family/mesh-events.ts +80 -0
- package/src/commands/high-family/mesh-status.ts +710 -0
- package/src/commands/high-family/types.ts +76 -0
- package/src/commands/low-family/coordinator-prompt.ts +72 -0
- package/src/commands/low-family/daemon-lifecycle.ts +107 -0
- package/src/commands/low-family/diagnostics.ts +57 -0
- package/src/commands/low-family/index.ts +37 -0
- package/src/commands/low-family/mesh-ledger.ts +62 -0
- package/src/commands/low-family/mesh-node-logs.ts +87 -0
- package/src/commands/low-family/notification.ts +116 -0
- package/src/commands/low-family/refine-config.ts +106 -0
- package/src/commands/low-family/session-host.ts +274 -0
- package/src/commands/low-family/spec-providerdev.ts +217 -0
- package/src/commands/low-family/status-meta.ts +112 -0
- package/src/commands/low-family/types.ts +39 -0
- package/src/commands/med-family/cli-agent.ts +238 -0
- package/src/commands/med-family/fast-forward.ts +230 -0
- package/src/commands/med-family/ide.ts +163 -0
- package/src/commands/med-family/index.ts +37 -0
- package/src/commands/med-family/mesh-crud.ts +1066 -0
- package/src/commands/med-family/mesh-host-pairing.ts +234 -0
- package/src/commands/med-family/mesh-queue.ts +167 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/commands/med-family/types.ts +143 -0
- package/src/commands/mesh-coordinator.ts +334 -124
- package/src/commands/router.ts +2711 -3254
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +310 -45
- package/src/config/chat-history.ts +483 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +495 -22
- package/src/config/mesh-json-config.ts +376 -0
- package/src/config/recent-activity.ts +8 -2
- package/src/config/repo-settings.ts +111 -0
- package/src/daemon/dev-auto-implement.ts +3 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/daemon/dev-server.ts +0 -541
- package/src/detection/cli-detector.ts +28 -9
- package/src/detection/ide-detector.ts +55 -16
- package/src/detection/win32-ide-version.ts +106 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-commands.ts +59 -15
- package/src/git/git-diff.ts +81 -11
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +767 -48
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +261 -4
- package/src/git/index.ts +17 -0
- package/src/index.ts +194 -12
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/command-log.ts +7 -5
- package/src/logging/log-redactor.ts +100 -0
- package/src/logging/log-tail-reader.ts +341 -0
- package/src/logging/logger.ts +14 -7
- package/src/mesh/contracts.ts +338 -0
- package/src/mesh/coordinator-prompt.ts +381 -33
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +645 -0
- package/src/mesh/mesh-clone-grace.ts +68 -0
- package/src/mesh/mesh-coordinator-config.ts +97 -0
- package/src/mesh/mesh-delivery-policy.ts +315 -0
- package/src/mesh/mesh-event-classify.ts +51 -0
- package/src/mesh/mesh-event-forwarding.ts +1888 -0
- package/src/mesh/mesh-event-trace.ts +67 -0
- package/src/mesh/mesh-events-coordinator.ts +32 -0
- package/src/mesh/mesh-events-pending.ts +656 -0
- package/src/mesh/mesh-events-stale.ts +389 -0
- package/src/mesh/mesh-events-utils.ts +443 -0
- package/src/mesh/mesh-events.ts +33 -1035
- package/src/mesh/mesh-fast-forward.ts +843 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-init.ts +260 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
- package/src/mesh/mesh-ledger.ts +656 -102
- package/src/mesh/mesh-magi-status.ts +223 -0
- package/src/mesh/mesh-missions.ts +449 -0
- package/src/mesh/mesh-node-identity.ts +1887 -0
- package/src/mesh/mesh-queue-assignment.ts +1918 -0
- package/src/mesh/mesh-reconcile-loop.ts +1780 -0
- package/src/mesh/mesh-refine-batch.ts +205 -0
- package/src/mesh/mesh-refine-gates.ts +1673 -0
- package/src/mesh/mesh-refine-status.ts +231 -0
- package/src/mesh/mesh-review-inbox.ts +307 -0
- package/src/mesh/mesh-routing.ts +291 -0
- package/src/mesh/mesh-runtime-store.ts +1853 -0
- package/src/mesh/mesh-scheduling-runtime.ts +198 -0
- package/src/mesh/mesh-task-inflight.ts +70 -0
- package/src/mesh/mesh-task-stats.ts +161 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
- package/src/mesh/mesh-warmup-deadline.ts +152 -0
- package/src/mesh/mesh-work-queue.ts +1243 -141
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +423 -0
- package/src/mesh/worktree-bootstrap-config.ts +450 -0
- package/src/providers/acp-provider-instance.ts +43 -10
- package/src/providers/approval-utils.d.ts +5 -0
- package/src/providers/approval-utils.ts +57 -5
- package/src/providers/chat-message-normalization.ts +92 -4
- package/src/providers/cli-provider-instance.ts +1933 -104
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +150 -6
- package/src/providers/extension-provider-instance.ts +12 -7
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +35 -12
- package/src/providers/manual-attendance.ts +85 -0
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +479 -0
- package/src/providers/native-history/constants.ts +19 -0
- package/src/providers/native-history/dispatcher.ts +339 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +71 -0
- package/src/providers/provider-instance.ts +19 -1
- package/src/providers/provider-loader.ts +668 -50
- package/src/providers/provider-schema.ts +87 -14
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +229 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +235 -0
- package/src/providers/spec/cli-adapter.ts +1056 -0
- package/src/providers/spec/evaluator.ts +407 -0
- package/src/providers/spec/fsm-driver.ts +1410 -0
- package/src/providers/spec/fsm-evaluator.ts +272 -0
- package/src/providers/spec/fsm-loader.ts +120 -0
- package/src/providers/spec/fsm-types.ts +273 -0
- package/src/providers/spec/native-history-executor.ts +1128 -0
- package/src/providers/spec/pre-launch-trust.ts +104 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +262 -0
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +536 -0
- package/src/providers/version-archive.ts +64 -24
- package/src/providers/working-dir.ts +23 -0
- package/src/repo-mesh-types.ts +740 -14
- package/src/runtime-defaults.ts +39 -0
- package/src/sessions/registry.ts +6 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +64 -1
- package/src/status/builders.ts +26 -6
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +19 -1
- package/src/status/snapshot.ts +95 -26
- package/src/system/hash.ts +23 -0
- package/src/system/host-memory.ts +29 -12
- package/src/system/load-better-sqlite3.ts +68 -0
- package/src/types.ts +5 -0
- package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -18,10 +18,12 @@ import * as path from 'path';
|
|
|
18
18
|
import * as os from 'os';
|
|
19
19
|
import * as chokidar from 'chokidar';
|
|
20
20
|
import { registerIDEDefinition } from '../detection/ide-detector.js';
|
|
21
|
+
import { sha256Hex } from '../system/hash.js';
|
|
21
22
|
import { LOG } from '../logging/logger.js';
|
|
22
23
|
import { VersionArchive } from './version-archive.js';
|
|
23
24
|
import type {
|
|
24
25
|
ProviderCompatibilityEntry,
|
|
26
|
+
ProviderControlDef,
|
|
25
27
|
ProviderModule,
|
|
26
28
|
ProviderCategory,
|
|
27
29
|
ProviderScripts,
|
|
@@ -30,8 +32,34 @@ import type {
|
|
|
30
32
|
ResolvedProvider,
|
|
31
33
|
} from './contracts.js';
|
|
32
34
|
import { validateProviderDefinition } from './provider-schema.js';
|
|
35
|
+
import {
|
|
36
|
+
loadProvidersActive,
|
|
37
|
+
resolveActiveSource,
|
|
38
|
+
} from './external-sources.js';
|
|
33
39
|
import type { ProviderSourceMode } from '../config/config.js';
|
|
34
40
|
import type { ProviderSourceConfigSnapshot, ProviderUserDirSource } from '../config/provider-source-config.js';
|
|
41
|
+
import { executeNativeHistory } from './spec/native-history-executor.js';
|
|
42
|
+
import { createNativeHistoryDispatcher, type ReaderId } from './native-history/dispatcher.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adds a provider-script root to the require whitelist. Wrapped in a
|
|
46
|
+
* try/catch + null check so a loader hot-path can't crash on a path
|
|
47
|
+
* that doesn't exist yet or one the whitelist hook rejects.
|
|
48
|
+
*
|
|
49
|
+
* The require-whitelist module is loaded lazily on first call. Eagerly
|
|
50
|
+
* top-level importing it pulls `node:fs.realpathSync.native` into
|
|
51
|
+
* module evaluation, which breaks unit tests that partially mock `fs`
|
|
52
|
+
* (e.g. test/commands/get-logs-incremental.test.ts mocks only
|
|
53
|
+
* existsSync + readFileSync). Lazy load keeps that mock surface valid.
|
|
54
|
+
*/
|
|
55
|
+
function registerProviderScriptRootSafely(root: string | null | undefined): void {
|
|
56
|
+
if (!root || typeof root !== 'string') return;
|
|
57
|
+
try {
|
|
58
|
+
const { registerProviderScriptRoot } =
|
|
59
|
+
require('./sdk/v1/sandbox/require-whitelist.js') as typeof import('./sdk/v1/sandbox/require-whitelist.js');
|
|
60
|
+
registerProviderScriptRoot(root);
|
|
61
|
+
} catch { /* boot-time only — swallow */ }
|
|
62
|
+
}
|
|
35
63
|
|
|
36
64
|
interface ProviderAvailabilityState {
|
|
37
65
|
installed: boolean;
|
|
@@ -61,6 +89,78 @@ export interface MachineProviderConfig {
|
|
|
61
89
|
lastVerification?: MachineProviderCheckResult;
|
|
62
90
|
}
|
|
63
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Translate a spec `control_bar` array into the web-facing
|
|
94
|
+
* `ProviderControlDef[]` shape the dashboard renders.
|
|
95
|
+
*
|
|
96
|
+
* The two shapes are distinct: `control_bar` entries are daemon-side
|
|
97
|
+
* `{ id, label, visible_when_state, action }` records driving
|
|
98
|
+
* SpecCliAdapter.invokeScript, while the dashboard's chat bar reads
|
|
99
|
+
* `ProviderControlDef` (`{ id, type, label, placement, ... }`). Spec
|
|
100
|
+
* providers (claude-cli / codex-cli) historically declared *only*
|
|
101
|
+
* `control_bar`, so the dashboard saw no controls at all — the Model / Mode
|
|
102
|
+
* pickers never rendered. This bridges that gap without changing how the
|
|
103
|
+
* controls actually dispatch.
|
|
104
|
+
*
|
|
105
|
+
* Script-name contract: the dashboard sends the control's
|
|
106
|
+
* `listScript` / `setScript` / `invokeScript` name through
|
|
107
|
+
* `invoke_provider_script`, which gates on `provider.scripts[<name>]` and then
|
|
108
|
+
* routes to `SpecCliAdapter.invokeScript(<name>)` — which matches the name
|
|
109
|
+
* against `control_bar[].id`. So every synthesized script name MUST equal the
|
|
110
|
+
* control id (the loader stubs `provider.scripts[id]` from the same source).
|
|
111
|
+
*
|
|
112
|
+
* Mapping:
|
|
113
|
+
* open_picker → select (dynamic): list + set both keyed on the control id;
|
|
114
|
+
* the adapter distinguishes LIST vs SELECT by the presence of
|
|
115
|
+
* a choice arg, so one id serves both roles.
|
|
116
|
+
* send_keys → action: one-shot keystroke (stop, cycle_mode).
|
|
117
|
+
* attach_image → skipped: it needs an image blob from a file picker, not a
|
|
118
|
+
* bare bar button; surfacing it as an `action` would only
|
|
119
|
+
* produce a button that errors with "requires args.blob".
|
|
120
|
+
*/
|
|
121
|
+
function synthesizeControlsFromControlBar(specControls: any[]): ProviderControlDef[] {
|
|
122
|
+
const out: ProviderControlDef[] = [];
|
|
123
|
+
specControls.forEach((ctl, index) => {
|
|
124
|
+
const id = typeof ctl?.id === 'string' ? ctl.id.trim() : '';
|
|
125
|
+
const actionType = ctl?.action?.type;
|
|
126
|
+
if (!id || !actionType) return;
|
|
127
|
+
const label = typeof ctl?.label === 'string' && ctl.label.trim() ? ctl.label : id;
|
|
128
|
+
// Preserve the spec's state gating so the web bar can mirror the daemon's
|
|
129
|
+
// FsmDriver.handleClickControl enforcement (otherwise the button renders in
|
|
130
|
+
// states where the daemon would silently drop the click).
|
|
131
|
+
const visibleWhenState = Array.isArray(ctl?.visible_when_state)
|
|
132
|
+
? ctl.visible_when_state.filter((s: unknown): s is string => typeof s === 'string')
|
|
133
|
+
: undefined;
|
|
134
|
+
if (actionType === 'open_picker') {
|
|
135
|
+
out.push({
|
|
136
|
+
id,
|
|
137
|
+
type: 'select',
|
|
138
|
+
label,
|
|
139
|
+
placement: 'bar',
|
|
140
|
+
dynamic: true,
|
|
141
|
+
listScript: id,
|
|
142
|
+
setScript: id,
|
|
143
|
+
readFrom: id,
|
|
144
|
+
order: index,
|
|
145
|
+
...(visibleWhenState && visibleWhenState.length > 0 ? { visibleWhenState } : {}),
|
|
146
|
+
});
|
|
147
|
+
} else if (actionType === 'send_keys') {
|
|
148
|
+
out.push({
|
|
149
|
+
id,
|
|
150
|
+
type: 'action',
|
|
151
|
+
label,
|
|
152
|
+
placement: 'bar',
|
|
153
|
+
invokeScript: id,
|
|
154
|
+
resultDisplay: 'none',
|
|
155
|
+
order: index,
|
|
156
|
+
...(visibleWhenState && visibleWhenState.length > 0 ? { visibleWhenState } : {}),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// attach_image intentionally skipped — see fn doc.
|
|
160
|
+
});
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
64
164
|
type CliDetectionEntry = {
|
|
65
165
|
id: string;
|
|
66
166
|
displayName: string;
|
|
@@ -92,7 +192,9 @@ export class ProviderLoader {
|
|
|
92
192
|
}
|
|
93
193
|
|
|
94
194
|
private static readonly GITHUB_TARBALL_URL = 'https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz';
|
|
195
|
+
private static readonly REGISTRY_BASE_URL = 'https://api.adhf.dev/api/v1/registry';
|
|
95
196
|
private static readonly META_FILE = '.meta.json';
|
|
197
|
+
private static readonly REGISTRY_META_FILE = '.registry-meta.json';
|
|
96
198
|
private static readonly REPO_PROVIDER_DIRNAME = 'adhdev-providers';
|
|
97
199
|
private static readonly SIBLING_MARKER_FILE = '.adhdev-provider-root';
|
|
98
200
|
private static readonly SIBLING_ENV_VAR = 'ADHDEV_USE_SIBLING_PROVIDERS';
|
|
@@ -193,6 +295,33 @@ export class ProviderLoader {
|
|
|
193
295
|
sourceMode: options?.sourceMode,
|
|
194
296
|
disableUpstream: options?.disableUpstream,
|
|
195
297
|
});
|
|
298
|
+
|
|
299
|
+
// One-time migration: ~/.adhdev/marketplace → ~/.adhdev/external.
|
|
300
|
+
// The directory was renamed when the "marketplace" install model was
|
|
301
|
+
// dropped in favour of explicit external git sources. Best-effort:
|
|
302
|
+
// if the rename fails we leave both dirs in place and log so the user
|
|
303
|
+
// can investigate.
|
|
304
|
+
this.migrateMarketplaceDirToExternal();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private migrateMarketplaceDirToExternal(): void {
|
|
308
|
+
try {
|
|
309
|
+
const home = os.homedir();
|
|
310
|
+
const oldDir = path.join(home, '.adhdev', 'marketplace');
|
|
311
|
+
const newDir = path.join(home, '.adhdev', 'external');
|
|
312
|
+
if (!fs.existsSync(oldDir)) return;
|
|
313
|
+
if (fs.existsSync(newDir)) {
|
|
314
|
+
// Both exist — don't merge. Leave old in place; surface in logs so
|
|
315
|
+
// the user can decide what to keep. Loader still loads from
|
|
316
|
+
// external/ only, so old marketplace/ becomes inert.
|
|
317
|
+
this.log(`Migration skipped: both ~/.adhdev/marketplace and ~/.adhdev/external exist (marketplace dir is now inert and can be removed manually).`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
fs.renameSync(oldDir, newDir);
|
|
321
|
+
this.log(`Migrated ~/.adhdev/marketplace → ~/.adhdev/external (one-time rename after provider source-layer cleanup).`);
|
|
322
|
+
} catch (e: any) {
|
|
323
|
+
this.log(`Marketplace→external migration failed: ${e?.message || e}`);
|
|
324
|
+
}
|
|
196
325
|
}
|
|
197
326
|
|
|
198
327
|
private log(msg: string): void {
|
|
@@ -224,7 +353,12 @@ export class ProviderLoader {
|
|
|
224
353
|
* Highest-priority editable overrides come first.
|
|
225
354
|
*/
|
|
226
355
|
getProviderRoots(): string[] {
|
|
227
|
-
|
|
356
|
+
// Order matters: user customs > external (3rd-party sources) > upstream
|
|
357
|
+
// (official auto-sync). findProviderDirInternal walks this list in order
|
|
358
|
+
// to locate the provider dir containing the scripts/, so external must
|
|
359
|
+
// be included here even though loadAll() also reads it directly.
|
|
360
|
+
const externalDir = path.join(os.homedir(), '.adhdev', 'external');
|
|
361
|
+
return [this.userDir, externalDir, this.upstreamDir];
|
|
228
362
|
}
|
|
229
363
|
|
|
230
364
|
getSourceConfig(): ProviderSourceConfigSnapshot {
|
|
@@ -316,16 +450,19 @@ export class ProviderLoader {
|
|
|
316
450
|
|
|
317
451
|
/**
|
|
318
452
|
* Load all providers (3-tier priority)
|
|
319
|
-
* 1.
|
|
320
|
-
* 2.
|
|
321
|
-
*
|
|
453
|
+
* 1. ~/.adhdev/providers/.upstream/ — official git, auto-synced
|
|
454
|
+
* 2. ~/.adhdev/external/ — 3rd-party git sources, user-added,
|
|
455
|
+
* bundled providers may include arbitrary JS (untrusted by default)
|
|
456
|
+
* 3. ~/.adhdev/providers/ (excluding .upstream) — user-authored customs,
|
|
457
|
+
* always wins
|
|
458
|
+
* Highest priority listed last (overwrites earlier loads).
|
|
322
459
|
* If .upstream/ is empty, call fetchLatest() before loadAll().
|
|
323
460
|
*/
|
|
324
461
|
loadAll(): void {
|
|
325
462
|
this.providers.clear();
|
|
326
463
|
this.providerAvailability.clear();
|
|
327
464
|
|
|
328
|
-
// 1. Load upstream (GitHub auto-download — primary source)
|
|
465
|
+
// 1. Load upstream (GitHub auto-download — primary official source)
|
|
329
466
|
let upstreamCount = 0;
|
|
330
467
|
if (!this.disableUpstream && fs.existsSync(this.upstreamDir)) {
|
|
331
468
|
upstreamCount = this.loadDir(this.upstreamDir);
|
|
@@ -336,7 +473,90 @@ export class ProviderLoader {
|
|
|
336
473
|
this.log('Upstream loading disabled (sourceMode=no-upstream)');
|
|
337
474
|
}
|
|
338
475
|
|
|
339
|
-
// 2. Load
|
|
476
|
+
// 2. Load external providers from ~/.adhdev/external/<source-name>/
|
|
477
|
+
// (3rd-party git sources). Overrides upstream but is itself overridden
|
|
478
|
+
// by user customs in step 3.
|
|
479
|
+
//
|
|
480
|
+
// Each registered source is a separate subdirectory so two sources can
|
|
481
|
+
// both expose the same provider type without overwriting each other.
|
|
482
|
+
// When more than one source provides the same type, providers-active.json
|
|
483
|
+
// chooses the active one; without an explicit choice we deterministically
|
|
484
|
+
// pick the first in disk-walk order and log the ambiguity so the user
|
|
485
|
+
// can resolve it from the dashboard.
|
|
486
|
+
//
|
|
487
|
+
// Any non-spec manifest (tui block / overrides / scriptDir) coming from
|
|
488
|
+
// an external source runs JavaScript the daemon hasn't audited, so
|
|
489
|
+
// dashboards must surface an "untrusted source" badge before letting
|
|
490
|
+
// the user enable them.
|
|
491
|
+
const externalDir = path.join(os.homedir(), '.adhdev', 'external');
|
|
492
|
+
if (fs.existsSync(externalDir)) {
|
|
493
|
+
// Legacy layout (pre-source-namespace): manifests sit directly at
|
|
494
|
+
// external/<category>/<type>/. Detect by presence of category dirs at
|
|
495
|
+
// the root and migrate inline by treating the whole tree as a single
|
|
496
|
+
// implicit source. Loader behavior unchanged for legacy callers.
|
|
497
|
+
const rootEntries = (() => {
|
|
498
|
+
try { return fs.readdirSync(externalDir, { withFileTypes: true }); }
|
|
499
|
+
catch { return [] as fs.Dirent[]; }
|
|
500
|
+
})();
|
|
501
|
+
const KNOWN_CATEGORIES = new Set(['cli', 'ide', 'extension', 'acp']);
|
|
502
|
+
const looksLegacy = rootEntries.some(e => e.isDirectory() && KNOWN_CATEGORIES.has(e.name));
|
|
503
|
+
if (looksLegacy) {
|
|
504
|
+
// Tree shape predates per-source dirs — treat the whole thing as a
|
|
505
|
+
// single anonymous source so existing installs keep working until
|
|
506
|
+
// they're migrated to a real source registration.
|
|
507
|
+
const externalCount = this.loadDir(externalDir);
|
|
508
|
+
if (externalCount > 0) {
|
|
509
|
+
this.log(`Loaded ${externalCount} external providers (legacy unnamed source)`);
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
// New layout: external/<source-name>/<category>/<type>/…
|
|
513
|
+
const activeFile = loadProvidersActive();
|
|
514
|
+
let totalLoaded = 0;
|
|
515
|
+
const ambiguousTypes: { type: string; chosen: string; candidates: string[] }[] = [];
|
|
516
|
+
// Per-source load, then filter by active-selection: for each type
|
|
517
|
+
// present in more than one source, only the active source's copy
|
|
518
|
+
// is left in this.providers.
|
|
519
|
+
for (const sourceEntry of rootEntries) {
|
|
520
|
+
if (!sourceEntry.isDirectory()) continue;
|
|
521
|
+
const sourceDir = path.join(externalDir, sourceEntry.name);
|
|
522
|
+
const sourceLoaded = this.loadDir(sourceDir);
|
|
523
|
+
if (sourceLoaded > 0) {
|
|
524
|
+
totalLoaded += sourceLoaded;
|
|
525
|
+
this.log(`Loaded ${sourceLoaded} providers from external source "${sourceEntry.name}"`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// Resolve ambiguities — when the same type came from multiple
|
|
529
|
+
// sources, the last load wins by default. Replay with the active
|
|
530
|
+
// selection so the user-chosen source ends up winning.
|
|
531
|
+
for (const [type] of this.providers) {
|
|
532
|
+
const prov = this.providers.get(type);
|
|
533
|
+
if (!prov) continue;
|
|
534
|
+
const resolved = resolveActiveSource(prov.category, type, activeFile);
|
|
535
|
+
if (resolved.candidates.length <= 1) continue;
|
|
536
|
+
if (resolved.ambiguous) {
|
|
537
|
+
ambiguousTypes.push({ type, chosen: resolved.source ?? '?', candidates: resolved.candidates });
|
|
538
|
+
}
|
|
539
|
+
if (resolved.source && resolved.source !== '?') {
|
|
540
|
+
const sourceDir = path.join(externalDir, resolved.source);
|
|
541
|
+
// Reload only this source's copy of the conflicting type so it
|
|
542
|
+
// overwrites whatever else won the initial pass.
|
|
543
|
+
const reloadCount = this.loadDir(sourceDir);
|
|
544
|
+
// reloadCount is a sanity check — we expect ≥1
|
|
545
|
+
if (reloadCount === 0) {
|
|
546
|
+
this.log(`Active source "${resolved.source}" no longer provides ${type}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (totalLoaded > 0) {
|
|
551
|
+
this.log(`Loaded ${totalLoaded} external providers (3rd-party sources)`);
|
|
552
|
+
}
|
|
553
|
+
for (const a of ambiguousTypes) {
|
|
554
|
+
this.log(`Ambiguous provider "${a.type}" — provided by [${a.candidates.join(', ')}], defaulted to "${a.chosen}". Set the active source from the dashboard to silence this warning.`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// 3. Load user custom (excluding .upstream — highest priority, never auto-updated)
|
|
340
560
|
if (fs.existsSync(this.userDir)) {
|
|
341
561
|
const userCount = this.loadDir(this.userDir, ['.upstream']);
|
|
342
562
|
if (userCount > 0) {
|
|
@@ -909,18 +1129,28 @@ export class ProviderLoader {
|
|
|
909
1129
|
|
|
910
1130
|
for (const entry of compat) {
|
|
911
1131
|
if (this.matchesVersion(currentVersion, entry.ideVersion)) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
if (
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1132
|
+
// entry.scriptDir is optional now — spec-driven providers (agy,
|
|
1133
|
+
// codex on >=0.137, claude on >=2.1) only ship `spec` here, so
|
|
1134
|
+
// there's nothing to load from the filesystem. SpecCliAdapter
|
|
1135
|
+
// takes over via the `spec` path later in this method.
|
|
1136
|
+
if (entry.scriptDir) {
|
|
1137
|
+
const loaded = this.loadScriptsFromDir(type, entry.scriptDir);
|
|
1138
|
+
if (loaded) {
|
|
1139
|
+
resolved.scripts = loaded;
|
|
1140
|
+
this.debugLog(` [compatibility] ${type} v${currentVersion} → ${entry.scriptDir}`);
|
|
1141
|
+
resolved._resolvedScriptDir = entry.scriptDir;
|
|
1142
|
+
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
1143
|
+
if (providerDir) {
|
|
1144
|
+
const fullDir = path.join(providerDir, entry.scriptDir);
|
|
1145
|
+
resolved._resolvedScriptsPath = fs.existsSync(path.join(fullDir, 'scripts.js'))
|
|
1146
|
+
? path.join(fullDir, 'scripts.js')
|
|
1147
|
+
: fullDir;
|
|
1148
|
+
}
|
|
1149
|
+
matched = true;
|
|
923
1150
|
}
|
|
1151
|
+
} else {
|
|
1152
|
+
// Spec-only entry — still counts as a match so the
|
|
1153
|
+
// defaultScriptDir fallback below doesn't kick in.
|
|
924
1154
|
matched = true;
|
|
925
1155
|
}
|
|
926
1156
|
break; // first match wins
|
|
@@ -988,7 +1218,12 @@ export class ProviderLoader {
|
|
|
988
1218
|
}
|
|
989
1219
|
|
|
990
1220
|
// 3. Composite override (OS + version)
|
|
991
|
-
|
|
1221
|
+
// Legacy shape: base.overrides is an Array<{ when: {os,version}, scripts }>.
|
|
1222
|
+
// v1 manifests (Phase 3-4) repurposed `overrides` as an object map of
|
|
1223
|
+
// capability overrides (e.g. { detectStatus: { path, schema } }), which is
|
|
1224
|
+
// consumed by the SDK builders, not by this resolver. Only iterate when
|
|
1225
|
+
// the field is in the legacy array shape.
|
|
1226
|
+
if (Array.isArray(base.overrides)) {
|
|
992
1227
|
for (const override of base.overrides) {
|
|
993
1228
|
const osMatch = !override.when.os || override.when.os === currentOs;
|
|
994
1229
|
const verMatch = !override.when.version || (currentVersion && this.matchesVersion(currentVersion, override.when.version));
|
|
@@ -996,6 +1231,40 @@ export class ProviderLoader {
|
|
|
996
1231
|
resolved.scripts = { ...resolved.scripts, ...override.scripts };
|
|
997
1232
|
}
|
|
998
1233
|
}
|
|
1234
|
+
} else if (base.overrides && typeof base.overrides === 'object') {
|
|
1235
|
+
// v1 manifest shape: { detectStatus: { path }, parseSession: { path }, ... }
|
|
1236
|
+
// Each script name maps to a path inside the provider directory. We load
|
|
1237
|
+
// the file and merge its export(s) into resolved.scripts. Lets a
|
|
1238
|
+
// provider override a single primitive (e.g. just detectStatus) while
|
|
1239
|
+
// letting the SDK synthesize the rest from the tui block.
|
|
1240
|
+
const providerDir = this.findProviderDirInternal(base.type);
|
|
1241
|
+
if (providerDir) {
|
|
1242
|
+
for (const [scriptName, override] of Object.entries(base.overrides as Record<string, any>)) {
|
|
1243
|
+
if (!override || typeof override.path !== 'string') continue;
|
|
1244
|
+
const fullPath = path.join(providerDir, override.path);
|
|
1245
|
+
if (!fs.existsSync(fullPath)) {
|
|
1246
|
+
this.log(` [overrides] ${base.type}: ${scriptName} path not found: ${fullPath}`);
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
// Override scripts go through the same whitelist gate as the
|
|
1251
|
+
// main scripts dir. Use the provider parent root so a v1
|
|
1252
|
+
// override can still require ../_shared helpers.
|
|
1253
|
+
registerProviderScriptRootSafely(path.dirname(path.dirname(providerDir)));
|
|
1254
|
+
delete require.cache[require.resolve(fullPath)];
|
|
1255
|
+
const fn = require(fullPath);
|
|
1256
|
+
const target = typeof fn === 'function' ? fn : (fn && fn[scriptName]);
|
|
1257
|
+
if (typeof target === 'function') {
|
|
1258
|
+
resolved.scripts = { ...resolved.scripts, [scriptName]: target } as any;
|
|
1259
|
+
this.log(` [overrides] ${base.type}: ${scriptName} loaded from ${override.path}`);
|
|
1260
|
+
} else {
|
|
1261
|
+
this.log(` [overrides] ${base.type}: ${scriptName} export missing in ${override.path}`);
|
|
1262
|
+
}
|
|
1263
|
+
} catch (e: any) {
|
|
1264
|
+
this.log(` [overrides] ${base.type}: ${scriptName} require failed: ${e?.message || e}`);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
999
1268
|
}
|
|
1000
1269
|
|
|
1001
1270
|
if ((resolved.category === 'cli' || resolved.category === 'acp') && resolved.spawn?.command) {
|
|
@@ -1006,6 +1275,135 @@ export class ProviderLoader {
|
|
|
1006
1275
|
};
|
|
1007
1276
|
}
|
|
1008
1277
|
|
|
1278
|
+
// (spec migration) Late-binding spec.json native-history hook. Runs
|
|
1279
|
+
// *after* every script-loading path (compatibility / defaultScriptDir /
|
|
1280
|
+
// overrides) so it deterministically wins over a legacy v1 scripts.js
|
|
1281
|
+
// export. Three modes, picked by spec.json's native_history block:
|
|
1282
|
+
// 1. source — declarative jsonl/sqlite executor (new-provider path,
|
|
1283
|
+
// no daemon change needed for new on-disk formats)
|
|
1284
|
+
// 2. override_path — provider-supplied reader file (escape hatch for
|
|
1285
|
+
// exotic formats); module default-exports a reader fn
|
|
1286
|
+
// 3. reader — built-in reader id (claude-cli / codex-cli /
|
|
1287
|
+
// antigravity-cli / hermes-cli), kept for backwards
|
|
1288
|
+
// compatibility with the four shipped providers
|
|
1289
|
+
if (providerDir) {
|
|
1290
|
+
try {
|
|
1291
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1292
|
+
const fs = require('node:fs');
|
|
1293
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1294
|
+
const path = require('node:path');
|
|
1295
|
+
// Pick the right spec file for the detected CLI version. Resolution
|
|
1296
|
+
// order:
|
|
1297
|
+
// 1. compatibility[i].spec where ideVersion matches currentVersion
|
|
1298
|
+
// (lets a provider ship specs/2.0.json, specs/2.1.json, etc.
|
|
1299
|
+
// alongside the matching scriptDir)
|
|
1300
|
+
// 2. specs/default.json — explicit fallback
|
|
1301
|
+
// 3. spec.json — legacy single-spec layout
|
|
1302
|
+
// Missing files fall through silently to the next candidate.
|
|
1303
|
+
const candidates: string[] = [];
|
|
1304
|
+
if (Array.isArray((base as any).compatibility)) {
|
|
1305
|
+
for (const entry of (base as any).compatibility) {
|
|
1306
|
+
if (typeof entry?.spec !== 'string') continue;
|
|
1307
|
+
// If currentVersion is unknown (cli-manager hasn't probed yet)
|
|
1308
|
+
// we still let compatibility entries that don't pin a version
|
|
1309
|
+
// through, plus any entry whose pin matches.
|
|
1310
|
+
const matches = !entry.ideVersion
|
|
1311
|
+
|| (currentVersion && this.matchesVersion(currentVersion, entry.ideVersion))
|
|
1312
|
+
|| !currentVersion;
|
|
1313
|
+
if (matches) candidates.push(path.join(providerDir, entry.spec));
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
candidates.push(path.join(providerDir, 'specs', 'default.json'));
|
|
1317
|
+
candidates.push(path.join(providerDir, 'spec.json'));
|
|
1318
|
+
const specPath = candidates.find((p: string) => fs.existsSync(p));
|
|
1319
|
+
if (specPath) {
|
|
1320
|
+
// Hand the resolved spec path off to route.ts via a hidden field
|
|
1321
|
+
// so the routing layer doesn't have to repeat the candidate walk.
|
|
1322
|
+
(resolved as any)._resolvedSpecPath = specPath;
|
|
1323
|
+
// Extract control_bar + native_history directly from the JSON header.
|
|
1324
|
+
let specControls: any[] | undefined;
|
|
1325
|
+
let nh: any | undefined;
|
|
1326
|
+
try {
|
|
1327
|
+
const rawSpec = JSON.parse(fs.readFileSync(specPath, 'utf8'));
|
|
1328
|
+
specControls = rawSpec.control_bar;
|
|
1329
|
+
nh = rawSpec.native_history;
|
|
1330
|
+
} catch { /* unreadable spec — leave controls/native unavailable */ }
|
|
1331
|
+
// Stub each control_bar entry as a provider.scripts.<id>. The
|
|
1332
|
+
// upstream invoke_provider_script gate checks that the script
|
|
1333
|
+
// name exists on provider.scripts before calling adapter.invokeScript;
|
|
1334
|
+
// for spec providers the *actual* dispatch happens inside
|
|
1335
|
+
// SpecCliAdapter.invokeScript which maps the name to control_bar.
|
|
1336
|
+
// The stub is just a presence marker so the gate doesn't reject.
|
|
1337
|
+
if (specControls && specControls.length > 0) {
|
|
1338
|
+
resolved.scripts = { ...(resolved.scripts || {}) };
|
|
1339
|
+
for (const ctl of specControls) {
|
|
1340
|
+
if (!(resolved.scripts as any)[ctl.id]) {
|
|
1341
|
+
(resolved.scripts as any)[ctl.id] = (..._args: unknown[]) => ({
|
|
1342
|
+
__spec_control: true,
|
|
1343
|
+
controlId: ctl.id,
|
|
1344
|
+
actionType: ctl.action.type,
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
// Bridge the spec control_bar into the web-facing controls schema so
|
|
1349
|
+
// the dashboard chat bar actually renders Model/Mode pickers. Only
|
|
1350
|
+
// synthesize when the provider hasn't already declared its own
|
|
1351
|
+
// `controls` in provider.v1.json (e.g. hermes-cli) — an explicit
|
|
1352
|
+
// declaration wins and must not be clobbered.
|
|
1353
|
+
const hasDeclaredControls = Array.isArray((resolved as any).controls)
|
|
1354
|
+
&& (resolved as any).controls.length > 0;
|
|
1355
|
+
if (!hasDeclaredControls) {
|
|
1356
|
+
const synthesized = synthesizeControlsFromControlBar(specControls);
|
|
1357
|
+
if (synthesized.length > 0) {
|
|
1358
|
+
resolved.controls = synthesized;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (nh) {
|
|
1363
|
+
let reader: ((input: any) => any) | null = null;
|
|
1364
|
+
let format = 'spec';
|
|
1365
|
+
|
|
1366
|
+
if (nh.source) {
|
|
1367
|
+
format = `spec-${nh.source.kind}`;
|
|
1368
|
+
reader = (input: any) => executeNativeHistory(nh, input);
|
|
1369
|
+
} else if (nh.override_path) {
|
|
1370
|
+
const overrideFile = path.resolve(providerDir, nh.override_path);
|
|
1371
|
+
if (fs.existsSync(overrideFile)) {
|
|
1372
|
+
try {
|
|
1373
|
+
registerProviderScriptRootSafely(path.dirname(path.dirname(providerDir)));
|
|
1374
|
+
delete require.cache[require.resolve(overrideFile)];
|
|
1375
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1376
|
+
const mod = require(overrideFile);
|
|
1377
|
+
const fn = typeof mod === 'function' ? mod : (mod && typeof mod.default === 'function' ? mod.default : null);
|
|
1378
|
+
if (fn) {
|
|
1379
|
+
format = 'spec-override';
|
|
1380
|
+
reader = (input: any) => fn(input);
|
|
1381
|
+
}
|
|
1382
|
+
} catch { /* fall through — leave native unavailable */ }
|
|
1383
|
+
}
|
|
1384
|
+
} else if (nh.reader) {
|
|
1385
|
+
const dispatch = createNativeHistoryDispatcher(nh.reader as ReaderId);
|
|
1386
|
+
format = nh.reader;
|
|
1387
|
+
reader = (input: any) => dispatch(input);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
if (reader) {
|
|
1391
|
+
resolved.scripts = { ...(resolved.scripts || {}) };
|
|
1392
|
+
(resolved.scripts as any).readNativeHistory = reader;
|
|
1393
|
+
(resolved as any).nativeHistory = {
|
|
1394
|
+
format,
|
|
1395
|
+
watchPath: undefined,
|
|
1396
|
+
scripts: { readSession: 'readNativeHistory' },
|
|
1397
|
+
mode: 'native-source',
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
} catch {
|
|
1403
|
+
// Best-effort — spec wiring failure must not break legacy providers.
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1009
1407
|
return resolved;
|
|
1010
1408
|
}
|
|
1011
1409
|
|
|
@@ -1016,16 +1414,27 @@ export class ProviderLoader {
|
|
|
1016
1414
|
private loadScriptsFromDir(type: string, scriptDir: string): Partial<ProviderScripts> | null {
|
|
1017
1415
|
const providerDir = this.findProviderDirInternal(type);
|
|
1018
1416
|
if (!providerDir) {
|
|
1019
|
-
this
|
|
1417
|
+
// No provider dir for this type — a spec-only provider with no
|
|
1418
|
+
// legacy scripts/v1 layout is a normal configuration, not a
|
|
1419
|
+
// problem to surface at INFO. resolve() calls this on every
|
|
1420
|
+
// request; INFO spam every 200ms drowns out the real signal.
|
|
1421
|
+
this.debugLog(`[loadScriptsFromDir] ${type}: providerDir not found`);
|
|
1020
1422
|
return null;
|
|
1021
1423
|
}
|
|
1022
1424
|
|
|
1023
1425
|
const dir = path.join(providerDir, scriptDir);
|
|
1024
1426
|
if (!fs.existsSync(dir)) {
|
|
1025
|
-
this.
|
|
1427
|
+
this.debugLog(`[loadScriptsFromDir] ${type}: dir not found: ${dir}`);
|
|
1026
1428
|
return null;
|
|
1027
1429
|
}
|
|
1028
1430
|
|
|
1431
|
+
// Register the provider's *parent root* (e.g. .../adhdev-providers/) so
|
|
1432
|
+
// the require whitelist gates every script + every _shared helper this
|
|
1433
|
+
// provider may reach. Picking the grandparent (one above the category
|
|
1434
|
+
// dir `cli/`) lets sibling helpers in `_shared` resolve while still
|
|
1435
|
+
// blocking `../../etc/...` escapes. Idempotent.
|
|
1436
|
+
registerProviderScriptRootSafely(path.dirname(path.dirname(providerDir)));
|
|
1437
|
+
|
|
1029
1438
|
// Return cached scripts if available (cleared on reload/watch)
|
|
1030
1439
|
const cached = this.scriptsCache.get(dir);
|
|
1031
1440
|
if (cached) return cached;
|
|
@@ -1036,7 +1445,7 @@ export class ProviderLoader {
|
|
|
1036
1445
|
try {
|
|
1037
1446
|
delete require.cache[require.resolve(scriptsJs)];
|
|
1038
1447
|
const loaded = require(scriptsJs);
|
|
1039
|
-
this.
|
|
1448
|
+
this.debugLog(`[loadScriptsFromDir] ${type}: loaded scripts.js from ${dir} (${Object.keys(loaded).length} exports)`);
|
|
1040
1449
|
this.scriptsCache.set(dir, loaded);
|
|
1041
1450
|
return loaded;
|
|
1042
1451
|
} catch (e) {
|
|
@@ -1067,13 +1476,17 @@ export class ProviderLoader {
|
|
|
1067
1476
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 },
|
|
1068
1477
|
});
|
|
1069
1478
|
|
|
1479
|
+
let reloadTimer: ReturnType<typeof setTimeout> | null = null;
|
|
1070
1480
|
const handleChange = (filePath: string) => {
|
|
1071
1481
|
if (/[\/\\]fixtures[\/\\]/.test(filePath)) {
|
|
1072
1482
|
return;
|
|
1073
1483
|
}
|
|
1074
1484
|
if (filePath.endsWith('.js') || filePath.endsWith('.json')) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1485
|
+
if (reloadTimer) clearTimeout(reloadTimer);
|
|
1486
|
+
reloadTimer = setTimeout(() => {
|
|
1487
|
+
this.log(`File changed: ${path.basename(filePath)}, reloading...`);
|
|
1488
|
+
this.reload();
|
|
1489
|
+
}, 300);
|
|
1077
1490
|
}
|
|
1078
1491
|
};
|
|
1079
1492
|
|
|
@@ -1124,13 +1537,115 @@ export class ProviderLoader {
|
|
|
1124
1537
|
*
|
|
1125
1538
|
* @returns Whether an update occurred
|
|
1126
1539
|
*/
|
|
1540
|
+
/**
|
|
1541
|
+
* Sync providers from the ADHDev registry (registry.adhf.dev).
|
|
1542
|
+
*
|
|
1543
|
+
* Downloads only providers whose server checksum differs from the locally
|
|
1544
|
+
* cached checksum. Falls back gracefully to the GitHub tarball path if the
|
|
1545
|
+
* registry is unreachable or returns an unexpected response.
|
|
1546
|
+
*
|
|
1547
|
+
* Returns `{ updated: true }` when at least one provider file changed on disk,
|
|
1548
|
+
* `{ updated: false }` when everything is already current, or
|
|
1549
|
+
* `{ updated: false, error }` when the registry couldn't be reached and we
|
|
1550
|
+
* should proceed to the GitHub tarball fallback.
|
|
1551
|
+
*/
|
|
1552
|
+
async fetchFromRegistry(): Promise<{ updated: boolean; error?: string }> {
|
|
1553
|
+
if (this.disableUpstream) {
|
|
1554
|
+
this.log('Registry sync skipped (sourceMode=no-upstream)');
|
|
1555
|
+
return { updated: false };
|
|
1556
|
+
}
|
|
1557
|
+
this.log(`Registry sync starting (${ProviderLoader.REGISTRY_BASE_URL})...`);
|
|
1558
|
+
|
|
1559
|
+
const https = require('https') as typeof import('https');
|
|
1560
|
+
const regMetaPath = path.join(this.upstreamDir, ProviderLoader.REGISTRY_META_FILE);
|
|
1561
|
+
|
|
1562
|
+
// Load cached checksums
|
|
1563
|
+
let cachedChecksums: Record<string, string> = {};
|
|
1564
|
+
try {
|
|
1565
|
+
if (fs.existsSync(regMetaPath)) {
|
|
1566
|
+
cachedChecksums = JSON.parse(fs.readFileSync(regMetaPath, 'utf-8')).checksums ?? {};
|
|
1567
|
+
}
|
|
1568
|
+
} catch { }
|
|
1569
|
+
|
|
1570
|
+
try {
|
|
1571
|
+
// 1. Fetch provider list
|
|
1572
|
+
const listUrl = `${ProviderLoader.REGISTRY_BASE_URL}/providers`;
|
|
1573
|
+
const listBody = await new Promise<string>((resolve, reject) => {
|
|
1574
|
+
const req = https.get(listUrl, { headers: { 'User-Agent': 'adhdev-daemon', 'Accept': 'application/json' }, timeout: 10000 }, (res) => {
|
|
1575
|
+
if (res.statusCode !== 200) { reject(new Error(`registry list HTTP ${res.statusCode}`)); return; }
|
|
1576
|
+
const chunks: Buffer[] = [];
|
|
1577
|
+
res.on('data', (c: Buffer) => chunks.push(c));
|
|
1578
|
+
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
1579
|
+
});
|
|
1580
|
+
req.on('error', reject);
|
|
1581
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('registry list timeout')); });
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
const list = JSON.parse(listBody) as { providers: Array<{ type: string; category: string; checksum: string; version: string }> };
|
|
1585
|
+
if (!Array.isArray(list.providers)) throw new Error('unexpected registry response shape');
|
|
1586
|
+
|
|
1587
|
+
let updatedCount = 0;
|
|
1588
|
+
|
|
1589
|
+
for (const entry of list.providers) {
|
|
1590
|
+
const { type, category, checksum, version } = entry;
|
|
1591
|
+
const cacheKey = `${category}/${type}`;
|
|
1592
|
+
if (cachedChecksums[cacheKey] === checksum) continue; // already current
|
|
1593
|
+
|
|
1594
|
+
// Download this provider's manifest
|
|
1595
|
+
const dlUrl = `${ProviderLoader.REGISTRY_BASE_URL}/providers/${type}/${version}/download`;
|
|
1596
|
+
const manifestBody = await new Promise<string>((resolve, reject) => {
|
|
1597
|
+
const req = https.get(dlUrl, { headers: { 'User-Agent': 'adhdev-daemon', 'Accept': 'application/json' }, timeout: 30000 }, (res) => {
|
|
1598
|
+
if (res.statusCode !== 200) { reject(new Error(`registry download HTTP ${res.statusCode} for ${type}@${version}`)); return; }
|
|
1599
|
+
const chunks: Buffer[] = [];
|
|
1600
|
+
res.on('data', (c: Buffer) => chunks.push(c));
|
|
1601
|
+
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
1602
|
+
});
|
|
1603
|
+
req.on('error', reject);
|
|
1604
|
+
req.on('timeout', () => { req.destroy(); reject(new Error(`download timeout for ${type}`)); });
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
// Verify checksum
|
|
1608
|
+
const actualChecksum = sha256Hex(manifestBody);
|
|
1609
|
+
if (actualChecksum !== checksum) {
|
|
1610
|
+
this.log(`⚠ Registry checksum mismatch for ${type}@${version} — skipping`);
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Write to upstream dir
|
|
1615
|
+
const providerDir = path.join(this.upstreamDir, category, type);
|
|
1616
|
+
fs.mkdirSync(providerDir, { recursive: true });
|
|
1617
|
+
fs.writeFileSync(path.join(providerDir, 'provider.json'), manifestBody, 'utf-8');
|
|
1618
|
+
|
|
1619
|
+
cachedChecksums[cacheKey] = checksum;
|
|
1620
|
+
updatedCount++;
|
|
1621
|
+
this.log(`✓ Registry updated: ${category}/${type}@${version}`);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Persist updated checksums
|
|
1625
|
+
fs.mkdirSync(this.upstreamDir, { recursive: true });
|
|
1626
|
+
fs.writeFileSync(regMetaPath, JSON.stringify({
|
|
1627
|
+
checksums: cachedChecksums,
|
|
1628
|
+
syncedAt: new Date().toISOString(),
|
|
1629
|
+
providerCount: list.providers.length,
|
|
1630
|
+
}, null, 2));
|
|
1631
|
+
|
|
1632
|
+
this.log(`Registry sync complete: ${list.providers.length} providers, ${updatedCount} updated`);
|
|
1633
|
+
return { updated: updatedCount > 0 };
|
|
1634
|
+
} catch (e: any) {
|
|
1635
|
+
this.log(`⚠ Registry sync failed (falling back to GitHub tarball): ${e?.message}`);
|
|
1636
|
+
return { updated: false, error: e?.message };
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1127
1640
|
async fetchLatest(): Promise<{ updated: boolean; error?: string }> {
|
|
1128
1641
|
if (this.disableUpstream) {
|
|
1129
1642
|
this.log('Upstream fetch skipped (sourceMode=no-upstream)');
|
|
1130
1643
|
return { updated: false };
|
|
1131
1644
|
}
|
|
1132
1645
|
const https = require('https') as typeof import('https');
|
|
1133
|
-
const {
|
|
1646
|
+
const { exec } = require('child_process') as typeof import('child_process');
|
|
1647
|
+
const { promisify } = require('util');
|
|
1648
|
+
const execAsync = promisify(exec);
|
|
1134
1649
|
|
|
1135
1650
|
const metaPath = path.join(this.upstreamDir, ProviderLoader.META_FILE);
|
|
1136
1651
|
let prevEtag = '';
|
|
@@ -1207,7 +1722,7 @@ export class ProviderLoader {
|
|
|
1207
1722
|
|
|
1208
1723
|
// Extract
|
|
1209
1724
|
fs.mkdirSync(tmpExtract, { recursive: true });
|
|
1210
|
-
|
|
1725
|
+
await execAsync(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 30000 });
|
|
1211
1726
|
|
|
1212
1727
|
// Tarball internal structure: adhdev-providers-main/ide/... → strip 1 level
|
|
1213
1728
|
const extracted = fs.readdirSync(tmpExtract);
|
|
@@ -1315,15 +1830,17 @@ export class ProviderLoader {
|
|
|
1315
1830
|
} catch { }
|
|
1316
1831
|
}
|
|
1317
1832
|
|
|
1318
|
-
/** Count provider files (provider.
|
|
1833
|
+
/** Count provider files (provider.v1.json or provider.json — at most one per dir). */
|
|
1319
1834
|
private countProviders(dir: string): number {
|
|
1320
1835
|
if (!fs.existsSync(dir)) return 0;
|
|
1321
1836
|
let count = 0;
|
|
1322
1837
|
const scan = (d: string) => {
|
|
1323
1838
|
try {
|
|
1324
|
-
|
|
1839
|
+
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
1840
|
+
const hasManifest = entries.some(e => e.name === 'provider.v1.json' || e.name === 'provider.json');
|
|
1841
|
+
if (hasManifest) count++;
|
|
1842
|
+
for (const entry of entries) {
|
|
1325
1843
|
if (entry.isDirectory()) scan(path.join(d, entry.name));
|
|
1326
|
-
else if (entry.name === 'provider.json') count++;
|
|
1327
1844
|
}
|
|
1328
1845
|
} catch { }
|
|
1329
1846
|
};
|
|
@@ -1372,11 +1889,15 @@ export class ProviderLoader {
|
|
|
1372
1889
|
return args ? args.map((arg) => /\s/.test(arg) ? JSON.stringify(arg) : arg).join(' ') : '';
|
|
1373
1890
|
}
|
|
1374
1891
|
const schemaDef = this.getSettingsSchema(providerType)[key];
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1892
|
+
// (fix) Previously this hard-coded `autoApprove` boolean default to `true`,
|
|
1893
|
+
// overriding whatever schemaDef.default the provider.json declared. That
|
|
1894
|
+
// surfaced as soon as a provider added an `autoApprove` schema entry with
|
|
1895
|
+
// default=false: the user had never opted in but the daemon treated the
|
|
1896
|
+
// session as auto-approve, which then triggered recordAutoApproval every
|
|
1897
|
+
// time the CLI showed an approval modal — producing a flood of system
|
|
1898
|
+
// "Auto-approved: ..." messages and keeping the session pinned to
|
|
1899
|
+
// generating while modals cycled in and out. Trust the schemaDef.default.
|
|
1900
|
+
const defaultVal = schemaDef ? schemaDef.default : undefined;
|
|
1380
1901
|
|
|
1381
1902
|
const config = this.readConfig();
|
|
1382
1903
|
const userVal = config?.providerSettings?.[providerType]?.[key];
|
|
@@ -1490,10 +2011,17 @@ export class ProviderLoader {
|
|
|
1490
2011
|
...this.getSyntheticSettings(type, provider),
|
|
1491
2012
|
...(provider.settings || {}),
|
|
1492
2013
|
};
|
|
2014
|
+
// (fix) Previously this clause forced `autoApprove.default = true` for any
|
|
2015
|
+
// boolean autoApprove schema, even when the provider.json explicitly set
|
|
2016
|
+
// `default: false`. Combined with the synthetic-settings fallback at
|
|
2017
|
+
// getSyntheticSettings (which also defaults autoApprove to true when the
|
|
2018
|
+
// provider doesn't supply one), that meant CLI providers silently turned on
|
|
2019
|
+
// auto-approval, producing a flood of "Auto-approved: ..." system messages
|
|
2020
|
+
// every time an approval modal appeared and pinning the session to
|
|
2021
|
+
// generating while modals cycled. Trust the provider's declared default.
|
|
1493
2022
|
if (result.autoApprove?.type === 'boolean') {
|
|
1494
2023
|
result.autoApprove = {
|
|
1495
2024
|
...result.autoApprove,
|
|
1496
|
-
default: true,
|
|
1497
2025
|
public: true,
|
|
1498
2026
|
label: result.autoApprove.label || 'Auto Approve',
|
|
1499
2027
|
description: result.autoApprove.description || 'Automatically approve actionable prompts without sending approval alerts.',
|
|
@@ -1518,7 +2046,10 @@ export class ProviderLoader {
|
|
|
1518
2046
|
if (!provider.settings?.autoApprove) {
|
|
1519
2047
|
result.autoApprove = {
|
|
1520
2048
|
type: 'boolean',
|
|
1521
|
-
default
|
|
2049
|
+
// (fix) Safe default is *off*. Auto-approving every modal without the
|
|
2050
|
+
// user opting in produced silent-bash-execution surprises and the
|
|
2051
|
+
// "Auto-approved: ..." system-message flood seen on AGY/Codex.
|
|
2052
|
+
default: false,
|
|
1522
2053
|
public: true,
|
|
1523
2054
|
label: 'Auto Approve',
|
|
1524
2055
|
description: 'Automatically approve actionable prompts without sending approval alerts.',
|
|
@@ -1581,23 +2112,32 @@ export class ProviderLoader {
|
|
|
1581
2112
|
const cat = provider.category;
|
|
1582
2113
|
|
|
1583
2114
|
const searchRoots = this.getProviderRoots();
|
|
2115
|
+
const hasManifest = (dir: string) =>
|
|
2116
|
+
fs.existsSync(path.join(dir, 'provider.v1.json')) || fs.existsSync(path.join(dir, 'provider.json'));
|
|
2117
|
+
const readManifestType = (dir: string): string | null => {
|
|
2118
|
+
for (const file of ['provider.v1.json', 'provider.json']) {
|
|
2119
|
+
const p = path.join(dir, file);
|
|
2120
|
+
if (!fs.existsSync(p)) continue;
|
|
2121
|
+
try {
|
|
2122
|
+
const data = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
2123
|
+
if (typeof data?.type === 'string') return data.type;
|
|
2124
|
+
} catch { /* skip */ }
|
|
2125
|
+
}
|
|
2126
|
+
return null;
|
|
2127
|
+
};
|
|
1584
2128
|
for (const root of searchRoots) {
|
|
1585
2129
|
if (!fs.existsSync(root)) continue;
|
|
1586
2130
|
const candidate = this.getProviderDir(root, cat, type);
|
|
1587
|
-
if (
|
|
2131
|
+
if (hasManifest(candidate)) return candidate;
|
|
1588
2132
|
// Scan category dir for type match
|
|
1589
2133
|
const catDir = path.join(root, cat);
|
|
1590
2134
|
if (fs.existsSync(catDir)) {
|
|
1591
2135
|
try {
|
|
1592
2136
|
for (const entry of fs.readdirSync(catDir, { withFileTypes: true })) {
|
|
1593
2137
|
if (!entry.isDirectory()) continue;
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
const data = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
|
|
1598
|
-
if (data.type === type) return path.join(catDir, entry.name);
|
|
1599
|
-
} catch { /* skip */ }
|
|
1600
|
-
}
|
|
2138
|
+
const entryDir = path.join(catDir, entry.name);
|
|
2139
|
+
const manifestType = readManifestType(entryDir);
|
|
2140
|
+
if (manifestType === type) return entryDir;
|
|
1601
2141
|
}
|
|
1602
2142
|
} catch { /* skip */ }
|
|
1603
2143
|
}
|
|
@@ -1690,17 +2230,44 @@ export class ProviderLoader {
|
|
|
1690
2230
|
return;
|
|
1691
2231
|
}
|
|
1692
2232
|
|
|
1693
|
-
//
|
|
2233
|
+
// v1-first manifest selection. provider.v1.json (the SDK-shape
|
|
2234
|
+
// manifest with `overrides`, `tui`, `source`, `canonicalHistory`)
|
|
2235
|
+
// wins over provider.json (legacy). Without this branch the v1
|
|
2236
|
+
// file is silently ignored — that's how the codex-cli `overrides`
|
|
2237
|
+
// path and the tui-block builders went un-honored for the first
|
|
2238
|
+
// pass of SDK rollout.
|
|
2239
|
+
const hasV1 = entries.some(e => e.name === 'provider.v1.json');
|
|
1694
2240
|
const hasJson = entries.some(e => e.name === 'provider.json');
|
|
1695
2241
|
|
|
1696
|
-
if (hasJson) {
|
|
1697
|
-
const
|
|
2242
|
+
if (hasV1 || hasJson) {
|
|
2243
|
+
const manifestFile = hasV1 ? 'provider.v1.json' : 'provider.json';
|
|
2244
|
+
const jsonPath = path.join(d, manifestFile);
|
|
1698
2245
|
try {
|
|
1699
2246
|
const raw = fs.readFileSync(jsonPath, 'utf-8');
|
|
1700
2247
|
const mod = JSON.parse(raw) as Omit<ProviderModule, 'extensionIdPattern'> & {
|
|
1701
2248
|
extensionIdPattern?: RegExp | string;
|
|
1702
2249
|
};
|
|
1703
2250
|
|
|
2251
|
+
// Validate v1 manifests against the SDK schema. Failures are
|
|
2252
|
+
// surfaced as a single warning line with all issues attached
|
|
2253
|
+
// so manifest authors don't need to guess which field is wrong.
|
|
2254
|
+
// Loading still proceeds — bricking the daemon on a single
|
|
2255
|
+
// bad field would be worse than running with a known warning.
|
|
2256
|
+
if (hasV1 && mod?.category === 'cli') {
|
|
2257
|
+
try {
|
|
2258
|
+
const { validateCliProviderManifest, formatManifestValidationIssues } =
|
|
2259
|
+
require('./sdk/v1/validators/manifest.js') as typeof import('./sdk/v1/validators/manifest.js');
|
|
2260
|
+
const validation = validateCliProviderManifest(mod);
|
|
2261
|
+
if (!validation.ok) {
|
|
2262
|
+
this.log(`⚠ ${jsonPath}: schema validation failed:\n${formatManifestValidationIssues(validation.issues)}`);
|
|
2263
|
+
}
|
|
2264
|
+
} catch (e: any) {
|
|
2265
|
+
// Validator load failed — log once and continue so a
|
|
2266
|
+
// broken validator can't take down provider loading.
|
|
2267
|
+
this.log(`⚠ ${jsonPath}: validator unavailable: ${e?.message || e}`);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
1704
2271
|
// Restore RegExp fields from JSON (extensionIdPattern)
|
|
1705
2272
|
if (typeof mod.extensionIdPattern === 'string') {
|
|
1706
2273
|
const flags = mod.extensionIdPattern_flags || '';
|
|
@@ -1712,6 +2279,22 @@ export class ProviderLoader {
|
|
|
1712
2279
|
...(extensionIdPattern instanceof RegExp ? { extensionIdPattern } : {}),
|
|
1713
2280
|
};
|
|
1714
2281
|
|
|
2282
|
+
// v1 manifests use `nativeHistory` as the canonical field name.
|
|
2283
|
+
// Legacy v0 manifests use `canonicalHistory`. The daemon's
|
|
2284
|
+
// runtime + downstream code reads `provider.nativeHistory`, so
|
|
2285
|
+
// for legacy manifests we copy `canonicalHistory` into
|
|
2286
|
+
// `nativeHistory` here. We also keep `canonicalHistory`
|
|
2287
|
+
// populated in both directions (deprecated alias) so any
|
|
2288
|
+
// external consumers still reading the old name keep working
|
|
2289
|
+
// during the one-release deprecation window.
|
|
2290
|
+
const nh = (normalizedProvider as any).nativeHistory;
|
|
2291
|
+
const ch = (normalizedProvider as any).canonicalHistory;
|
|
2292
|
+
if (nh && !ch) {
|
|
2293
|
+
(normalizedProvider as any).canonicalHistory = nh;
|
|
2294
|
+
} else if (ch && !nh) {
|
|
2295
|
+
(normalizedProvider as any).nativeHistory = ch;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
1715
2298
|
const validation = validateProviderDefinition(normalizedProvider);
|
|
1716
2299
|
for (const warning of validation.warnings) {
|
|
1717
2300
|
this.log(`⚠ ${jsonPath}: ${warning}`);
|
|
@@ -1725,6 +2308,10 @@ export class ProviderLoader {
|
|
|
1725
2308
|
const scriptsPath = path.join(d, 'scripts.js');
|
|
1726
2309
|
if (!hasCompatibility && fs.existsSync(scriptsPath)) {
|
|
1727
2310
|
try {
|
|
2311
|
+
// Gate the IDE/extension scripts.js (legacy single-file
|
|
2312
|
+
// format) under the same whitelist. `d` here is the
|
|
2313
|
+
// provider dir; its grandparent contains _shared.
|
|
2314
|
+
registerProviderScriptRootSafely(path.dirname(path.dirname(d)));
|
|
1728
2315
|
delete require.cache[require.resolve(scriptsPath)];
|
|
1729
2316
|
const scripts = require(scriptsPath) as Partial<ProviderScripts>;
|
|
1730
2317
|
normalizedProvider.scripts = scripts;
|
|
@@ -1733,14 +2320,40 @@ export class ProviderLoader {
|
|
|
1733
2320
|
}
|
|
1734
2321
|
}
|
|
1735
2322
|
|
|
2323
|
+
// Classify trust based on which on-disk layer this manifest
|
|
2324
|
+
// came from + whether it ships JavaScript hooks. The dashboard
|
|
2325
|
+
// uses this to render trust badges; non-spec external manifests
|
|
2326
|
+
// need an explicit user confirm before activation.
|
|
2327
|
+
const externalDirAbs = path.join(os.homedir(), '.adhdev', 'external');
|
|
2328
|
+
const layer: 'user' | 'upstream' | 'external' = d.startsWith(externalDirAbs)
|
|
2329
|
+
? 'external'
|
|
2330
|
+
: (d.startsWith(this.userDir) && !d.includes('.upstream') ? 'user' : 'upstream');
|
|
2331
|
+
try {
|
|
2332
|
+
const { inspectManifestShape, classifyTrust } =
|
|
2333
|
+
require('./provider-trust.js') as typeof import('./provider-trust.js');
|
|
2334
|
+
const shape = inspectManifestShape(mod as Record<string, unknown>);
|
|
2335
|
+
const trust = classifyTrust(layer, shape);
|
|
2336
|
+
(normalizedProvider as any)._sourceLayer = layer;
|
|
2337
|
+
(normalizedProvider as any)._sourceTrust = trust;
|
|
2338
|
+
(normalizedProvider as any)._manifestShape = shape;
|
|
2339
|
+
// For external-namespaced layouts (external/<source>/…) record
|
|
2340
|
+
// which source the manifest came from so dashboards can name
|
|
2341
|
+
// it in the trust badge.
|
|
2342
|
+
if (layer === 'external') {
|
|
2343
|
+
const rel = path.relative(externalDirAbs, d);
|
|
2344
|
+
const firstSeg = rel.split(path.sep)[0];
|
|
2345
|
+
if (firstSeg && firstSeg !== '..') (normalizedProvider as any)._sourceName = firstSeg;
|
|
2346
|
+
}
|
|
2347
|
+
} catch { /* best-effort — trust is enrichment, not gating */ }
|
|
2348
|
+
|
|
1736
2349
|
const existed = this.providers.has(normalizedProvider.type);
|
|
1737
2350
|
this.providers.set(normalizedProvider.type, normalizedProvider);
|
|
1738
2351
|
count++;
|
|
1739
|
-
|
|
1740
|
-
const source = d.startsWith(this.userDir) && !d.includes('.upstream')
|
|
1741
|
-
? 'user' : 'upstream';
|
|
2352
|
+
const source = (normalizedProvider as any)._sourceLayer ?? 'upstream';
|
|
1742
2353
|
const overrideWarning = existed && source === 'user' ? ' ⚠ OVERRIDES upstream' : '';
|
|
1743
|
-
|
|
2354
|
+
const sourceName = (normalizedProvider as any)._sourceName;
|
|
2355
|
+
const sourceLabel = sourceName ? `${source}/${sourceName}` : source;
|
|
2356
|
+
this.log(` ${existed ? '🔄' : '✅'} ${normalizedProvider.type} (${normalizedProvider.category}) — ${normalizedProvider.name} [${sourceLabel}]${overrideWarning}`);
|
|
1744
2357
|
}
|
|
1745
2358
|
} catch (e) {
|
|
1746
2359
|
this.log(`⚠ Failed to load ${jsonPath}: ${(e as Error).message}`);
|
|
@@ -1752,6 +2365,11 @@ export class ProviderLoader {
|
|
|
1752
2365
|
for (const entry of entries) {
|
|
1753
2366
|
if (!entry.isDirectory()) continue;
|
|
1754
2367
|
if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue;
|
|
2368
|
+
// `examples/` is a documentation / scaffold tree (e.g. stub-cli),
|
|
2369
|
+
// not a real provider source. SDK authors copy from here when
|
|
2370
|
+
// writing a new provider; daemon-core tests reference the
|
|
2371
|
+
// manifest by path. Keep it off the dashboard's provider list.
|
|
2372
|
+
if (d === dir && entry.name === 'examples') continue;
|
|
1755
2373
|
if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
|
|
1756
2374
|
scan(path.join(d, entry.name));
|
|
1757
2375
|
}
|