@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,76 @@
1
+ /**
2
+ * Restore GitHub/SSH files when a running task's cloud credentials become
3
+ * usable, after recovery or a live change. The persisted provider locator
4
+ * is the only runtime authority; a missing runtime is a retryable failure.
5
+ * Fresh task-up uses the same writer on its newly provisioned handle.
6
+ */
7
+ import type { HostTask } from "../db/schema";
8
+ import { CredentialLeaseUnavailableError, type TaskAssignment } from "./cloud-credential-leases";
9
+ import { writeTaskPersonCredentials, type PersonCredentialLane } from "./credential-consumers/github";
10
+ import type { TaskWriteEnvironment } from "./credential-consumers/task-files";
11
+ import { getHostTask, readTaskRunEpochState } from "./runtime-state";
12
+ import { reconstructPersistedTaskEnvironment } from "./task-environment";
13
+
14
+ /**
15
+ * The composer retries a rejection while the same material is current and
16
+ * aborts this signal before cleanup or takeover. In particular, a fresh
17
+ * task that is still starting must retry: a rotation after its runtime-ready
18
+ * writes but before it is marked running must not be silently missed.
19
+ */
20
+ export async function reconcileRunningTaskPersonCredentials(
21
+ assignment: Readonly<TaskAssignment>,
22
+ lanes: readonly PersonCredentialLane[],
23
+ signal: AbortSignal,
24
+ ): Promise<void> {
25
+ const current = (): HostTask => {
26
+ signal.throwIfAborted();
27
+ const task = getHostTask(assignment.taskId);
28
+ const run = readTaskRunEpochState(assignment.taskId);
29
+ if (
30
+ task === null ||
31
+ task.statusMirror !== "running" ||
32
+ task.runEpoch !== assignment.runEpoch ||
33
+ run === null ||
34
+ !run.assignmentOpen ||
35
+ run.highestRunEpoch !== assignment.runEpoch ||
36
+ run.runtimeEstablishedEpoch !== assignment.runEpoch
37
+ ) {
38
+ throw new CredentialLeaseUnavailableError("the task's running credential assignment is not current");
39
+ }
40
+ return task;
41
+ };
42
+ const task = current();
43
+ const assertCurrent = (): void => {
44
+ const live = current();
45
+ if (
46
+ live.environmentProvider !== task.environmentProvider ||
47
+ live.environmentLocator !== task.environmentLocator ||
48
+ live.envMode !== task.envMode
49
+ ) {
50
+ throw new CredentialLeaseUnavailableError("the task's credential runtime has changed");
51
+ }
52
+ };
53
+ const environment = await reconstructPersistedTaskEnvironment(task);
54
+ assertCurrent();
55
+ if (environment === null) {
56
+ throw new CredentialLeaseUnavailableError("the task's credential runtime is unavailable");
57
+ }
58
+ // Keep the exact running assignment and cancellation at each provider's
59
+ // last exec boundary too: reconstruction or address lookup can suspend
60
+ // while Stop or a new run replaces the runtime.
61
+ const writable: TaskWriteEnvironment = {
62
+ descriptor: environment.descriptor,
63
+ exec: (request) => {
64
+ assertCurrent();
65
+ return environment.exec({
66
+ ...request,
67
+ admit: () => {
68
+ assertCurrent();
69
+ request.admit?.();
70
+ },
71
+ });
72
+ },
73
+ };
74
+ await writeTaskPersonCredentials(writable, assignment, lanes, signal);
75
+ assertCurrent();
76
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * ADR-161 §2: the live-project commands wired to this runner's real state —
3
+ * the task's persisted environment, its run-epoch admission, its lifecycle
4
+ * slot, its lease grant and env replica, and its persisted env spec.
5
+ */
6
+
7
+ import { activeCloudCredentials, credentialsMode } from "./cloud-credential-host";
8
+ import { getOrchestrator } from "./orchestrator";
9
+ import {
10
+ getHostTask,
11
+ hostTaskProjectEnvSpec,
12
+ readTaskRunEpochState,
13
+ taskProjectEnvSpec,
14
+ upsertHostTask,
15
+ type TaskProjectEnvSpec,
16
+ } from "./runtime-state";
17
+ import { taskUaiDir } from "./env";
18
+ import { captureTaskEnvSchemaFromClone, taskEnvSchemaObservation } from "./project-env-schema-capture";
19
+ import { reconstructPersistedTaskEnvironment } from "./task-environment";
20
+ import { forgetTaskEnvSnapshot, taskEnvResolver } from "./task-env-source";
21
+ import { currentTaskGatewayRegistration, setTaskGatewayEnvResolver } from "./task-gateway-state";
22
+ import { TaskProjectJournalStore, TaskProjectOps } from "./task-project-ops";
23
+ import type { TaskCommandProject } from "../src/protocol";
24
+
25
+ function runIsCurrent(taskId: string, runEpoch: number): boolean {
26
+ const task = getHostTask(taskId);
27
+ if (!task || task.statusMirror !== "running") return false;
28
+ const state = readTaskRunEpochState(taskId);
29
+ // A task started before run epochs carries none; the cloud never sends
30
+ // these commands to it (it has no `live-task-projects-v1` contract).
31
+ return state !== null && state.assignmentOpen && state.highestRunEpoch === runEpoch;
32
+ }
33
+
34
+ /** Names the cloud's literal channel must never set live: the editor theme
35
+ * is the task's, chosen at start. */
36
+ const RESERVED_LITERAL_ENV = new Set(["UAI_EDITOR_THEME"]);
37
+
38
+ /**
39
+ * One project (and its preview URL literals) added to a persisted env spec.
40
+ * Every other entry stays; a literal name the task already has keeps its
41
+ * value, as at start where the first project to declare one wins.
42
+ */
43
+ export function specWithProject(
44
+ spec: TaskProjectEnvSpec,
45
+ project: TaskCommandProject,
46
+ previewEnv: Record<string, string> = {},
47
+ ): TaskProjectEnvSpec {
48
+ const hasProject = spec.projects.some((entry) => entry.projectId === project.id);
49
+ const addedLiterals = Object.entries(previewEnv).filter(
50
+ ([name]) => !RESERVED_LITERAL_ENV.has(name) && !(name in spec.previewEnv),
51
+ );
52
+ if (hasProject && addedLiterals.length === 0) return spec;
53
+ const [entry] = taskProjectEnvSpec({
54
+ projects: [project],
55
+ task: { previewEnv: {} } as Parameters<typeof taskProjectEnvSpec>[0]["task"],
56
+ }).projects;
57
+ return {
58
+ ...spec,
59
+ projects: hasProject
60
+ ? spec.projects
61
+ : [...spec.projects, entry!].sort(
62
+ (left, right) => left.position - right.position || left.projectId.localeCompare(right.projectId),
63
+ ),
64
+ previewEnv: Object.fromEntries(
65
+ [...Object.entries(spec.previewEnv), ...addedLiterals].sort(([left], [right]) => left.localeCompare(right)),
66
+ ),
67
+ };
68
+ }
69
+
70
+ function liveProjectsSupported(taskId: string): boolean {
71
+ const task = getHostTask(taskId);
72
+ if (!task) return false;
73
+ try {
74
+ return hostTaskProjectEnvSpec(task) !== null && currentTaskGatewayRegistration(taskId) !== null;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Persist the extended spec, then ALWAYS re-point the live resolver at the
82
+ * persisted spec, so a repeat repairs a resolver a crash left behind. Throws
83
+ * when the environment can't take it; Activate then answers `incomplete`.
84
+ */
85
+ function installProjectEnv(
86
+ taskId: string,
87
+ runEpoch: number,
88
+ project: TaskCommandProject,
89
+ previewEnv: Record<string, string>,
90
+ ): void {
91
+ const task = getHostTask(taskId);
92
+ if (!task) throw new Error(`no such task: ${taskId}`);
93
+ const spec = hostTaskProjectEnvSpec(task);
94
+ if (spec === null) throw new Error("this task has no persisted env spec to extend");
95
+ const registration = currentTaskGatewayRegistration(taskId);
96
+ if (!registration) throw new Error("this task has no live env gateway");
97
+ const next = specWithProject(spec, project, previewEnv);
98
+ if (next !== spec) upsertHostTask(taskId, { projectEnvSpec: JSON.stringify(next) });
99
+ forgetTaskEnvSnapshot(taskId);
100
+ if (!setTaskGatewayEnvResolver(taskId, registration.generation, taskEnvResolver(taskId, next, runEpoch))) {
101
+ throw new Error("the task's env gateway changed while the project was installed");
102
+ }
103
+ }
104
+
105
+ let singleton: TaskProjectOps | null = null;
106
+
107
+ export function taskProjectOps(): TaskProjectOps {
108
+ singleton ??= new TaskProjectOps({
109
+ journal: new TaskProjectJournalStore(taskUaiDir),
110
+ environment: async (taskId) => {
111
+ const task = getHostTask(taskId);
112
+ if (!task || task.statusMirror !== "running") return null;
113
+ return reconstructPersistedTaskEnvironment(task);
114
+ },
115
+ runIsCurrent,
116
+ inLifecycleSlot: (taskId, fn) => getOrchestrator().runTaskLifecycle(taskId, fn),
117
+ credentials: () => (credentialsMode() === "active" ? "cloud" : "own"),
118
+ installedGrant: (taskId, runEpoch) => {
119
+ const composer = activeCloudCredentials();
120
+ if (!composer) return null;
121
+ const view = composer.registry.currentView({ taskId, runEpoch });
122
+ return view
123
+ ? { runEpoch, manifestRevision: view.manifestRevision, manifestDigest: view.manifestDigest }
124
+ : null;
125
+ },
126
+ envServes: (taskId, runEpoch, projectId) => {
127
+ const composer = activeCloudCredentials();
128
+ // The runner's own store serves every project it holds values for.
129
+ if (!composer) return true;
130
+ const view = composer.registry.currentView({ taskId, runEpoch });
131
+ if (!view || !view.projectIds.includes(projectId)) return false;
132
+ return composer.envSnapshot({ taskId, runEpoch }, view.projectIds) !== null;
133
+ },
134
+ liveProjectsSupported,
135
+ captureEnvSchema: (taskId, projectId, git) => captureTaskEnvSchemaFromClone(taskId, projectId, git),
136
+ storedEnvSchema: (taskId, projectId) => taskEnvSchemaObservation(taskId, projectId),
137
+ servedProjectIds: (taskId, runEpoch) => {
138
+ const composer = activeCloudCredentials();
139
+ if (!composer) return null;
140
+ const view = composer.registry.currentView({ taskId, runEpoch });
141
+ if (!view) return null;
142
+ return composer.envSnapshot({ taskId, runEpoch }, view.projectIds) !== null ? [...view.projectIds] : null;
143
+ },
144
+ installProjectEnv,
145
+ });
146
+ return singleton;
147
+ }