@moondeck/station 0.0.1 → 1.0.1

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.
Files changed (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -2
  3. package/bin/uai-host.mjs +130 -0
  4. package/db/migrations/0000_host_tasks.sql +12 -0
  5. package/db/migrations/0001_host_ui.sql +11 -0
  6. package/db/migrations/0002_host_github_tokens.sql +8 -0
  7. package/db/migrations/0003_host_ssh_keys.sql +8 -0
  8. package/db/migrations/0004_host_owner_name.sql +1 -0
  9. package/db/migrations/0005_host_env_vars.sql +5 -0
  10. package/db/migrations/0006_host_project_env.sql +9 -0
  11. package/db/migrations/0007_host_github_token_kind.sql +7 -0
  12. package/db/migrations/0008_host_mcp_connections.sql +18 -0
  13. package/db/migrations/0009_host_agent_sessions.sql +15 -0
  14. package/db/migrations/0010_host_engine_accounts.sql +13 -0
  15. package/db/migrations/0011_codex_thread_id.sql +5 -0
  16. package/db/migrations/0012_agent_session_attach_key.sql +4 -0
  17. package/db/migrations/0013_github_credential_generations.sql +11 -0
  18. package/db/migrations/0014_host_inventory_event_index.sql +1 -0
  19. package/db/migrations/0015_host_settings.sql +9 -0
  20. package/db/migrations/0016_task_environment.sql +2 -0
  21. package/db/migrations/0017_engine_account_scope.sql +7 -0
  22. package/db/migrations/0018_engine_account_position.sql +3 -0
  23. package/db/migrations/0019_engine_account_store_ref.sql +3 -0
  24. package/db/migrations/0020_user_credential_store_ref.sql +6 -0
  25. package/db/migrations/0021_project_env_store_ref.sql +3 -0
  26. package/db/migrations/0022_task_project_ids.sql +5 -0
  27. package/db/migrations/0023_project_env_org.sql +4 -0
  28. package/db/migrations/0024_task_env_mode.sql +9 -0
  29. package/db/migrations/0025_project_env_authority.sql +147 -0
  30. package/db/migrations/0026_project_env_replica.sql +150 -0
  31. package/db/migrations/0027_cloud_credential_foundation.sql +51 -0
  32. package/db/migrations/0028_leased_engine_runtimes.sql +11 -0
  33. package/db/migrations/0029_task_shared_files_mode.sql +5 -0
  34. package/db/migrations/0030_credential_cleanup_restops.sql +12 -0
  35. package/db/migrations/0031_first_turn_receipts.sql +9 -0
  36. package/db/migrations/meta/_journal.json +230 -0
  37. package/db/schema.ts +871 -0
  38. package/images/standard/Dockerfile +347 -0
  39. package/images/standard/README.md +167 -0
  40. package/images/standard/container/code-server-settings.json +63 -0
  41. package/images/standard/container/corepack-version +1 -0
  42. package/images/standard/container/uai-init +704 -0
  43. package/images/standard/container/uai-materialize-runtimes +1527 -0
  44. package/images/standard/container/uai-task-forwarder.mjs +303 -0
  45. package/images/standard/tool-versions +2 -0
  46. package/lib/__fixtures__/skill-index/SKILL.md +10 -0
  47. package/lib/__fixtures__/skill-index/skills/accessibility-checklist.md +7 -0
  48. package/lib/__fixtures__/skill-index/skills/brand-voice.md +9 -0
  49. package/lib/__fixtures__/skill-index/skills/design-tokens.md +9 -0
  50. package/lib/agent-cli.ts +1507 -0
  51. package/lib/agent-errors.ts +22 -0
  52. package/lib/agent.ts +672 -0
  53. package/lib/agents/attach-compatibility.ts +51 -0
  54. package/lib/agents/claude.ts +918 -0
  55. package/lib/agents/codex-token-usage.fixture.json +130 -0
  56. package/lib/agents/codex.ts +1392 -0
  57. package/lib/agents/cursor-period-usage.ts +253 -0
  58. package/lib/agents/cursor.ts +562 -0
  59. package/lib/agents/dispatch.ts +78 -0
  60. package/lib/agents/durable-proc.ts +334 -0
  61. package/lib/agents/factory.ts +57 -0
  62. package/lib/agents/grok.ts +381 -0
  63. package/lib/agents/kimi.ts +329 -0
  64. package/lib/agents/machine-durable.ts +604 -0
  65. package/lib/agents/mock.ts +133 -0
  66. package/lib/agents/mode.ts +64 -0
  67. package/lib/agents/opencode.ts +508 -0
  68. package/lib/agents/output-source.ts +3 -0
  69. package/lib/agents/proc.ts +221 -0
  70. package/lib/agents/rate-limit.ts +337 -0
  71. package/lib/agents/registry.ts +182 -0
  72. package/lib/agents/session-state.ts +197 -0
  73. package/lib/agents/skill-index-preamble.fixture.ts +26 -0
  74. package/lib/agents/transport.ts +741 -0
  75. package/lib/agents/types.ts +254 -0
  76. package/lib/agents/uai.ts +106 -0
  77. package/lib/agents/usage.ts +44 -0
  78. package/lib/apple-one-off.ts +277 -0
  79. package/lib/apple-runtime-recycle.ts +237 -0
  80. package/lib/apple-uninstall-teardown.ts +224 -0
  81. package/lib/assignment-evidence.ts +94 -0
  82. package/lib/attachments.ts +48 -0
  83. package/lib/browser-testing.ts +1537 -0
  84. package/lib/capture-flow.ts +332 -0
  85. package/lib/cloud-credential-cleanup-adapter.ts +123 -0
  86. package/lib/cloud-credential-host.ts +512 -0
  87. package/lib/cloud-credential-leases.ts +2587 -0
  88. package/lib/cloud-credential-runtime.ts +1357 -0
  89. package/lib/cloud-state.ts +56 -0
  90. package/lib/codex-auth.ts +456 -0
  91. package/lib/command-db.ts +310 -0
  92. package/lib/compose-network-recovery.ts +283 -0
  93. package/lib/connection-secret-redaction.ts +25 -0
  94. package/lib/container-runtime.ts +1470 -0
  95. package/lib/credential-cleanup-inventory.ts +490 -0
  96. package/lib/credential-consumers/engine-anchor.ts +1880 -0
  97. package/lib/credential-consumers/engines.ts +797 -0
  98. package/lib/credential-consumers/files.ts +934 -0
  99. package/lib/credential-consumers/github.ts +702 -0
  100. package/lib/credential-consumers/mcp.ts +389 -0
  101. package/lib/credential-consumers/task-files.ts +127 -0
  102. package/lib/credential-consumers/task-secrets.ts +10 -0
  103. package/lib/credential-store-pickup.ts +149 -0
  104. package/lib/credential-store-scope.ts +112 -0
  105. package/lib/credential-store.ts +932 -0
  106. package/lib/credentials-activation-authority.ts +709 -0
  107. package/lib/credentials-purge.ts +1006 -0
  108. package/lib/custom-connection-capture.ts +395 -0
  109. package/lib/custom-connection-launch.ts +384 -0
  110. package/lib/custom-connection-runtime.ts +141 -0
  111. package/lib/db.ts +80 -0
  112. package/lib/disk-pressure.ts +42 -0
  113. package/lib/docker-exec.ts +156 -0
  114. package/lib/engine-accounts.ts +1970 -0
  115. package/lib/engine-connect.ts +321 -0
  116. package/lib/engine-credential-env.ts +52 -0
  117. package/lib/engine-login.ts +2933 -0
  118. package/lib/engines.ts +1342 -0
  119. package/lib/enrollment-state.ts +251 -0
  120. package/lib/env-file.ts +246 -0
  121. package/lib/env-schema.ts +937 -0
  122. package/lib/env.ts +195 -0
  123. package/lib/first-turn-receipts.ts +62 -0
  124. package/lib/fs-aliases.ts +49 -0
  125. package/lib/git-clone-credentials.ts +334 -0
  126. package/lib/git-diff.ts +530 -0
  127. package/lib/git-identity.ts +715 -0
  128. package/lib/github-git-auth.ts +207 -0
  129. package/lib/github-origin.ts +28 -0
  130. package/lib/github-tokens.ts +2439 -0
  131. package/lib/host-cloud-url.ts +62 -0
  132. package/lib/host-config.ts +279 -0
  133. package/lib/host-env.ts +424 -0
  134. package/lib/host-logs.ts +965 -0
  135. package/lib/internal-connections.ts +5 -0
  136. package/lib/keyed-promise-tail.ts +23 -0
  137. package/lib/launch-mode.ts +25 -0
  138. package/lib/lease.ts +239 -0
  139. package/lib/legacy-host.ts +769 -0
  140. package/lib/legacy-runtime-retirement.ts +424 -0
  141. package/lib/legacy-runtime-v1.fixture.ts +627 -0
  142. package/lib/machine-exec.ts +238 -0
  143. package/lib/machine-gateway-tunnel.ts +344 -0
  144. package/lib/machine-keys.ts +65 -0
  145. package/lib/machine-project-env-loader.ts +152 -0
  146. package/lib/machine-provider-aws.ts +923 -0
  147. package/lib/machine-provider-local.ts +338 -0
  148. package/lib/machine-provider.ts +90 -0
  149. package/lib/managed-activation-watcher.ts +72 -0
  150. package/lib/managed-install-owner-watcher.ts +55 -0
  151. package/lib/managed-operation-drain.ts +57 -0
  152. package/lib/managed-runtime.ts +4296 -0
  153. package/lib/managed-update-scheduler.ts +125 -0
  154. package/lib/mcp-capture.ts +865 -0
  155. package/lib/mcp-config-lock.ts +2 -0
  156. package/lib/mcp-connections.ts +959 -0
  157. package/lib/mcp-gateway.ts +2594 -0
  158. package/lib/obs.ts +512 -0
  159. package/lib/openrouter-budget.ts +247 -0
  160. package/lib/orchestrator.ts +11409 -0
  161. package/lib/person-capture.ts +327 -0
  162. package/lib/preview-ports.ts +85 -0
  163. package/lib/preview-sidecar.ts +331 -0
  164. package/lib/project-env-authority.ts +1319 -0
  165. package/lib/project-env-feature.ts +74 -0
  166. package/lib/project-env-legacy-tags.ts +130 -0
  167. package/lib/project-env-loader.ts +242 -0
  168. package/lib/project-env-replica.ts +2094 -0
  169. package/lib/project-env-schema-capture.ts +342 -0
  170. package/lib/project-env-staging.ts +148 -0
  171. package/lib/project-env-task-policy.ts +176 -0
  172. package/lib/project-env-task-replica.ts +1752 -0
  173. package/lib/release-feed.ts +61 -0
  174. package/lib/release-manifest.ts +710 -0
  175. package/lib/release-trust.ts +46 -0
  176. package/lib/repo-clone.ts +38 -0
  177. package/lib/routing/shadow.ts +261 -0
  178. package/lib/runner-boot.ts +10 -0
  179. package/lib/runtime-activation-tail.ts +279 -0
  180. package/lib/runtime-archive.ts +1216 -0
  181. package/lib/runtime-authority.ts +166 -0
  182. package/lib/runtime-guard.ts +36 -0
  183. package/lib/runtime-provider-state.ts +169 -0
  184. package/lib/runtime-reconnect.ts +86 -0
  185. package/lib/runtime-state.ts +1033 -0
  186. package/lib/safe-http.ts +364 -0
  187. package/lib/secrets-proxy/address.ts +95 -0
  188. package/lib/secrets-proxy/audit.ts +30 -0
  189. package/lib/secrets-proxy/ca-store.ts +200 -0
  190. package/lib/secrets-proxy/ca.ts +470 -0
  191. package/lib/secrets-proxy/container-steps.ts +138 -0
  192. package/lib/secrets-proxy/der.ts +273 -0
  193. package/lib/secrets-proxy/engine.ts +1633 -0
  194. package/lib/secrets-proxy/host.ts +93 -0
  195. package/lib/secrets-proxy/hostname.ts +57 -0
  196. package/lib/secrets-proxy/placeholders.ts +196 -0
  197. package/lib/secrets-proxy/policy.ts +150 -0
  198. package/lib/secrets-proxy/readiness.ts +148 -0
  199. package/lib/secrets-proxy/runtime.ts +582 -0
  200. package/lib/secrets-proxy/scrub.ts +559 -0
  201. package/lib/secrets-proxy/sidecar-view.ts +139 -0
  202. package/lib/secrets-proxy/task-scripts.ts +262 -0
  203. package/lib/secrets-proxy/task-trust.ts +605 -0
  204. package/lib/secrets-proxy/trust-watch.ts +80 -0
  205. package/lib/secrets.ts +71 -0
  206. package/lib/shared-files-s3.ts +976 -0
  207. package/lib/shared-files.ts +156 -0
  208. package/lib/sidecar-assign.ts +49 -0
  209. package/lib/sidecar-guard.ts +58 -0
  210. package/lib/sidecar-resume.ts +178 -0
  211. package/lib/sidecar.ts +69 -0
  212. package/lib/signin-box-credentials.ts +465 -0
  213. package/lib/signin-box.ts +114 -0
  214. package/lib/skill-index-example.fixture.ts +56 -0
  215. package/lib/skill-layout.ts +417 -0
  216. package/lib/skills.ts +489 -0
  217. package/lib/ssh.ts +401 -0
  218. package/lib/standard-image-cleanup.ts +117 -0
  219. package/lib/standard-image-pin.ts +207 -0
  220. package/lib/standard-image.ts +1697 -0
  221. package/lib/stopped-task-status-queue.ts +120 -0
  222. package/lib/task-container-cli.ts +269 -0
  223. package/lib/task-credential-cleanup.ts +752 -0
  224. package/lib/task-diff-sandbox.ts +464 -0
  225. package/lib/task-diff.ts +91 -0
  226. package/lib/task-env-forwarder.ts +228 -0
  227. package/lib/task-env-source.ts +249 -0
  228. package/lib/task-environment/anchor.ts +31 -0
  229. package/lib/task-environment/apple-container.ts +860 -0
  230. package/lib/task-environment/docker.ts +1344 -0
  231. package/lib/task-environment/index.ts +466 -0
  232. package/lib/task-environment/legacy-adoption.ts +467 -0
  233. package/lib/task-environment/machine-environment.ts +61 -0
  234. package/lib/task-environment/machine-task-up.ts +1227 -0
  235. package/lib/task-environment/machine.ts +974 -0
  236. package/lib/task-environment/registry.ts +59 -0
  237. package/lib/task-environment/self-identity.ts +20 -0
  238. package/lib/task-environment/self.ts +566 -0
  239. package/lib/task-environment/types.ts +642 -0
  240. package/lib/task-environment/workspace-files.ts +685 -0
  241. package/lib/task-environment/workspace-runtime.fixture.ts +50 -0
  242. package/lib/task-files-machine-retirement-outbox.ts +592 -0
  243. package/lib/task-files-machine-retirement.ts +116 -0
  244. package/lib/task-files-readiness.ts +127 -0
  245. package/lib/task-gateway-port.ts +22 -0
  246. package/lib/task-gateway-protocol.ts +190 -0
  247. package/lib/task-gateway-recovery.ts +47 -0
  248. package/lib/task-gateway-state.ts +512 -0
  249. package/lib/task-identity.ts +19 -0
  250. package/lib/task-inventory.ts +682 -0
  251. package/lib/task-machine-credentials-activation.ts +95 -0
  252. package/lib/task-machine-swap-safety.ts +729 -0
  253. package/lib/task-person-credentials.ts +76 -0
  254. package/lib/task-project-ops-host.ts +147 -0
  255. package/lib/task-project-ops.ts +1062 -0
  256. package/lib/task-secret-staging.ts +16 -0
  257. package/lib/task-status.ts +46 -0
  258. package/lib/task-token.ts +46 -0
  259. package/lib/token-broker-adopt.ts +344 -0
  260. package/lib/token-broker-lambda.ts +105 -0
  261. package/lib/token-broker-local.ts +392 -0
  262. package/lib/token-broker-revision.ts +310 -0
  263. package/lib/token-broker-ssm.ts +525 -0
  264. package/lib/token-broker.ts +218 -0
  265. package/lib/token-lanes/openai-oauth.ts +123 -0
  266. package/lib/token-lanes/registry.ts +45 -0
  267. package/lib/token-lanes/types.ts +163 -0
  268. package/lib/token-lanes/xai-oauth.ts +165 -0
  269. package/lib/transcript.ts +128 -0
  270. package/lib/tunnel-registry.ts +220 -0
  271. package/lib/tunnel-runtime.ts +235 -0
  272. package/lib/ulid.ts +16 -0
  273. package/lib/user-bus-env.ts +116 -0
  274. package/lib/user-credentials-store.ts +435 -0
  275. package/lib/vnode-pressure.ts +142 -0
  276. package/package.json +115 -8
  277. package/runner/runner.mjs +442 -0
  278. package/scripts/agent/_common.sh +633 -0
  279. package/scripts/agent/env-hook.sh +626 -0
  280. package/scripts/agent/moondeck-cli.sh +5 -0
  281. package/scripts/agent/task-down.sh +291 -0
  282. package/scripts/agent/task-status.sh +70 -0
  283. package/scripts/agent/task-up.sh +3761 -0
  284. package/scripts/agent/uai-env-exec.sh +26 -0
  285. package/scripts/install/darwin.ts +1048 -0
  286. package/scripts/install/linux.ts +958 -0
  287. package/scripts/install/types.ts +78 -0
  288. package/scripts/install/util.ts +254 -0
  289. package/scripts/install/win.ts +143 -0
  290. package/src/activation-respawn.ts +155 -0
  291. package/src/apple-tunnel-route.ts +104 -0
  292. package/src/bridge-diagnostics.ts +39 -0
  293. package/src/capture-wire.ts +102 -0
  294. package/src/cli.ts +2585 -0
  295. package/src/cloud-credentials.ts +2144 -0
  296. package/src/cloud-origins.ts +23 -0
  297. package/src/credential-location.ts +248 -0
  298. package/src/credential-wire-validation.ts +25 -0
  299. package/src/custom-connections.ts +237 -0
  300. package/src/engine-account-paths.ts +11 -0
  301. package/src/engine-models.ts +39 -0
  302. package/src/engine-usage.ts +104 -0
  303. package/src/enrollment-next-steps.ts +24 -0
  304. package/src/event-outbox.ts +185 -0
  305. package/src/index.ts +2907 -0
  306. package/src/load-env.ts +97 -0
  307. package/src/machine-tunnel-route.ts +156 -0
  308. package/src/main.ts +6363 -0
  309. package/src/never-proxied-domains.ts +31 -0
  310. package/src/outbound-frame.ts +31 -0
  311. package/src/paths.ts +81 -0
  312. package/src/project-env-latest.ts +130 -0
  313. package/src/project-env-policy.ts +396 -0
  314. package/src/project-env-wording.ts +53 -0
  315. package/src/protocol.ts +5550 -0
  316. package/src/runtime-bootstrap.ts +165 -0
  317. package/src/station-identity.ts +96 -0
  318. package/src/task-env-names.ts +28 -0
  319. package/src/task-secrets.ts +75 -0
  320. package/src/tunnel-dial.ts +102 -0
  321. package/src/ui/server.ts +852 -0
  322. package/src/ui/types.ts +173 -0
  323. package/src/workspace-path-policy.ts +41 -0
  324. package/ui/app.js +1102 -0
  325. package/ui/index.html +90 -0
  326. package/ui/style.css +685 -0
  327. package/ui/uai-favicon.svg +21 -0
  328. package/ui/uai-logo-black.svg +9 -0
  329. package/ui/uai-wheel.svg +9 -0
  330. package/bin/moondeck-station.mjs +0 -3
@@ -0,0 +1,466 @@
1
+ import {
2
+ agent,
3
+ type TaskDownResult,
4
+ type TaskLaunchInput,
5
+ type TaskUpCredentials,
6
+ type TaskUpResult,
7
+ } from "../agent";
8
+ import { RUNTIME_RECHECK_COMMAND, pinnedContainerRuntimeMachineIdentity } from "../container-runtime";
9
+ import { getDb, schema, type HostTask } from "../db";
10
+ import { taskDir } from "../env";
11
+ import {
12
+ APPLE_TASK_ENVIRONMENT_PROVIDER,
13
+ type AppleTaskEnvironmentRecoveryDriver,
14
+ createAppleContainerTaskEnvironmentProvider,
15
+ parseAppleTaskEnvironmentLocator,
16
+ type AppleMachineIdentity,
17
+ } from "./apple-container";
18
+ import {
19
+ createDockerTaskEnvironmentProvider,
20
+ DOCKER_TASK_ENVIRONMENT_PROVIDER,
21
+ parseDockerTaskEnvironmentLocator,
22
+ type DockerTaskEnvironmentRecoveryDriver,
23
+ type DockerMachineIdentity,
24
+ } from "./docker";
25
+ import { createAwsMachineProvider } from "../machine-provider-aws";
26
+ import { createLocalMachineProvider } from "../machine-provider-local";
27
+ import { ensureMachineKeyPair } from "../machine-keys";
28
+ import { requestAccessToken } from "../github-tokens";
29
+ import {
30
+ createMachineTaskEnvironmentProvider,
31
+ } from "./machine";
32
+ import { createMachineHostTaskEnvironmentProvider } from "./machine-task-up";
33
+ import { TaskEnvironmentRegistry } from "./registry";
34
+ import {
35
+ parseTaskEnvironmentLocator,
36
+ type ProjectEnvMode,
37
+ type TaskEnvironmentHandle,
38
+ type TaskEnvironmentLocator,
39
+ type TaskEnvironmentProvisioned,
40
+ isEnvironmentTransportProvider,
41
+ } from "./types";
42
+
43
+ export {
44
+ adoptLegacyTaskEnvironments,
45
+ type LegacyTaskEnvironmentAdoptionDeps,
46
+ type LegacyTaskEnvironmentAdoptionVerdict,
47
+ } from "./legacy-adoption";
48
+ import { sidecarMode } from "../sidecar";
49
+ import { mountSharedFilesFromLease } from "../shared-files-s3";
50
+ import { createSelfTaskEnvironmentProvider } from "./self";
51
+ import { hostTaskEnvMode } from "../runtime-state";
52
+
53
+ const registry = new TaskEnvironmentRegistry<
54
+ TaskLaunchInput,
55
+ TaskUpCredentials,
56
+ TaskUpResult,
57
+ TaskDownResult
58
+ >();
59
+
60
+ let dockerRecoveryDriver: DockerTaskEnvironmentRecoveryDriver | null = null;
61
+
62
+ const dockerProvider = createDockerTaskEnvironmentProvider({
63
+ machineIdentity: requireDockerMachineIdentity,
64
+ taskUp: agent.taskUp,
65
+ taskEnvironmentDown: agent.taskEnvironmentDown,
66
+ recoveryDriver: () => dockerRecoveryDriver,
67
+ });
68
+
69
+ registry.register(dockerProvider);
70
+
71
+ /** Machine identity union across every registered backend. */
72
+ export type HostMachineIdentity = DockerMachineIdentity | AppleMachineIdentity;
73
+
74
+ export interface AppleContainerBinding {
75
+ /** Absolute path of the authenticated bundled `container` CLI. */
76
+ containerCliPath: () => string;
77
+ machineIdentity: () => AppleMachineIdentity;
78
+ }
79
+
80
+ let appleBinding: AppleContainerBinding | null = null;
81
+
82
+ /** Bind the Apple container provider to the authenticated bundle + minted
83
+ * installation identity. Selection wiring installs this exactly when the
84
+ * apple-container backend is active; a null binding keeps reconstruction of
85
+ * apple locators fail-closed. */
86
+ export function setAppleContainerBinding(
87
+ binding: AppleContainerBinding | null,
88
+ ): void {
89
+ appleBinding = binding;
90
+ }
91
+
92
+ function requireAppleBinding(): AppleContainerBinding {
93
+ if (!appleBinding) {
94
+ throw new Error("the apple-container runtime is not active on this host");
95
+ }
96
+ return appleBinding;
97
+ }
98
+
99
+ let appleRecoveryDriver: AppleTaskEnvironmentRecoveryDriver | null = null;
100
+
101
+ /** Bind the Apple provider's recovery implementation (orchestrator-owned),
102
+ * mirroring the Docker seam so HMR reloads cannot strand stale handles. */
103
+ export function setAppleTaskEnvironmentRecoveryDriver(
104
+ driver: AppleTaskEnvironmentRecoveryDriver,
105
+ ): void {
106
+ appleRecoveryDriver = driver;
107
+ }
108
+
109
+ const appleProvider = createAppleContainerTaskEnvironmentProvider<
110
+ TaskLaunchInput,
111
+ TaskUpCredentials,
112
+ TaskUpResult
113
+ >({
114
+ containerCliPath: () => requireAppleBinding().containerCliPath(),
115
+ machineIdentity: () => requireAppleBinding().machineIdentity(),
116
+ taskEnvironmentDown: agent.taskEnvironmentDown,
117
+ recoveryDriver: () => appleRecoveryDriver,
118
+ provisioning: {
119
+ // The runtime pin published UAI_TASK_RUNTIME / UAI_CONTAINER_CLI, so the
120
+ // same task-up entrypoint drives the Apple CLI instead of Compose.
121
+ taskUp: agent.taskUp,
122
+ inputTaskId: (input) => input.task.id,
123
+ hostWorktreePath: (taskId) => taskDir(taskId),
124
+ resultWorktreePath: (result) => result.worktreePath,
125
+ },
126
+ });
127
+
128
+ registry.register(appleProvider);
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // ADR-121: machine-backed tasks (ephemeral VM per task). Opt-in via
132
+ // UAI_MACHINE_TASKS=1 — the guinea-pig flag; selection stays coarse (whole
133
+ // host) until machine-backing becomes a per-task property.
134
+ // ---------------------------------------------------------------------------
135
+
136
+ export function machineTasksEnabled(): boolean {
137
+ return process.env.UAI_MACHINE_TASKS === "1";
138
+ }
139
+
140
+ function machineBackend() {
141
+ if (process.env.UAI_MACHINE_PROVIDER === "aws") {
142
+ const region = process.env.UAI_AWS_REGION;
143
+ if (!region) {
144
+ throw new Error("UAI_MACHINE_PROVIDER=aws requires UAI_AWS_REGION");
145
+ }
146
+ const diskGiB = Number(process.env.UAI_MACHINE_DISK_GIB ?? "");
147
+ return createAwsMachineProvider({
148
+ region,
149
+ subnetId: process.env.UAI_AWS_SUBNET_ID,
150
+ securityGroupId: process.env.UAI_AWS_SECURITY_GROUP_ID,
151
+ iamInstanceProfileArn: process.env.UAI_AWS_INSTANCE_PROFILE_ARN,
152
+ ...(Number.isInteger(diskGiB) && diskGiB > 0 ? { diskGiB } : {}),
153
+ });
154
+ }
155
+ return createLocalMachineProvider();
156
+ }
157
+
158
+ const machineEnvironmentProvider = createMachineTaskEnvironmentProvider({
159
+ machines: machineBackend(),
160
+ taskControlDir: (taskId) => taskDir(taskId),
161
+ mintKeyPair: ensureMachineKeyPair,
162
+ });
163
+
164
+ const machineProvider = createMachineHostTaskEnvironmentProvider({
165
+ environment: machineEnvironmentProvider,
166
+ taskControlDir: (taskId) => taskDir(taskId),
167
+ machineImage: () => process.env.UAI_MACHINE_IMAGE ?? "uai-machine:dev",
168
+ machineCpus: () => Number(process.env.UAI_MACHINE_CPUS ?? "2"),
169
+ machineMemoryMiB: () => Number(process.env.UAI_MACHINE_MEMORY_MIB ?? "4096"),
170
+ // Local machines on macOS need loopback-published ssh (bridge IPs are not
171
+ // host-routable there); cloud machines and Linux dial the address directly.
172
+ publishSsh: () =>
173
+ process.env.UAI_MACHINE_PROVIDER !== "aws" && process.platform === "darwin",
174
+ githubToken: (userId) => requestAccessToken(userId),
175
+ });
176
+
177
+ registry.register(machineProvider);
178
+
179
+ // ADR-133 sidecar mode: the environment is this machine. Same task-up on top
180
+ // (clone, editor, browser), a local-exec handle underneath; shared files
181
+ // are mounted from S3 rather than copied from a host directory.
182
+ const selfProvider = createMachineHostTaskEnvironmentProvider({
183
+ environment: createSelfTaskEnvironmentProvider(),
184
+ taskControlDir: (taskId) => taskDir(taskId),
185
+ machineImage: () => "self",
186
+ machineCpus: () => 0,
187
+ machineMemoryMiB: () => 0,
188
+ publishSsh: () => false,
189
+ githubToken: (userId) => requestAccessToken(userId),
190
+ sharedFiles: mountSharedFilesFromLease,
191
+ });
192
+
193
+ registry.register(selfProvider);
194
+
195
+ /**
196
+ * Bind the Docker provider's private recovery implementation. Kept as a
197
+ * replaceable module seam so HMR can reload the orchestrator without leaving
198
+ * handles captured to an obsolete implementation.
199
+ */
200
+ export function setDockerTaskEnvironmentRecoveryDriver(
201
+ driver: DockerTaskEnvironmentRecoveryDriver,
202
+ ): void {
203
+ dockerRecoveryDriver = driver;
204
+ }
205
+
206
+ export function provisionTaskEnvironment(
207
+ input: TaskLaunchInput,
208
+ credentials: TaskUpCredentials = {},
209
+ onPrepared?: (locator: TaskEnvironmentLocator) => Promise<void>,
210
+ ): Promise<TaskEnvironmentProvisioned<TaskUpResult, TaskDownResult>> {
211
+ const provider = sidecarMode()
212
+ ? selfProvider
213
+ : machineTasksEnabled()
214
+ ? machineProvider
215
+ : requireMachineIdentity().backend === "apple-container"
216
+ ? appleProvider
217
+ : dockerProvider;
218
+ return provider.provision({
219
+ taskId: input.task.id,
220
+ envMode: "loader",
221
+ input,
222
+ credentials,
223
+ onPrepared,
224
+ });
225
+ }
226
+
227
+ export function reconstructTaskEnvironment(
228
+ locator: TaskEnvironmentLocator,
229
+ envMode: ProjectEnvMode,
230
+ ): Promise<TaskEnvironmentHandle<TaskDownResult>> {
231
+ return registry.reconstruct(locator, envMode);
232
+ }
233
+
234
+ export async function reconstructPersistedTaskEnvironment(
235
+ task: HostTask,
236
+ ): Promise<TaskEnvironmentHandle<TaskDownResult> | null> {
237
+ // ADR-133: a machine or sidecar row carries its own locator and never
238
+ // needs the container runtime's identity — on a task machine there is
239
+ // none, and demanding it here would strand every session after task-up.
240
+ const locator = persistedTaskEnvironmentLocator(
241
+ task,
242
+ isEnvironmentTransportProvider(task.environmentProvider)
243
+ ? null
244
+ : requireMachineIdentity(),
245
+ );
246
+ if (locator === null) return null;
247
+ const handle = await registry.reconstruct(locator, hostTaskEnvMode(task));
248
+ if (handle.descriptor.taskId !== task.taskId) {
249
+ throw new Error("task environment locator belongs to a different task");
250
+ }
251
+ return handle;
252
+ }
253
+
254
+ /**
255
+ * Provider metadata is authoritative. Legacy resource rows must first cross
256
+ * the read-only adoption proof; reconstruction never auto-binds them merely
257
+ * because the currently-selected endpoint happens to answer.
258
+ */
259
+ export function persistedTaskEnvironmentLocator(
260
+ task: Pick<
261
+ HostTask,
262
+ | "taskId"
263
+ | "composeProject"
264
+ | "worktreePath"
265
+ | "environmentProvider"
266
+ | "environmentLocator"
267
+ | "codeServerPort"
268
+ | "previewPorts"
269
+ >,
270
+ /** Null only for rows whose provider is reached over the environment
271
+ * transport (machine, self): their locator is self-contained. */
272
+ machine: HostMachineIdentity | null,
273
+ ): TaskEnvironmentLocator | null {
274
+ const provider = task.environmentProvider;
275
+ const raw = task.environmentLocator;
276
+ if ((provider === null) !== (raw === null)) {
277
+ throw new Error("task environment persistence is incomplete");
278
+ }
279
+ if (provider !== null && raw !== null) {
280
+ const locator = parseTaskEnvironmentLocator(raw);
281
+ if (locator.provider !== provider) {
282
+ throw new Error("task environment provider does not match its locator");
283
+ }
284
+ return locator;
285
+ }
286
+ if (task.composeProject === null && task.worktreePath === null) return null;
287
+ if (task.composeProject === null || task.worktreePath === null) {
288
+ throw new Error("legacy task runtime persistence is incomplete");
289
+ }
290
+ if (machine === null) {
291
+ throw new Error("legacy task runtime cannot be adopted on a task machine");
292
+ }
293
+ throw new Error(
294
+ machine.backend === "docker"
295
+ ? "legacy Docker resources have not passed safe adoption"
296
+ : `legacy task runtime belongs to ambient Docker and cannot move to ${machine.backend}`,
297
+ );
298
+ }
299
+
300
+ export interface TaskEnvironmentBackendVerdict {
301
+ ok: boolean;
302
+ detail?: string;
303
+ }
304
+
305
+ /**
306
+ * Machine/provider switch fence used before provisioning or maintenance. Any
307
+ * row that still names runtime resources belongs to exactly one backend. An
308
+ * unknown/newer provider or endpoint mismatch is a stop signal, never a hint
309
+ * to probe/mutate the currently-selected daemon.
310
+ */
311
+ /** Extract the (backend, endpoint, engineId) triple a locator was pinned to,
312
+ * dispatching by provider. An unknown/newer provider throws. */
313
+ export function locatorMachineIdentity(
314
+ locator: TaskEnvironmentLocator,
315
+ ): HostMachineIdentity {
316
+ if (locator.provider === DOCKER_TASK_ENVIRONMENT_PROVIDER) {
317
+ return parseDockerTaskEnvironmentLocator(locator).machine;
318
+ }
319
+ if (locator.provider === APPLE_TASK_ENVIRONMENT_PROVIDER) {
320
+ return parseAppleTaskEnvironmentLocator(locator).machine;
321
+ }
322
+ throw new Error(
323
+ `belongs to unavailable environment provider ${locator.provider}`,
324
+ );
325
+ }
326
+
327
+ export function persistedTaskEnvironmentsMatchMachine(
328
+ tasks: ReadonlyArray<
329
+ Pick<
330
+ HostTask,
331
+ | "taskId"
332
+ | "composeProject"
333
+ | "worktreePath"
334
+ | "environmentProvider"
335
+ | "environmentLocator"
336
+ | "codeServerPort"
337
+ | "previewPorts"
338
+ >
339
+ >,
340
+ machine: HostMachineIdentity,
341
+ ): TaskEnvironmentBackendVerdict {
342
+ for (const task of tasks) {
343
+ let locator: TaskEnvironmentLocator | null;
344
+ try {
345
+ locator = persistedTaskEnvironmentLocator(task, machine);
346
+ } catch (error) {
347
+ return {
348
+ ok: false,
349
+ detail: `task ${task.taskId}: ${boundedMessage(error)}`,
350
+ };
351
+ }
352
+ if (locator === null) continue;
353
+ // ADR-121: machine locators name an ephemeral VM, not a container
354
+ // daemon — no container-runtime identity claim to verify.
355
+ if (isEnvironmentTransportProvider(locator.provider)) continue;
356
+ try {
357
+ const persisted = locatorMachineIdentity(locator);
358
+ if (
359
+ persisted.backend !== machine.backend ||
360
+ persisted.endpoint !== machine.endpoint ||
361
+ persisted.engineId !== machine.engineId
362
+ ) {
363
+ return {
364
+ ok: false,
365
+ detail: `task ${task.taskId} belongs to a different container backend`,
366
+ };
367
+ }
368
+ } catch (error) {
369
+ return {
370
+ ok: false,
371
+ detail: `task ${task.taskId}: ${boundedMessage(error)}`,
372
+ };
373
+ }
374
+ }
375
+ return { ok: true };
376
+ }
377
+
378
+ export function hostTaskEnvironmentsMatchMachine(
379
+ machine: HostMachineIdentity,
380
+ ): TaskEnvironmentBackendVerdict {
381
+ const rows = getDb().select().from(schema.hostTasks).all();
382
+ return persistedTaskEnvironmentsMatchMachine(rows, machine);
383
+ }
384
+
385
+ export interface DockerMachineSelection {
386
+ backend: HostMachineIdentity["backend"];
387
+ endpoint: string;
388
+ }
389
+
390
+ /** Pre-provision provider fence. The engine identity is not knowable until
391
+ * the backend is activated, so this guard proves only that no durable row
392
+ * belongs to another provider/endpoint. The exact engine-ID fence runs
393
+ * immediately after the atomic daemon probe and before maintenance or
394
+ * recovery. */
395
+ export function hostTaskEnvironmentsAllowMachineSelection(
396
+ machine: DockerMachineSelection,
397
+ ): TaskEnvironmentBackendVerdict {
398
+ const tasks = getDb().select().from(schema.hostTasks).all();
399
+ for (const task of tasks) {
400
+ if (
401
+ (task.environmentProvider === null) !==
402
+ (task.environmentLocator === null)
403
+ ) {
404
+ return {
405
+ ok: false,
406
+ detail: `task ${task.taskId}: task environment persistence is incomplete`,
407
+ };
408
+ }
409
+ if (task.environmentProvider === null) {
410
+ if (task.composeProject === null && task.worktreePath === null) continue;
411
+ return {
412
+ ok: false,
413
+ detail:
414
+ `task ${task.taskId} has legacy ambient-Docker resources; ` +
415
+ `start that Docker daemon and run \`${RUNTIME_RECHECK_COMMAND}\` ` +
416
+ "before selecting another container runtime",
417
+ };
418
+ }
419
+ try {
420
+ const locator = parseTaskEnvironmentLocator(task.environmentLocator!);
421
+ if (locator.provider !== task.environmentProvider) {
422
+ throw new Error("task environment provider does not match its locator");
423
+ }
424
+ // ADR-121: machine rows make no container-runtime claim.
425
+ if (isEnvironmentTransportProvider(locator.provider)) continue;
426
+ const persisted = locatorMachineIdentity(locator);
427
+ if (
428
+ persisted.backend !== machine.backend ||
429
+ persisted.endpoint !== machine.endpoint
430
+ ) {
431
+ throw new Error("belongs to a different container backend");
432
+ }
433
+ } catch (error) {
434
+ return {
435
+ ok: false,
436
+ detail: `task ${task.taskId}: ${boundedMessage(error)}`,
437
+ };
438
+ }
439
+ }
440
+ return { ok: true };
441
+ }
442
+
443
+ function requireMachineIdentity(): HostMachineIdentity {
444
+ // The PIN, not the operational-gated identity: reconstruction must serve
445
+ // teardown while the runtime is still `checking` (the 2026-08-16 ring).
446
+ // Locator comparison still demands an exact backend/endpoint/engine match,
447
+ // so nothing loosens about WHICH machine may own a row.
448
+ const identity = pinnedContainerRuntimeMachineIdentity();
449
+ if (identity === null) {
450
+ throw new Error("container runtime does not have a durable machine identity");
451
+ }
452
+ return identity;
453
+ }
454
+
455
+ /** The Docker provider must never operate on another backend's identity. */
456
+ function requireDockerMachineIdentity(): DockerMachineIdentity {
457
+ const identity = requireMachineIdentity();
458
+ if (identity.backend !== "docker") {
459
+ throw new Error("the Docker task backend is not active on this host");
460
+ }
461
+ return identity;
462
+ }
463
+
464
+ function boundedMessage(error: unknown): string {
465
+ return (error instanceof Error ? error.message : String(error)).slice(0, 512);
466
+ }