@moondeck/station 0.0.0-stage → 1.0.0

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 (325) 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/meta/_journal.json +223 -0
  36. package/db/schema.ts +850 -0
  37. package/images/standard/Dockerfile +347 -0
  38. package/images/standard/README.md +167 -0
  39. package/images/standard/container/code-server-settings.json +63 -0
  40. package/images/standard/container/corepack-version +1 -0
  41. package/images/standard/container/uai-init +704 -0
  42. package/images/standard/container/uai-materialize-runtimes +1527 -0
  43. package/images/standard/container/uai-task-forwarder.mjs +303 -0
  44. package/images/standard/tool-versions +2 -0
  45. package/lib/__fixtures__/skill-index/SKILL.md +10 -0
  46. package/lib/__fixtures__/skill-index/skills/accessibility-checklist.md +7 -0
  47. package/lib/__fixtures__/skill-index/skills/brand-voice.md +9 -0
  48. package/lib/__fixtures__/skill-index/skills/design-tokens.md +9 -0
  49. package/lib/agent-cli.ts +1507 -0
  50. package/lib/agent-errors.ts +22 -0
  51. package/lib/agent.ts +672 -0
  52. package/lib/agents/attach-compatibility.ts +51 -0
  53. package/lib/agents/claude.ts +918 -0
  54. package/lib/agents/codex-token-usage.fixture.json +130 -0
  55. package/lib/agents/codex.ts +1392 -0
  56. package/lib/agents/cursor-period-usage.ts +253 -0
  57. package/lib/agents/cursor.ts +562 -0
  58. package/lib/agents/dispatch.ts +78 -0
  59. package/lib/agents/durable-proc.ts +334 -0
  60. package/lib/agents/factory.ts +57 -0
  61. package/lib/agents/grok.ts +381 -0
  62. package/lib/agents/kimi.ts +329 -0
  63. package/lib/agents/machine-durable.ts +604 -0
  64. package/lib/agents/mock.ts +133 -0
  65. package/lib/agents/mode.ts +64 -0
  66. package/lib/agents/opencode.ts +508 -0
  67. package/lib/agents/output-source.ts +3 -0
  68. package/lib/agents/proc.ts +221 -0
  69. package/lib/agents/rate-limit.ts +337 -0
  70. package/lib/agents/registry.ts +182 -0
  71. package/lib/agents/session-state.ts +197 -0
  72. package/lib/agents/skill-index-preamble.fixture.ts +26 -0
  73. package/lib/agents/transport.ts +741 -0
  74. package/lib/agents/types.ts +254 -0
  75. package/lib/agents/uai.ts +106 -0
  76. package/lib/agents/usage.ts +44 -0
  77. package/lib/apple-one-off.ts +277 -0
  78. package/lib/apple-runtime-recycle.ts +237 -0
  79. package/lib/apple-uninstall-teardown.ts +224 -0
  80. package/lib/assignment-evidence.ts +94 -0
  81. package/lib/attachments.ts +48 -0
  82. package/lib/browser-testing.ts +1537 -0
  83. package/lib/capture-flow.ts +332 -0
  84. package/lib/cloud-credential-cleanup-adapter.ts +123 -0
  85. package/lib/cloud-credential-host.ts +512 -0
  86. package/lib/cloud-credential-leases.ts +2587 -0
  87. package/lib/cloud-credential-runtime.ts +1357 -0
  88. package/lib/cloud-state.ts +56 -0
  89. package/lib/codex-auth.ts +456 -0
  90. package/lib/command-db.ts +310 -0
  91. package/lib/compose-network-recovery.ts +283 -0
  92. package/lib/connection-secret-redaction.ts +25 -0
  93. package/lib/container-runtime.ts +1453 -0
  94. package/lib/credential-cleanup-inventory.ts +490 -0
  95. package/lib/credential-consumers/engine-anchor.ts +1880 -0
  96. package/lib/credential-consumers/engines.ts +797 -0
  97. package/lib/credential-consumers/files.ts +934 -0
  98. package/lib/credential-consumers/github.ts +702 -0
  99. package/lib/credential-consumers/mcp.ts +389 -0
  100. package/lib/credential-consumers/task-files.ts +127 -0
  101. package/lib/credential-consumers/task-secrets.ts +10 -0
  102. package/lib/credential-store-pickup.ts +149 -0
  103. package/lib/credential-store-scope.ts +112 -0
  104. package/lib/credential-store.ts +932 -0
  105. package/lib/credentials-activation-authority.ts +709 -0
  106. package/lib/credentials-purge.ts +1006 -0
  107. package/lib/custom-connection-capture.ts +395 -0
  108. package/lib/custom-connection-launch.ts +384 -0
  109. package/lib/custom-connection-runtime.ts +141 -0
  110. package/lib/db.ts +80 -0
  111. package/lib/disk-pressure.ts +42 -0
  112. package/lib/docker-exec.ts +156 -0
  113. package/lib/engine-accounts.ts +1970 -0
  114. package/lib/engine-connect.ts +321 -0
  115. package/lib/engine-credential-env.ts +52 -0
  116. package/lib/engine-login.ts +2933 -0
  117. package/lib/engines.ts +1342 -0
  118. package/lib/enrollment-state.ts +251 -0
  119. package/lib/env-file.ts +246 -0
  120. package/lib/env-schema.ts +891 -0
  121. package/lib/env.ts +195 -0
  122. package/lib/fs-aliases.ts +49 -0
  123. package/lib/git-clone-credentials.ts +334 -0
  124. package/lib/git-diff.ts +530 -0
  125. package/lib/git-identity.ts +715 -0
  126. package/lib/github-git-auth.ts +207 -0
  127. package/lib/github-origin.ts +28 -0
  128. package/lib/github-tokens.ts +2439 -0
  129. package/lib/host-cloud-url.ts +62 -0
  130. package/lib/host-config.ts +279 -0
  131. package/lib/host-env.ts +424 -0
  132. package/lib/host-logs.ts +965 -0
  133. package/lib/internal-connections.ts +5 -0
  134. package/lib/keyed-promise-tail.ts +23 -0
  135. package/lib/launch-mode.ts +25 -0
  136. package/lib/lease.ts +239 -0
  137. package/lib/legacy-host.ts +769 -0
  138. package/lib/legacy-runtime-retirement.ts +424 -0
  139. package/lib/legacy-runtime-v1.fixture.ts +627 -0
  140. package/lib/machine-exec.ts +238 -0
  141. package/lib/machine-gateway-tunnel.ts +344 -0
  142. package/lib/machine-keys.ts +65 -0
  143. package/lib/machine-project-env-loader.ts +152 -0
  144. package/lib/machine-provider-aws.ts +923 -0
  145. package/lib/machine-provider-local.ts +338 -0
  146. package/lib/machine-provider.ts +90 -0
  147. package/lib/managed-activation-watcher.ts +72 -0
  148. package/lib/managed-install-owner-watcher.ts +55 -0
  149. package/lib/managed-operation-drain.ts +57 -0
  150. package/lib/managed-runtime.ts +4296 -0
  151. package/lib/managed-update-scheduler.ts +125 -0
  152. package/lib/mcp-capture.ts +865 -0
  153. package/lib/mcp-config-lock.ts +2 -0
  154. package/lib/mcp-connections.ts +959 -0
  155. package/lib/mcp-gateway.ts +2594 -0
  156. package/lib/obs.ts +512 -0
  157. package/lib/openrouter-budget.ts +247 -0
  158. package/lib/orchestrator.ts +11409 -0
  159. package/lib/person-capture.ts +327 -0
  160. package/lib/preview-ports.ts +85 -0
  161. package/lib/preview-sidecar.ts +331 -0
  162. package/lib/project-env-authority.ts +1319 -0
  163. package/lib/project-env-feature.ts +74 -0
  164. package/lib/project-env-legacy-tags.ts +130 -0
  165. package/lib/project-env-loader.ts +242 -0
  166. package/lib/project-env-replica.ts +2094 -0
  167. package/lib/project-env-schema-capture.ts +342 -0
  168. package/lib/project-env-staging.ts +148 -0
  169. package/lib/project-env-task-policy.ts +176 -0
  170. package/lib/project-env-task-replica.ts +1752 -0
  171. package/lib/release-feed.ts +61 -0
  172. package/lib/release-manifest.ts +710 -0
  173. package/lib/release-trust.ts +46 -0
  174. package/lib/repo-clone.ts +38 -0
  175. package/lib/routing/shadow.ts +261 -0
  176. package/lib/runner-boot.ts +10 -0
  177. package/lib/runtime-activation-tail.ts +279 -0
  178. package/lib/runtime-archive.ts +1216 -0
  179. package/lib/runtime-authority.ts +166 -0
  180. package/lib/runtime-guard.ts +36 -0
  181. package/lib/runtime-provider-state.ts +169 -0
  182. package/lib/runtime-reconnect.ts +86 -0
  183. package/lib/runtime-state.ts +1033 -0
  184. package/lib/safe-http.ts +364 -0
  185. package/lib/secrets-proxy/address.ts +95 -0
  186. package/lib/secrets-proxy/audit.ts +30 -0
  187. package/lib/secrets-proxy/ca-store.ts +200 -0
  188. package/lib/secrets-proxy/ca.ts +470 -0
  189. package/lib/secrets-proxy/container-steps.ts +138 -0
  190. package/lib/secrets-proxy/der.ts +273 -0
  191. package/lib/secrets-proxy/engine.ts +1633 -0
  192. package/lib/secrets-proxy/host.ts +93 -0
  193. package/lib/secrets-proxy/hostname.ts +57 -0
  194. package/lib/secrets-proxy/placeholders.ts +196 -0
  195. package/lib/secrets-proxy/policy.ts +150 -0
  196. package/lib/secrets-proxy/readiness.ts +148 -0
  197. package/lib/secrets-proxy/runtime.ts +582 -0
  198. package/lib/secrets-proxy/scrub.ts +559 -0
  199. package/lib/secrets-proxy/sidecar-view.ts +139 -0
  200. package/lib/secrets-proxy/task-scripts.ts +262 -0
  201. package/lib/secrets-proxy/task-trust.ts +605 -0
  202. package/lib/secrets-proxy/trust-watch.ts +80 -0
  203. package/lib/secrets.ts +71 -0
  204. package/lib/shared-files-s3.ts +976 -0
  205. package/lib/shared-files.ts +156 -0
  206. package/lib/sidecar-assign.ts +49 -0
  207. package/lib/sidecar-guard.ts +58 -0
  208. package/lib/sidecar-resume.ts +178 -0
  209. package/lib/sidecar.ts +69 -0
  210. package/lib/signin-box-credentials.ts +465 -0
  211. package/lib/signin-box.ts +114 -0
  212. package/lib/skill-index-example.fixture.ts +56 -0
  213. package/lib/skill-layout.ts +417 -0
  214. package/lib/skills.ts +489 -0
  215. package/lib/ssh.ts +401 -0
  216. package/lib/standard-image-cleanup.ts +117 -0
  217. package/lib/standard-image-pin.ts +207 -0
  218. package/lib/standard-image.ts +1697 -0
  219. package/lib/stopped-task-status-queue.ts +120 -0
  220. package/lib/task-container-cli.ts +269 -0
  221. package/lib/task-credential-cleanup.ts +752 -0
  222. package/lib/task-diff-sandbox.ts +464 -0
  223. package/lib/task-diff.ts +91 -0
  224. package/lib/task-env-forwarder.ts +228 -0
  225. package/lib/task-env-source.ts +249 -0
  226. package/lib/task-environment/anchor.ts +31 -0
  227. package/lib/task-environment/apple-container.ts +860 -0
  228. package/lib/task-environment/docker.ts +1344 -0
  229. package/lib/task-environment/index.ts +466 -0
  230. package/lib/task-environment/legacy-adoption.ts +467 -0
  231. package/lib/task-environment/machine-environment.ts +61 -0
  232. package/lib/task-environment/machine-task-up.ts +1227 -0
  233. package/lib/task-environment/machine.ts +974 -0
  234. package/lib/task-environment/registry.ts +59 -0
  235. package/lib/task-environment/self-identity.ts +20 -0
  236. package/lib/task-environment/self.ts +566 -0
  237. package/lib/task-environment/types.ts +642 -0
  238. package/lib/task-environment/workspace-files.ts +685 -0
  239. package/lib/task-environment/workspace-runtime.fixture.ts +50 -0
  240. package/lib/task-files-machine-retirement-outbox.ts +592 -0
  241. package/lib/task-files-machine-retirement.ts +116 -0
  242. package/lib/task-files-readiness.ts +127 -0
  243. package/lib/task-gateway-port.ts +22 -0
  244. package/lib/task-gateway-protocol.ts +190 -0
  245. package/lib/task-gateway-recovery.ts +47 -0
  246. package/lib/task-gateway-state.ts +512 -0
  247. package/lib/task-identity.ts +19 -0
  248. package/lib/task-inventory.ts +682 -0
  249. package/lib/task-machine-credentials-activation.ts +95 -0
  250. package/lib/task-machine-swap-safety.ts +729 -0
  251. package/lib/task-person-credentials.ts +76 -0
  252. package/lib/task-project-ops-host.ts +147 -0
  253. package/lib/task-project-ops.ts +1062 -0
  254. package/lib/task-secret-staging.ts +16 -0
  255. package/lib/task-status.ts +46 -0
  256. package/lib/task-token.ts +46 -0
  257. package/lib/token-broker-adopt.ts +344 -0
  258. package/lib/token-broker-lambda.ts +105 -0
  259. package/lib/token-broker-local.ts +392 -0
  260. package/lib/token-broker-revision.ts +310 -0
  261. package/lib/token-broker-ssm.ts +525 -0
  262. package/lib/token-broker.ts +218 -0
  263. package/lib/token-lanes/openai-oauth.ts +123 -0
  264. package/lib/token-lanes/registry.ts +45 -0
  265. package/lib/token-lanes/types.ts +163 -0
  266. package/lib/token-lanes/xai-oauth.ts +165 -0
  267. package/lib/transcript.ts +128 -0
  268. package/lib/tunnel-registry.ts +220 -0
  269. package/lib/tunnel-runtime.ts +235 -0
  270. package/lib/ulid.ts +16 -0
  271. package/lib/user-bus-env.ts +116 -0
  272. package/lib/user-credentials-store.ts +435 -0
  273. package/lib/vnode-pressure.ts +142 -0
  274. package/package.json +131 -4
  275. package/runner/runner.mjs +442 -0
  276. package/scripts/agent/_common.sh +633 -0
  277. package/scripts/agent/env-hook.sh +626 -0
  278. package/scripts/agent/moondeck-cli.sh +5 -0
  279. package/scripts/agent/task-down.sh +291 -0
  280. package/scripts/agent/task-status.sh +70 -0
  281. package/scripts/agent/task-up.sh +3761 -0
  282. package/scripts/agent/uai-env-exec.sh +26 -0
  283. package/scripts/install/darwin.ts +1048 -0
  284. package/scripts/install/linux.ts +958 -0
  285. package/scripts/install/types.ts +78 -0
  286. package/scripts/install/util.ts +254 -0
  287. package/scripts/install/win.ts +143 -0
  288. package/src/activation-respawn.ts +155 -0
  289. package/src/apple-tunnel-route.ts +104 -0
  290. package/src/capture-wire.ts +102 -0
  291. package/src/cli.ts +2583 -0
  292. package/src/cloud-credentials.ts +2144 -0
  293. package/src/cloud-origins.ts +23 -0
  294. package/src/credential-location.ts +248 -0
  295. package/src/credential-wire-validation.ts +25 -0
  296. package/src/custom-connections.ts +237 -0
  297. package/src/engine-account-paths.ts +11 -0
  298. package/src/engine-models.ts +39 -0
  299. package/src/engine-usage.ts +104 -0
  300. package/src/event-outbox.ts +185 -0
  301. package/src/index.ts +2899 -0
  302. package/src/load-env.ts +97 -0
  303. package/src/machine-tunnel-route.ts +156 -0
  304. package/src/main.ts +6349 -0
  305. package/src/never-proxied-domains.ts +31 -0
  306. package/src/outbound-frame.ts +31 -0
  307. package/src/paths.ts +81 -0
  308. package/src/project-env-latest.ts +130 -0
  309. package/src/project-env-policy.ts +396 -0
  310. package/src/project-env-wording.ts +53 -0
  311. package/src/protocol.ts +5545 -0
  312. package/src/runtime-bootstrap.ts +165 -0
  313. package/src/station-identity.ts +96 -0
  314. package/src/task-env-names.ts +28 -0
  315. package/src/task-secrets.ts +75 -0
  316. package/src/tunnel-dial.ts +102 -0
  317. package/src/ui/server.ts +852 -0
  318. package/src/ui/types.ts +173 -0
  319. package/src/workspace-path-policy.ts +41 -0
  320. package/ui/app.js +1102 -0
  321. package/ui/index.html +90 -0
  322. package/ui/style.css +685 -0
  323. package/ui/uai-favicon.svg +21 -0
  324. package/ui/uai-logo-black.svg +9 -0
  325. package/ui/uai-wheel.svg +9 -0
@@ -0,0 +1,347 @@
1
+ # syntax=docker/dockerfile:1.7
2
+ #
3
+ # uai standard image — the ONE per-host workbench image (ADR-022).
4
+ #
5
+ # Built once per host at host startup and cached as `uai-standard:dev`.
6
+ # A task reuses this image unchanged unless a selected project declares an
7
+ # `extra` snippet, in which case task-up derives `uai-task-<id>` FROM this
8
+ # image. See ../../../docs/runtime.md.
9
+ #
10
+ # Design points:
11
+ # - Debian bookworm-slim base, non-root `node` user (home /home/node).
12
+ # - asdf at /opt/asdf with plugins for nodejs/python/golang/ruby/rust.
13
+ # Before Compose starts a task, a host-controlled maintenance container
14
+ # materializes its declared versions into the host-wide /opt/asdf-data
15
+ # volume. uai-init only installs task-local dependencies afterward.
16
+ # - A default ~/.tool-versions (asdf global fallback) pins a working Node +
17
+ # Python so the agent CLIs and a 0-project scratchpad always have a runtime;
18
+ # a project's own .tool-versions takes precedence (asdf dir walk).
19
+ # - Claude Code + Codex + code-server installed globally via the root Node.
20
+ #
21
+ # This Dockerfile takes NO build args and substitutes NO placeholders — it is
22
+ # fully static. Per-task variation lives in the derived `uai-task-<id>` image.
23
+
24
+ FROM debian:bookworm-slim
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # 0. Pinned versions (single source of truth for this image).
28
+ # ---------------------------------------------------------------------------
29
+
30
+ # asdf release to clone. Pinned for reproducible builds; bump deliberately.
31
+ ENV ASDF_VERSION=v0.14.1
32
+
33
+ # Qualified agent builds. These are execution-contract pins: per-command env
34
+ # freshness is requalified before any of them moves.
35
+ ENV UAI_CODEX_CLI_VERSION=0.164.0
36
+ ENV UAI_CURSOR_AGENT_VERSION=2026.10.22-27dcf5f
37
+ ENV UAI_GROK_CLI_VERSION=1.0.70
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # 1. System packages.
41
+ #
42
+ # build-essential + the -dev libs cover what asdf's nodejs/python/ruby/rust
43
+ # build plugins need to compile a runtime from source on first use. unzip is
44
+ # needed by several asdf plugins; gnupg + ca-certificates for the gh apt repo.
45
+ # iptables is for the secrets proxy's NAT chain (ADR-153); only the runner's
46
+ # one privileged exec runs it, and ca-certificates also installs its CA.
47
+ # ---------------------------------------------------------------------------
48
+
49
+ ENV DEBIAN_FRONTEND=noninteractive
50
+
51
+ RUN apt-get update \
52
+ && apt-get install -y --no-install-recommends \
53
+ build-essential \
54
+ ca-certificates \
55
+ curl \
56
+ direnv \
57
+ git \
58
+ gnupg \
59
+ iptables \
60
+ jq \
61
+ libbz2-dev \
62
+ libffi-dev \
63
+ liblzma-dev \
64
+ libncurses-dev \
65
+ libreadline-dev \
66
+ libsqlite3-dev \
67
+ libssl-dev \
68
+ libxml2-dev \
69
+ libxmlsec1-dev \
70
+ libyaml-dev \
71
+ openssh-client \
72
+ pkg-config \
73
+ procps \
74
+ ripgrep \
75
+ tk-dev \
76
+ tmux \
77
+ unzip \
78
+ util-linux \
79
+ xz-utils \
80
+ zlib1g-dev \
81
+ zsh \
82
+ && rm -rf /var/lib/apt/lists/*
83
+
84
+ # ADR-053: agent browser (Playwright/Chromium) runtime libs + the watchable
85
+ # display stack (Xvfb → x11vnc → noVNC). Baked here because the
86
+ # per-container background apt raced session start (headless-stuck MCP
87
+ # server, viewer lagging the preview by minutes — Debian's novnc drags a
88
+ # large Python tree). The Chromium BINARY still comes from the shared
89
+ # uai-playwright volume (downloaded once per host); these are its deps.
90
+ RUN apt-get update \
91
+ && apt-get install -y --no-install-recommends \
92
+ libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
93
+ libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 \
94
+ libxrandr2 libgbm1 libasound2 libpango-1.0-0 libcairo2 \
95
+ fonts-liberation fonts-unifont \
96
+ xvfb x11vnc novnc websockify \
97
+ && rm -rf /var/lib/apt/lists/*
98
+
99
+ # GitHub CLI (`gh`) — used by the ship/PR flow inside the container.
100
+ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
101
+ | tee /usr/share/keyrings/githubcli-archive-keyring.gpg >/dev/null \
102
+ && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
103
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
104
+ > /etc/apt/sources.list.d/github-cli.list \
105
+ && apt-get update \
106
+ && apt-get install -y --no-install-recommends gh \
107
+ && rm -rf /var/lib/apt/lists/*
108
+
109
+ # uv — fast Python package + venv manager (used by uai-init's Python path).
110
+ # Installed system-wide so both root and node can run it.
111
+ RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh
112
+
113
+ # ---------------------------------------------------------------------------
114
+ # 2. Non-root `node` user.
115
+ #
116
+ # The container runs as `node`; uai-init and the agent CLIs expect
117
+ # /home/node. uid/gid 1000 matches the common host default so bind-mounted
118
+ # worktree files stay writable.
119
+ # ---------------------------------------------------------------------------
120
+
121
+ RUN groupadd --gid 1000 node \
122
+ && useradd --uid 1000 --gid 1000 --create-home --home-dir /home/node --shell /bin/bash node
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # 3. asdf.
126
+ #
127
+ # Clone to /opt/asdf (the code) and point the DATA dir at /opt/asdf-data
128
+ # (the host-wide cache volume mounts there). Both root and node must be able
129
+ # to read the install and write the data dir, so /opt/asdf-data is group
130
+ # owned by `node` and group-writable; the data volume inherits this.
131
+ # ---------------------------------------------------------------------------
132
+
133
+ ENV ASDF_DIR=/opt/asdf
134
+ ENV ASDF_DATA_DIR=/opt/asdf-data
135
+ ENV ASDF_CONFIG_FILE=/dev/null
136
+ ENV ASDF_DEFAULT_TOOL_VERSIONS_FILENAME=.tool-versions
137
+ ENV COREPACK_HOME=/opt/asdf-data/corepack
138
+ ENV COREPACK_DEFAULT_TO_LATEST=0
139
+ ENV COREPACK_ENABLE_AUTO_PIN=0
140
+ ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
141
+ ENV COREPACK_ENV_FILE=0
142
+ ENV COREPACK_ENABLE_PROJECT_SPEC=1
143
+ ENV COREPACK_ENABLE_STRICT=1
144
+ ENV COREPACK_ENABLE_UNSAFE_CUSTOM_URLS=0
145
+ ENV COREPACK_INTEGRITY_CHECK=1
146
+ ENV COREPACK_ENABLE_NETWORK=0
147
+ ENV COREPACK_NPM_REGISTRY=https://registry.npmjs.org
148
+ # Leave the tool-versions filename at asdf's default (`.tool-versions`) so asdf
149
+ # walks UP from the cwd and a project's /workspace/<slug>/.tool-versions wins.
150
+ # The baked default lives at the node user's home (~/.tool-versions) as asdf's
151
+ # global fallback for tasks with no project file (agent CLIs, scratchpad).
152
+ # Keep ASDF_DEFAULT_TOOL_VERSIONS_FILENAME as the basename above, never an
153
+ # absolute path: an absolute value bypasses project files entirely (it made
154
+ # asdf read only /etc/.tool-versions, ignoring per-project pins).
155
+
156
+ RUN git clone --depth 1 --branch "$ASDF_VERSION" https://github.com/asdf-vm/asdf.git "$ASDF_DIR" \
157
+ && mkdir -p "$ASDF_DATA_DIR" \
158
+ && touch "$ASDF_DATA_DIR/.uai-maintenance.lock" \
159
+ && chown -R node:node "$ASDF_DIR" "$ASDF_DATA_DIR" \
160
+ && chmod -R g+rwX "$ASDF_DATA_DIR" \
161
+ && chmod 0664 "$ASDF_DATA_DIR/.uai-maintenance.lock"
162
+
163
+ # Make asdf available on PATH + shell init for BOTH root and node, for login
164
+ # and non-login shells (uai-init runs as node via a non-interactive exec).
165
+ # - asdf.sh sources the shim + completions and prepends the shims dir.
166
+ # - The shims dir is also added to PATH directly so a bare `docker exec`
167
+ # (which sources neither profile) still sees installed runtimes.
168
+ ENV PATH=/opt/asdf-data/shims:/opt/asdf/bin:$PATH
169
+
170
+ RUN printf '%s\n' \
171
+ '. /opt/asdf/asdf.sh' \
172
+ > /etc/profile.d/asdf.sh \
173
+ && chmod 0644 /etc/profile.d/asdf.sh
174
+
175
+ # Source asdf from each user's bashrc as well (interactive non-login shells,
176
+ # e.g. the code-server terminal) and enable direnv.
177
+ RUN for rc in /root/.bashrc /home/node/.bashrc; do \
178
+ printf '\n%s\n%s\n' \
179
+ '. /opt/asdf/asdf.sh' \
180
+ 'eval "$(direnv hook bash)"' >> "$rc"; \
181
+ done \
182
+ && chown node:node /home/node/.bashrc
183
+
184
+ # zsh + oh-my-zsh as the interactive login shell for `node` (the code-server
185
+ # terminal and `docker exec -it`). Installed unattended; the installer writes a
186
+ # default ~/.zshrc (robbyrussell theme, git plugin) which we extend with the
187
+ # same asdf + direnv wiring as .bashrc. Non-interactive execs (uai-init, claude,
188
+ # codex, git) invoke their command directly, so the login-shell change does not
189
+ # affect them.
190
+ RUN su node -c 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended' \
191
+ && printf '\n%s\n%s\n' \
192
+ '. /opt/asdf/asdf.sh' \
193
+ 'eval "$(direnv hook zsh)"' >> /home/node/.zshrc \
194
+ && chown node:node /home/node/.zshrc \
195
+ && usermod -s /usr/bin/zsh node
196
+
197
+ # ---------------------------------------------------------------------------
198
+ # 4. asdf plugins + the root default runtimes.
199
+ #
200
+ # Plugins are added for all common runtimes; only the root defaults are
201
+ # *installed* at build time. A pre-Compose maintenance container installs
202
+ # project versions into the shared /opt/asdf-data volume.
203
+ #
204
+ # Run as node so the install lands in /opt/asdf-data owned by node.
205
+ # ---------------------------------------------------------------------------
206
+
207
+ # Default .tool-versions at the node user's home = asdf's GLOBAL fallback, used
208
+ # only when no project /workspace/<slug>/.tool-versions is in scope (agent CLIs,
209
+ # scratchpad tasks). A project file takes precedence via asdf's dir walk. The
210
+ # `tool-versions` file in this directory is the AUTHORITATIVE source.
211
+ COPY tool-versions /home/node/.tool-versions
212
+ COPY --chown=root:root container/corepack-version /usr/local/share/uai/corepack-version
213
+ COPY --chown=root:root container/uai-materialize-runtimes /usr/local/bin/uai-materialize-runtimes
214
+ RUN chmod 0644 /home/node/.tool-versions /usr/local/share/uai/corepack-version \
215
+ && chmod 0755 /usr/local/bin/uai-materialize-runtimes \
216
+ && chown node:node /home/node/.tool-versions
217
+
218
+ USER node
219
+
220
+ # Add plugins for all common runtimes, then install exactly the versions the
221
+ # default ~/.tool-versions pins (read straight from the file so there is one
222
+ # source of truth). Other versions install in the pre-Compose materializer.
223
+ RUN bash -lc '\
224
+ set -e; \
225
+ . /opt/asdf/asdf.sh; \
226
+ asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git; \
227
+ asdf plugin add python https://github.com/asdf-community/asdf-python.git; \
228
+ asdf plugin add golang https://github.com/asdf-community/asdf-golang.git; \
229
+ asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git; \
230
+ asdf plugin add rust https://github.com/asdf-community/asdf-rust.git; \
231
+ cd /home/node && asdf install nodejs && asdf install python; \
232
+ '
233
+
234
+ # ---------------------------------------------------------------------------
235
+ # 5. Global agent CLIs + code-server, via the root-default Node.
236
+ #
237
+ # These are Node programs and must be present even for a 0-project task, so
238
+ # they are installed globally against the asdf-default Node and then reshimmed
239
+ # so `claude` / `codex` / `code-server` resolve through the asdf shims dir
240
+ # (which is on PATH for a bare docker exec).
241
+ # ---------------------------------------------------------------------------
242
+
243
+ RUN bash -lc '\
244
+ set -e; \
245
+ . /opt/asdf/asdf.sh; \
246
+ COREPACK_VERSION="$(sed -n "1p" /usr/local/share/uai/corepack-version)"; \
247
+ printf "%s" "$COREPACK_VERSION" | grep -Eq "^[0-9]+\\.[0-9]+\\.[0-9]+$"; \
248
+ npm install -g @anthropic-ai/claude-code "@openai/codex@$UAI_CODEX_CLI_VERSION" "corepack@$COREPACK_VERSION" \
249
+ --registry=https://registry.npmjs.org/ \
250
+ --userconfig=/dev/null \
251
+ --globalconfig=/dev/uai-empty-npmrc; \
252
+ corepack enable; \
253
+ curl -fsSL https://code-server.dev/install.sh | sh -s -- --method standalone --prefix /home/node/.local; \
254
+ asdf reshim nodejs; \
255
+ test "$(codex --version)" = "codex-cli $UAI_CODEX_CLI_VERSION"; \
256
+ '
257
+
258
+ # Optional agent CLIs — installed ONLY when the host has them configured
259
+ # (INSTALL_* build args from standard-image.ts, gated on the operator's creds
260
+ # and folded into the rebuild hash, so a new login rebuilds). Single-binary
261
+ # installs to /home/node/.{kimi-code,grok}/bin; the adapters invoke them by
262
+ # absolute path and task-up copies the subscription creds in. Non-fatal: a CDN
263
+ # hiccup must never break the image for Claude/Codex.
264
+ ARG INSTALL_KIMI=0
265
+ RUN if [ "$INSTALL_KIMI" = "1" ]; then \
266
+ bash -lc 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash' \
267
+ || echo "[warn] kimi-code install failed — kimi engine unavailable in this image"; \
268
+ fi
269
+
270
+ ARG INSTALL_GROK=0
271
+ RUN if [ "$INSTALL_GROK" = "1" ]; then \
272
+ bash -lc 'set -e; \
273
+ curl -fsSL https://x.ai/cli/install.sh | bash -s -- "$UAI_GROK_CLI_VERSION"; \
274
+ case "$("$HOME/.grok/bin/grok" --version)" in \
275
+ "grok $UAI_GROK_CLI_VERSION "*) ;; \
276
+ *) exit 1 ;; \
277
+ esac' \
278
+ || { \
279
+ rm -rf -- "$HOME/.grok/bin" "$HOME/.grok/downloads"; \
280
+ echo "[warn] grok install failed — grok engine unavailable in this image"; \
281
+ }; \
282
+ fi
283
+
284
+ # Cursor Agent (Anysphere) — installs to /home/node/.local/bin/cursor-agent.
285
+ # Auth is CURSOR_API_KEY (env-injected by the adapter), so nothing to copy at
286
+ # task-up — just the binary.
287
+ ARG INSTALL_CURSOR=0
288
+ RUN if [ "$INSTALL_CURSOR" = "1" ]; then \
289
+ bash -lc 'set -e; \
290
+ case "$(uname -m)" in \
291
+ x86_64|amd64) cursor_arch=x64 ;; \
292
+ aarch64|arm64) cursor_arch=arm64 ;; \
293
+ *) echo "unsupported Cursor architecture: $(uname -m)" >&2; exit 1 ;; \
294
+ esac; \
295
+ cursor_dir="$HOME/.local/share/cursor-agent/versions/$UAI_CURSOR_AGENT_VERSION"; \
296
+ mkdir -p "$cursor_dir" "$HOME/.local/bin"; \
297
+ curl -fSL "https://downloads.cursor.com/lab/$UAI_CURSOR_AGENT_VERSION/linux/$cursor_arch/agent-cli-package.tar.gz" \
298
+ | tar --strip-components=1 -xzf - -C "$cursor_dir"; \
299
+ ln -sfn "$cursor_dir/cursor-agent" "$HOME/.local/bin/agent"; \
300
+ ln -sfn "$cursor_dir/cursor-agent" "$HOME/.local/bin/cursor-agent"; \
301
+ test "$("$HOME/.local/bin/cursor-agent" --version)" = "$UAI_CURSOR_AGENT_VERSION"' \
302
+ || { \
303
+ rm -f -- "$HOME/.local/bin/agent" "$HOME/.local/bin/cursor-agent"; \
304
+ rm -rf -- "$HOME/.local/share/cursor-agent/versions/$UAI_CURSOR_AGENT_VERSION"; \
305
+ echo "[warn] cursor install failed — cursor engine unavailable in this image"; \
306
+ }; \
307
+ fi
308
+
309
+ # OpenCode (ADR-077) — multi-provider CLI. Installs to /home/node/.opencode/bin;
310
+ # the adapter invokes it by absolute path and task-up copies the provider auth
311
+ # (~/.local/share/opencode/auth.json) in. Non-fatal like the others.
312
+ ARG INSTALL_OPENCODE=0
313
+ RUN if [ "$INSTALL_OPENCODE" = "1" ]; then \
314
+ bash -lc 'curl -fsSL https://opencode.ai/install | bash' \
315
+ || echo "[warn] opencode install failed — opencode engine unavailable in this image"; \
316
+ fi
317
+
318
+ ENV PATH=/home/node/.local/bin:$PATH
319
+
320
+ # ---------------------------------------------------------------------------
321
+ # 6. uai-init baked in.
322
+ # ---------------------------------------------------------------------------
323
+
324
+ USER root
325
+
326
+ COPY --chown=root:root container/uai-init /usr/local/bin/uai-init
327
+ RUN node_version="$(awk '$1 == "nodejs" { print $2; exit }' /home/node/.tool-versions)" \
328
+ && test -n "$node_version" \
329
+ && test -x "/opt/asdf-data/installs/nodejs/$node_version/bin/node" \
330
+ && ln -s "/opt/asdf-data/installs/nodejs/$node_version/bin/node" /usr/local/bin/node \
331
+ && chmod 0755 /usr/local/bin/uai-init
332
+
333
+ # Curated code-server defaults. uai-init seeds these into the per-task User
334
+ # settings dir when none exists (slim chrome, telemetry off, trust off).
335
+ COPY --chown=root:root container/code-server-settings.json /usr/local/share/uai/code-server-settings.json
336
+
337
+ # ---------------------------------------------------------------------------
338
+ # 7. Default working directory + entrypoint.
339
+ #
340
+ # The container is a long-lived workbench. uai-init (deps + code-server) and
341
+ # the agent CLIs are started by the host via `docker exec` after task-up.
342
+ # ---------------------------------------------------------------------------
343
+
344
+ USER node
345
+ WORKDIR /workspace
346
+
347
+ CMD ["sleep", "infinity"]
@@ -0,0 +1,167 @@
1
+ # Moondeck standard image
2
+
3
+ The single per-host workbench image, built once at host startup and cached as
4
+ `moondeck-standard:dev` (ADR-022). Every task reuses it unchanged unless a selected
5
+ project declares an [`extra`](../../../docs/runtime.md#the-extra-escape-hatch)
6
+ snippet, in which case task-up derives `uai-task-<id>` FROM this image.
7
+
8
+ See [`docs/runtime.md`](../../../docs/runtime.md) for the authoritative spec.
9
+
10
+ ## What's inside
11
+
12
+ - **Base**: `debian:bookworm-slim`, non-root `node` user (home `/home/node`,
13
+ uid/gid 1000). The container runs as `node`; uai-init and the agent CLIs
14
+ expect `/home/node`.
15
+ - **asdf** at `/opt/asdf`, data dir `/opt/asdf-data` (the host-wide
16
+ `moondeck-asdf-data` volume mounts there read-only in task apps). Plugins
17
+ pre-added for `nodejs`, `python`, `golang`, `ruby`, `rust`. Only the root
18
+ defaults are installed at build time; after worktree resolution, a
19
+ host-controlled pre-start maintenance container is the bounded, locked
20
+ read-write user that installs and proves other versions before the task app
21
+ exists.
22
+ - **Default `~/.tool-versions`** (asdf's global fallback, at `/home/node`)
23
+ pinning a default Node + Python. The `tool-versions` file in this directory is
24
+ the authoritative source for the numbers; the `Dockerfile` `COPY`s it to
25
+ `/home/node/.tool-versions` and installs exactly those versions at build time.
26
+ This guarantees the agent CLIs and a 0-project scratchpad always have a
27
+ runtime. asdf uses its default `.tool-versions` filename and walks UP from the
28
+ cwd, so a project's `/workspace/<slug>/.tool-versions` **takes precedence**;
29
+ the home file is only the fallback. (Do **not** set
30
+ `ASDF_DEFAULT_TOOL_VERSIONS_FILENAME` to an absolute path — that overrides the
31
+ search filename and makes asdf ignore per-project files.)
32
+ - **uv uses asdf's Python.** For Python folders, `uai-init` runs
33
+ `uv venv --python "$(asdf which python)"` so the venv matches the asdf-pinned
34
+ version (`.tool-versions`) rather than uv's own `.python-version` /
35
+ `requires-python` discovery, which can disagree or be junk.
36
+ - **Claude Code** + **Codex**, installed globally via the root-default Node
37
+ (`@anthropic-ai/claude-code`, `@openai/codex`), reshimmed onto the asdf
38
+ shims dir so `claude` / `codex` resolve under a bare `docker exec`.
39
+ - **code-server** (the Editor pane), **gh**, **git**, **curl**,
40
+ **ca-certificates**, **jq**, **ripgrep**, **tmux**, **direnv**, **uv**,
41
+ plus `build-essential` and the `-dev` libraries asdf's build plugins need.
42
+ - **`uai-materialize-runtimes`** baked at
43
+ `/usr/local/bin/uai-materialize-runtimes`; it is the credential-free,
44
+ workspace-read-only shared-cache writer used before Compose starts a task.
45
+ The host supplies the selected project slugs at the fixed, read-only
46
+ `/run/uai/runtime-projects` allowlist; only those immediate Git worktrees are
47
+ inspected, so workspace control/shared directories cannot become projects.
48
+ - **`uai-init`** baked at `/usr/local/bin/uai-init`; it installs task-local
49
+ dependencies and starts code-server, but does not mutate asdf runtimes.
50
+ - **Task-private npm globals** under `/home/node/.local/share/uai-npm`, with
51
+ cache/logs under `/home/node/.cache/uai-npm`. The shared asdf shims and
52
+ system controls remain ahead of this prefix's `bin` on `PATH`, and
53
+ `ASDF_SKIP_RESHIM=1` prevents npm's asdf hook from writing shared shims.
54
+ - `WORKDIR /workspace`; `CMD ["sleep","infinity"]`.
55
+
56
+ Codex credentials are **not** baked in or mounted — task-up `docker cp`s them
57
+ into `/home/node/.codex` at init (ADR-015 keeps secrets host-side). `~/.claude`
58
+ is bind-mounted read-only by the generated compose.
59
+
60
+ ## Pinned constants (must match the rest of the host runtime)
61
+
62
+ | Thing | Value |
63
+ | --- | --- |
64
+ | Standard image tag | `moondeck-standard:dev` |
65
+ | Per-task derived image (only with `extra`) | `uai-task-<taskId>` |
66
+ | Shared asdf data volume | app: `moondeck-asdf-data:/opt/asdf-data:ro`; maintenance: `:rw` |
67
+ | Task npm prefix / cache | `/home/node/.local/share/uai-npm` / `/home/node/.cache/uai-npm` |
68
+ | Compose service name | `app` (container `task-<taskId>-app-1`) |
69
+ | In-container workspace | `/workspace` |
70
+ | code-server port | `8080` (inside the container) |
71
+
72
+ ## Build
73
+
74
+ The host process builds this at startup when the cached digest is missing or
75
+ stale. To build by hand from the repo root:
76
+
77
+ ```bash
78
+ docker build -t moondeck-standard:dev host-agent/images/standard
79
+ ```
80
+
81
+ A derived per-task image (when a project has `extra`) is built by `task-up`
82
+ from a generated `.uai/Dockerfile` that is `FROM moondeck-standard:dev` plus the
83
+ concatenated `extra` RUN lines, tagged `uai-task-<id>`.
84
+
85
+ ## Smoke test (no uai involved)
86
+
87
+ These files are validated at the Phase E smoke test (no Docker in CI), so the
88
+ steps below are for local image iteration only.
89
+
90
+ ```bash
91
+ # 1. Build.
92
+ docker build -t moondeck-standard:dev host-agent/images/standard
93
+
94
+ # 2. Create the shared asdf data volume + a throwaway Git worktree and the
95
+ # host-controlled selected-project allowlist.
96
+ docker volume create moondeck-asdf-data
97
+ mkdir -p /tmp/uai-smoke/workspace/demo/.git
98
+ printf 'nodejs 22.11.0\n' > /tmp/uai-smoke/workspace/demo/.tool-versions
99
+ printf '{"name":"demo","packageManager":"pnpm@10.15.0"}\n' \
100
+ > /tmp/uai-smoke/workspace/demo/package.json
101
+ printf 'demo\n' > /tmp/uai-smoke/runtime-projects
102
+
103
+ # 3. Materialize before the app exists, the way task-up does.
104
+ docker run --rm --user node --workdir /workspace \
105
+ -v /tmp/uai-smoke/workspace:/workspace:ro \
106
+ -v /tmp/uai-smoke/runtime-projects:/run/uai/runtime-projects:ro \
107
+ -v moondeck-asdf-data:/opt/asdf-data:rw \
108
+ moondeck-standard:dev /usr/bin/timeout --signal=TERM --kill-after=10s 600s \
109
+ /usr/local/bin/uai-materialize-runtimes
110
+
111
+ # 4. Run the container the way compose would (service `app`, workspace bind,
112
+ # asdf cache volume, code-server port published on loopback).
113
+ docker run -d --name uai-smoke \
114
+ -v /tmp/uai-smoke/workspace:/workspace \
115
+ -v /tmp/uai-smoke/runtime-projects:/run/uai/runtime-projects:ro \
116
+ -v moondeck-asdf-data:/opt/asdf-data:ro \
117
+ -e ASDF_SKIP_RESHIM=1 \
118
+ -e NPM_CONFIG_PREFIX=/home/node/.local/share/uai-npm \
119
+ -e npm_config_prefix=/home/node/.local/share/uai-npm \
120
+ -e NPM_CONFIG_CACHE=/home/node/.cache/uai-npm \
121
+ -e npm_config_cache=/home/node/.cache/uai-npm \
122
+ -e NPM_CONFIG_LOGS_DIR=/home/node/.cache/uai-npm/_logs \
123
+ -e npm_config_logs_dir=/home/node/.cache/uai-npm/_logs \
124
+ -e NPM_CONFIG_UMASK=0002 -e npm_config_umask=0002 \
125
+ -e 'PATH=/opt/asdf-data/shims:/opt/asdf/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/node/.local/share/uai-npm/bin' \
126
+ -p 127.0.0.1::8080 \
127
+ moondeck-standard:dev
128
+
129
+ # 5. Prove isolation and sanity-check the baked toolchain (bare exec — no
130
+ # profile sourced).
131
+ docker exec uai-smoke sh -c \
132
+ '! touch /opt/asdf-data/.uai-app-write-probe 2>/dev/null && test "$(npm config get prefix)" = /home/node/.local/share/uai-npm'
133
+ docker exec uai-smoke bash -lc 'asdf --version && node -v && python --version'
134
+ docker exec uai-smoke claude --version
135
+ docker exec uai-smoke codex --version
136
+
137
+ # 6. Run uai-init: task-local deps + code-server on 8080.
138
+ docker exec uai-smoke /usr/local/bin/uai-init
139
+ docker exec uai-smoke bash -lc 'curl -sf http://127.0.0.1:8080/healthz || \
140
+ curl -sf -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/'
141
+
142
+ # 7. Tear down.
143
+ docker rm -f uai-smoke
144
+ ```
145
+
146
+ Expected: step 5 proves the app cannot write the shared cache and npm resolves
147
+ the private prefix, then prints the asdf version and root-default Node +
148
+ Python; `claude` / `codex` resolve; step 6 launches code-server bound to
149
+ `0.0.0.0:8080` and the HTTP probe returns a 2xx/3xx.
150
+
151
+ ## Notes / gotchas
152
+
153
+ - **Bash 3.2 constraint does NOT apply here.** `uai-init` runs on the Linux
154
+ base image (bash 5) and may use `shopt`, arrays, etc. The 3.2 constraint is
155
+ only for host scripts under `host-agent/scripts/agent/`.
156
+ - **First-use asdf cost.** The first task needing a given runtime version pays
157
+ the download/build time; results persist on `moondeck-asdf-data` for every later
158
+ task on the host.
159
+ - **Rolling recovery is preserve-and-recreate.** Before shared maintenance, an
160
+ upgraded host stops legacy writable/non-current apps and leaves their
161
+ worktrees intact. Docker mount modes are immutable, so only an exact current
162
+ read-only app can recover in place; explicit Resume recreates anything else.
163
+ - **Ordinary `uai-init` failures remain best-effort.** A failing task-local
164
+ dependency install logs and is skipped so one bad repo never blocks editor
165
+ startup. Runtime materialization is separate and fail-closed before the app
166
+ starts; a workspace runtime-authority violation exits 78 so the host can
167
+ quarantine the container instead of treating it as ready.
@@ -0,0 +1,63 @@
1
+ {
2
+ "//": "Moondeck — slimmed code-server defaults. Seeded into the User settings",
3
+ "//2": "dir by uai-init when none exists yet. Trims welcome/telemetry/chat",
4
+ "//3": "chrome so the Editor pane is a focused editor, not a full AI-IDE.",
5
+
6
+ "telemetry.telemetryLevel": "off",
7
+ "workbench.startupEditor": "none",
8
+ "workbench.tips.enabled": false,
9
+ "workbench.welcomePage.walkthroughs.openOnInstall": false,
10
+ "window.commandCenter": false,
11
+ "chat.commandCenter.enabled": false,
12
+ "chat.viewSessions.enabled": false,
13
+ "chat.disableAIFeatures": true,
14
+ "update.mode": "none",
15
+ "extensions.autoCheckUpdates": false,
16
+ "extensions.autoUpdate": false,
17
+ "git.openRepositoryInParentFolders": "always",
18
+ "workbench.secondarySideBar.defaultVisibility": "hidden",
19
+
20
+ "//4": "Colour theme. Overwritten at FIRST init by uai-init when the task",
21
+ "//5": "carries UAI_EDITOR_THEME (ADR-091); this value is the fallback for",
22
+ "//6": "tasks created before that shipped, or created outside a browser.",
23
+ "workbench.colorTheme": "Dark 2026",
24
+
25
+ "//7": "An editor, not an IDE (ADR-092). This pane is one of three in a",
26
+ "//8": "task's right panel and is often half a screen wide, so every bar it",
27
+ "//9": "does not need is width the code does. Cut here is chrome only: the",
28
+ "//10": "status bar, the layout buttons, the menu bar, the empty-editor",
29
+ "//11": "watermark, Open Editors, the minimap and the terminal's chat hint.",
30
+ "//12": "Breadcrumbs STAY: with the status bar gone they are the only thing",
31
+ "//13": "naming the path, and a tab reading 'route.ts' is worth little in a",
32
+ "//14": "repo with eleven of them.",
33
+ "workbench.statusBar.visible": false,
34
+ "workbench.layoutControl.enabled": false,
35
+ "window.menuBarVisibility": "hidden",
36
+ "workbench.editor.empty.hint": "hidden",
37
+ "explorer.openEditors.visible": 0,
38
+ "editor.minimap.enabled": false,
39
+ "terminal.integrated.initialHint": false,
40
+
41
+ "//15": "Hide the activity bar and its custom title strip. Explorer stays",
42
+ "//16": "open; the Editor pane's own toolbar provides the pointer route to",
43
+ "//17": "Terminal. Individual pins still live in the BROWSER's IndexedDB; the",
44
+ "//18": "guarded migration keeps the requested containers ready if a user",
45
+ "//19": "later restores the bar.",
46
+ "workbench.activityBar.location": "hidden",
47
+
48
+ "//22": "Workspace Trust — every task runs in an isolated container off the",
49
+ "//23": "user's own repos. The 'do you trust this folder?' prompt is pure",
50
+ "//24": "friction here; the user already trusts their code and the container",
51
+ "//25": "is the security boundary.",
52
+ "security.workspace.trust.enabled": false,
53
+
54
+ "//26": "Port forwarding — code-server's own auto-forward / 'Open in Browser'",
55
+ "//27": "popup can't reach anything useful here: dev servers are exposed",
56
+ "//28": "through Moondeck's preview tunnel (ADR-025), not code-server's forwarder.",
57
+ "//29": "Disable it so the popup doesn't mislead.",
58
+ "remote.autoForwardPorts": false,
59
+ "remote.restoreForwardedPorts": false,
60
+
61
+ "//30": "Integrated terminal uses zsh + oh-my-zsh (the node login shell).",
62
+ "terminal.integrated.defaultProfile.linux": "zsh"
63
+ }
@@ -0,0 +1 @@
1
+ 0.33.0