@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,173 @@
1
+ /**
2
+ * Zod schemas for every local JSON API response (ADR-028, docs/host-ui.md).
3
+ *
4
+ * The server parses each response body through these before sending, so the
5
+ * API can never emit an off-contract shape. This file is the single source of
6
+ * truth for the wire shapes; the static UI assumes them.
7
+ */
8
+
9
+ import { z } from "zod";
10
+
11
+ const RuntimePreference = z.enum(["auto", "docker", "apple-container"]);
12
+ export const ContainerRuntimeResponse = z.union([
13
+ z
14
+ .object({
15
+ status: z.literal("checking"),
16
+ preference: RuntimePreference,
17
+ detail: z.string().trim().min(1).max(300).optional(),
18
+ })
19
+ .strict(),
20
+ z.union([
21
+ z
22
+ .object({
23
+ status: z.literal("ready"),
24
+ preference: z.enum(["auto", "docker"]),
25
+ provider: z.literal("docker"),
26
+ })
27
+ .strict(),
28
+ z
29
+ .object({
30
+ status: z.literal("ready"),
31
+ preference: z.enum(["auto", "apple-container"]),
32
+ provider: z.literal("apple-container"),
33
+ })
34
+ .strict(),
35
+ ]),
36
+ z
37
+ .object({
38
+ status: z.literal("no-runtime"),
39
+ preference: RuntimePreference,
40
+ message: z.string().trim().min(1).max(1_000),
41
+ action: z.string().trim().min(1).max(200),
42
+ })
43
+ .strict(),
44
+ ]);
45
+ export type ContainerRuntimeResponse = z.infer<typeof ContainerRuntimeResponse>;
46
+
47
+ // GET /api/status — process + config facts.
48
+ export const StatusResponse = z.object({
49
+ pid: z.number().int(),
50
+ uptime: z.number(), // seconds, process.uptime()
51
+ version: z.string(), // @runuai/host package version
52
+ logPath: z.string(), // service log file path (per-OS)
53
+ cloudUrl: z.string(), // UAI_CLOUD_URL
54
+ hostName: z.string(), // os.hostname()
55
+ hostId: z.string(), // UAI_HOST_ID
56
+ // Which agent adapters this host runs (UAI_AGENTS). Optional: a newer CLI
57
+ // can query a service that is still on the pre-upgrade build, which had no
58
+ // such field — `npm i -g` swaps the CLI before `uai-host restart` swaps the
59
+ // service, so that window is normal, not exotic.
60
+ agentMode: z.enum(["real", "mock"]).optional(),
61
+ containerRuntime: ContainerRuntimeResponse.optional(),
62
+ // The package that answers: "@moondeck/station". The frozen @runuai/host
63
+ // never reports it, so its presence alone says "a station" (legacy-host.ts:
64
+ // a station must never be taken for the old host on a shared port).
65
+ product: z.string().optional(),
66
+ });
67
+ export type StatusResponse = z.infer<typeof StatusResponse>;
68
+
69
+ // GET /api/cloud — live WSS connection state (in-memory, from the bridge).
70
+ export const CloudResponse = z.object({
71
+ state: z.enum(["connected", "reconnecting", "disconnected"]),
72
+ lastConnectedAt: z.number().nullable(), // epoch ms
73
+ reconnectCount: z.number().int(),
74
+ lastError: z.string().nullable(),
75
+ });
76
+ export type CloudResponse = z.infer<typeof CloudResponse>;
77
+
78
+ // One active task. Runtime fields come from host.sqlite; owner/slugs are
79
+ // persisted at task-up; memory is best-effort docker.
80
+ export const TaskRow = z.object({
81
+ taskId: z.string(),
82
+ status: z.string().nullable(), // statusMirror
83
+ ownerEmail: z.string().nullable(),
84
+ projectSlugs: z.array(z.string()),
85
+ startedAt: z.number().nullable(), // epoch ms
86
+ ageMs: z.number().nullable(), // now - startedAt
87
+ composeProject: z.string().nullable(),
88
+ codeServerPort: z.number().int().nullable(),
89
+ previewPorts: z.array(
90
+ z.object({ name: z.string(), hostPort: z.number().int().nullable() }),
91
+ ),
92
+ memoryBytes: z.number().nullable(), // docker stats, null on failure
93
+ });
94
+ export type TaskRow = z.infer<typeof TaskRow>;
95
+
96
+ export const TasksResponse = z.object({ tasks: z.array(TaskRow) });
97
+ export type TasksResponse = z.infer<typeof TasksResponse>;
98
+
99
+ // GET /api/events — last 50 host-recorded lifecycle events, newest first.
100
+ export const EventRow = z.object({
101
+ id: z.string(),
102
+ taskId: z.string(),
103
+ kind: z.enum(["task.created", "task.started", "task.ended", "task.ship"]),
104
+ ts: z.number(), // epoch ms
105
+ detail: z.string().nullable(),
106
+ });
107
+ export type EventRow = z.infer<typeof EventRow>;
108
+
109
+ export const EventsResponse = z.object({ events: z.array(EventRow) });
110
+ export type EventsResponse = z.infer<typeof EventsResponse>;
111
+
112
+ // GET /api/users — distinct owners across active tasks. v1: the single operator.
113
+ export const UserRow = z.object({
114
+ userId: z.string(),
115
+ email: z.string().nullable(),
116
+ activeTasks: z.number().int(),
117
+ });
118
+ export type UserRow = z.infer<typeof UserRow>;
119
+
120
+ export const UsersResponse = z.object({ users: z.array(UserRow) });
121
+ export type UsersResponse = z.infer<typeof UsersResponse>;
122
+
123
+ // GET /api/engines — the engine catalog + which are connected on this host.
124
+ export const EngineCatalogEntry = z.object({
125
+ kind: z.enum(["claude", "codex", "kimi", "grok", "cursor", "opencode"]),
126
+ label: z.string(),
127
+ authMode: z.enum([
128
+ "token-command",
129
+ "login-command",
130
+ "api-key",
131
+ "external-login",
132
+ ]),
133
+ notes: z.string().nullable(),
134
+ getKeyUrl: z.string().nullable(),
135
+ // Pasted-API-key alternative to the login/token flow (null = not offered).
136
+ apiKeyHint: z.string().nullable(),
137
+ apiKeyUrl: z.string().nullable(),
138
+ // Install command shown when the CLI is missing (null = nothing to install).
139
+ installHint: z.string().nullable(),
140
+ // external-login: the command the owner runs in their terminal (null else).
141
+ loginCmd: z.string().nullable(),
142
+ });
143
+ export type EngineCatalogEntry = z.infer<typeof EngineCatalogEntry>;
144
+
145
+ // One engine account (ADR-076) — secret-free descriptor for the host UI.
146
+ export const EngineAccountInfo = z.object({
147
+ id: z.string(),
148
+ kind: z.string(),
149
+ label: z.string(),
150
+ authKind: z.enum(["env", "config-dir"]),
151
+ isDefault: z.boolean(),
152
+ });
153
+ export type EngineAccountInfo = z.infer<typeof EngineAccountInfo>;
154
+
155
+ export const EnginesResponse = z.object({
156
+ catalog: z.array(EngineCatalogEntry),
157
+ statuses: z.record(z.boolean()), // kind → connected
158
+ cli: z.record(z.boolean()), // kind → CLI resolvable on this host
159
+ // kind → its accounts (default + extras), ADR-076. Kinds with no account
160
+ // model (kimi/grok/cursor) are absent.
161
+ accounts: z.record(z.array(EngineAccountInfo)),
162
+ });
163
+ export type EnginesResponse = z.infer<typeof EnginesResponse>;
164
+
165
+ // POST /api/engines/disconnect — the disconnect result.
166
+ export const EngineOpResponse = z.object({
167
+ ok: z.boolean(),
168
+ message: z.string().optional(),
169
+ });
170
+ export type EngineOpResponse = z.infer<typeof EngineOpResponse>;
171
+
172
+ export const ErrorResponse = z.object({ error: z.string() });
173
+ export type ErrorResponse = z.infer<typeof ErrorResponse>;
@@ -0,0 +1,41 @@
1
+ import { ENGINE_ACCOUNT_PATH_RULES } from "./engine-account-paths";
2
+ export { ENGINE_ACCOUNT_PATH_RULES } from "./engine-account-paths";
3
+ /** Shared by cloud validation and runner reads. Check normalized and lexical
4
+ * components: normalization must never erase a traversal through credentials. */
5
+ export function normalizedWorkspacePath(input: string): string {
6
+ if (!input.startsWith("/workspace/") || /[\x00-\x1f\x7f\\]/.test(input)) throw new Error("Use an absolute path under /workspace.");
7
+ const parts: string[] = [];
8
+ for (const part of input.split("/").slice(2)) {
9
+ if (!part || part === ".") continue;
10
+ if (part === "..") { if (!parts.length) throw new Error("Path escapes /workspace."); parts.pop(); }
11
+ else parts.push(part);
12
+ }
13
+ if (!parts.length) throw new Error("Choose a file under /workspace.");
14
+ return `/workspace/${parts.join("/")}`;
15
+ }
16
+ export function assertShareablePath(input: string): string { return validateShareablePath(input, ENGINE_ACCOUNT_PATH_RULES); }
17
+ /** Kept self-contained for embedding together with its data in the agent CLI. */
18
+ export function validateShareablePath(input: string, engineRules: ReadonlyArray<{ components: readonly string[]; prefix: boolean }>): string {
19
+ if (/[\x00-\x1f\x7f\\]/.test(input)) throw new Error("This path cannot be shared.");
20
+ if (!input.startsWith("/")) throw new Error("Use an absolute file path.");
21
+ const lexical = input.split("/").filter(Boolean);
22
+ const parts: string[] = [];
23
+ for (const part of lexical) {
24
+ if (part === "..") parts.pop(); else if (part !== ".") parts.push(part);
25
+ // Refuse entering these roots even when later '..' components erase them.
26
+ if (engineRules.some((rule) => parts.some((_, i) => rule.components.every((component, offset) => offset === rule.components.length - 1 && rule.prefix ? (parts[i + offset] ?? "").startsWith(component) : parts[i + offset] === component)))) throw new Error("Credential and Moondeck internal paths cannot be shared.");
27
+ if (["proc", "sys"].includes(parts[0] ?? "") || (parts[0] === "run" && ((parts[1] ?? "").startsWith("uai") || parts[1] === "secrets"))) throw new Error("Credential and Moondeck internal paths cannot be shared.");
28
+ }
29
+ const normalized = `/${parts.join("/")}`;
30
+ // Keep this function self-contained: its source is embedded into the
31
+ // dependency-free CLI. Inline callbacks need no compiler name helpers.
32
+ for (const components of [lexical, parts]) {
33
+ if (components.some((part, i) =>
34
+ (part === ".env" || (part.startsWith(".env.") && ![".env.example", ".env.schema"].includes(part))) ||
35
+ part.startsWith(".codex") ||
36
+ [".git", ".claude", ".ssh", ".grok", ".cursor", ".opencode", ".claude.json", ".git-credentials", ".netrc", ".npmrc", ".pypirc", ".aws", ".docker", ".kube", ".gnupg"].includes(part) ||
37
+ (part === ".config" && components[i + 1] === "gh") ||
38
+ ((part === ".uai" || part === ".moondeck") && components[i + 1] !== "attachments"))) throw new Error("Credential and Moondeck internal paths cannot be shared.");
39
+ }
40
+ return normalized;
41
+ }