@runuai/host 0.9.13 → 0.9.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/db/migrations/0014_host_inventory_event_index.sql +1 -0
- package/db/migrations/0015_host_settings.sql +9 -0
- package/db/migrations/0016_task_environment.sql +2 -0
- package/db/migrations/meta/_journal.json +21 -0
- package/db/schema.ts +80 -30
- package/images/standard/Dockerfile +36 -10
- package/images/standard/README.md +63 -18
- package/images/standard/container/corepack-version +1 -0
- package/images/standard/container/uai-init +308 -38
- package/images/standard/container/uai-materialize-runtimes +1527 -0
- package/lib/agent-cli.ts +69 -4
- package/lib/agent.ts +46 -7
- package/lib/agents/claude.ts +13 -8
- package/lib/agents/codex.ts +11 -6
- package/lib/agents/cursor.ts +39 -29
- package/lib/agents/durable-proc.ts +20 -27
- package/lib/agents/factory.ts +9 -25
- package/lib/agents/grok.ts +43 -30
- package/lib/agents/kimi.ts +44 -29
- package/lib/agents/opencode.ts +43 -31
- package/lib/agents/proc.ts +149 -114
- package/lib/agents/transport.ts +62 -50
- package/lib/agents/types.ts +6 -4
- package/lib/apple-runtime-recycle.ts +236 -0
- package/lib/apple-uninstall-teardown.ts +224 -0
- package/lib/browser-testing.ts +233 -93
- package/lib/codex-auth.ts +40 -6
- package/lib/command-db.ts +20 -0
- package/lib/container-runtime.ts +1338 -0
- package/lib/db.ts +1 -0
- package/lib/docker-exec.ts +87 -5
- package/lib/engine-accounts.ts +68 -5
- package/lib/engine-login.ts +1952 -0
- package/lib/enrollment-state.ts +251 -0
- package/lib/env-file.ts +155 -0
- package/lib/env.ts +4 -0
- package/lib/git-diff.ts +98 -32
- package/lib/git-identity.ts +199 -87
- package/lib/github-tokens.ts +202 -91
- package/lib/host-cloud-url.ts +62 -0
- package/lib/host-config.ts +279 -0
- package/lib/host-logs.ts +962 -0
- package/lib/keyed-promise-tail.ts +23 -0
- package/lib/legacy-runtime-v1.fixture.ts +627 -0
- package/lib/managed-activation-watcher.ts +72 -0
- package/lib/managed-install-owner-watcher.ts +55 -0
- package/lib/managed-operation-drain.ts +49 -0
- package/lib/managed-runtime.ts +3644 -0
- package/lib/managed-update-scheduler.ts +125 -0
- package/lib/mcp-gateway.ts +450 -23
- package/lib/orchestrator.ts +3070 -223
- package/lib/preview-sidecar.ts +57 -13
- package/lib/release-manifest.ts +708 -0
- package/lib/release-trust.ts +28 -0
- package/lib/runtime-activation-tail.ts +232 -0
- package/lib/runtime-archive.ts +1086 -0
- package/lib/runtime-authority.ts +79 -0
- package/lib/runtime-guard.ts +36 -0
- package/lib/runtime-provider-state.ts +169 -0
- package/lib/runtime-state.ts +232 -12
- package/lib/skills.ts +24 -3
- package/lib/ssh.ts +18 -0
- package/lib/standard-image.ts +1104 -141
- package/lib/stopped-task-status-queue.ts +44 -0
- package/lib/task-container-cli.ts +269 -0
- package/lib/task-diff.ts +66 -46
- package/lib/task-environment/apple-container.ts +757 -0
- package/lib/task-environment/docker.ts +945 -0
- package/lib/task-environment/index.ts +364 -0
- package/lib/task-environment/legacy-adoption.ts +443 -0
- package/lib/task-environment/registry.ts +58 -0
- package/lib/task-environment/types.ts +408 -0
- package/lib/task-identity.ts +19 -0
- package/lib/task-inventory.ts +585 -0
- package/lib/tunnel-registry.ts +135 -19
- package/lib/tunnel-runtime.ts +235 -0
- package/package.json +1 -1
- package/scripts/agent/_common.sh +123 -3
- package/scripts/agent/task-down.sh +146 -38
- package/scripts/agent/task-status.sh +19 -3
- package/scripts/agent/task-up.sh +1463 -109
- package/scripts/install/darwin.ts +848 -50
- package/scripts/install/linux.ts +838 -35
- package/scripts/install/types.ts +43 -0
- package/scripts/install/util.ts +215 -8
- package/scripts/install/win.ts +12 -0
- package/src/apple-tunnel-route.ts +104 -0
- package/src/cli.ts +1464 -72
- package/src/event-outbox.ts +83 -4
- package/src/index.ts +871 -50
- package/src/main.ts +1398 -255
- package/src/paths.ts +17 -1
- package/src/protocol.ts +695 -1
- package/src/runtime-bootstrap.ts +165 -0
- package/src/ui/server.ts +46 -10
- package/src/ui/types.ts +37 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/
|
|
1
|
+
#!/bin/bash
|
|
2
2
|
# uai-init — prepare a standard-image task container (ADR-022).
|
|
3
3
|
#
|
|
4
4
|
# Runs INSIDE the container (Linux, bash 5 — may use anything). Invoked by
|
|
@@ -9,31 +9,278 @@
|
|
|
9
9
|
# per selected project. A 0-project (scratchpad) task has an empty /workspace.
|
|
10
10
|
#
|
|
11
11
|
# Responsibilities:
|
|
12
|
-
# 1. For each /workspace/<slug>/
|
|
13
|
-
#
|
|
14
|
-
#
|
|
12
|
+
# 1. For each /workspace/<slug>/ project, run a dependency-install heuristic
|
|
13
|
+
# for the folder's stack. task-up has already materialized and verified
|
|
14
|
+
# its declared runtimes or the image's root defaults before this task
|
|
15
|
+
# container starts; this script never mutates the shared asdf volume.
|
|
15
16
|
# 2. Launch code-server on 0.0.0.0:8080 (auth disabled — reached only via
|
|
16
17
|
# the authenticated tunnel), opening /workspace.
|
|
17
18
|
#
|
|
18
|
-
# Best-effort + idempotent:
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
19
|
+
# Best-effort + idempotent: an ordinary failing folder logs and is skipped.
|
|
20
|
+
# Re-running when things are already up is a no-op. A host runtime-authority
|
|
21
|
+
# violation exits 78 so task-up/recovery can quarantine the task instead of
|
|
22
|
+
# starting agents against a runtime context that was never proved.
|
|
22
23
|
|
|
23
24
|
set -uo pipefail
|
|
24
25
|
|
|
25
26
|
WORKSPACE="${UAI_WORKSPACE:-/workspace}"
|
|
26
|
-
|
|
27
|
+
RUNTIME_PROJECTS="${UAI_RUNTIME_PROJECTS:-/run/uai/runtime-projects}"
|
|
28
|
+
|
|
29
|
+
# Compose owns runtime/cache selection. Reassert it here for rolling recovery
|
|
30
|
+
# and remove every inherited asdf/Corepack/npm authority variable before any
|
|
31
|
+
# dependency command resolves a runtime. No command in this script installs or
|
|
32
|
+
# reshims. Direct source-tree tests may prepend stubs; the packaged path never
|
|
33
|
+
# honors those test-only seams. Bash does not import environment names that
|
|
34
|
+
# contain `-`, but npm treats LOGS-DIR and LOGS_DIR as the same setting. Strip
|
|
35
|
+
# every owned spelling with GNU/BSD env before continuing, then export only
|
|
36
|
+
# Uai's exact canonical names. Unrelated registry/auth npm config survives.
|
|
37
|
+
npm_sanitize_args=()
|
|
38
|
+
while IFS= read -r -d '' environment_entry; do
|
|
39
|
+
environment_name=${environment_entry%%=*}
|
|
40
|
+
case "$environment_name" in
|
|
41
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Pp][Rr][Ee][Ff][Ii][Xx] | \
|
|
42
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Cc][Aa][Cc][Hh][Ee] | \
|
|
43
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Ll][Oo][Gg][Ss][_-][Dd][Ii][Rr] | \
|
|
44
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Uu][Mm][Aa][Ss][Kk])
|
|
45
|
+
npm_sanitize_args+=( -u "$environment_name" )
|
|
46
|
+
;;
|
|
47
|
+
esac
|
|
48
|
+
done < <(/usr/bin/env -0)
|
|
49
|
+
if [ "${#npm_sanitize_args[@]}" -gt 0 ]; then
|
|
50
|
+
exec /usr/bin/env "${npm_sanitize_args[@]}" \
|
|
51
|
+
/bin/bash "${BASH_SOURCE[0]}" "$@"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
for runtime_var in $(compgen -A variable 2>/dev/null); do
|
|
55
|
+
case "$runtime_var" in
|
|
56
|
+
ASDF_* | COREPACK_* | \
|
|
57
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Pp][Rr][Ee][Ff][Ii][Xx] | \
|
|
58
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Cc][Aa][Cc][Hh][Ee] | \
|
|
59
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Ll][Oo][Gg][Ss]_[Dd][Ii][Rr] | \
|
|
60
|
+
[Nn][Pp][Mm]_[Cc][Oo][Nn][Ff][Ii][Gg]_[Uu][Mm][Aa][Ss][Kk])
|
|
61
|
+
unset "$runtime_var"
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
unset LD_PRELOAD LD_AUDIT LD_LIBRARY_PATH NODE_OPTIONS NODE_PATH PS4 BASH_ENV ENV
|
|
66
|
+
case "${BASH_SOURCE[0]}" in
|
|
67
|
+
/usr/local/bin/uai-init | /uai-init-current) export HOME=/home/node ;;
|
|
68
|
+
esac
|
|
69
|
+
export XDG_CONFIG_HOME="$HOME/.config"
|
|
70
|
+
export XDG_DATA_HOME="$HOME/.local/share"
|
|
71
|
+
export XDG_CACHE_HOME="$HOME/.cache"
|
|
72
|
+
export ASDF_DIR=/opt/asdf
|
|
73
|
+
export ASDF_DATA_DIR=/opt/asdf-data
|
|
74
|
+
export ASDF_CONFIG_FILE=/dev/null
|
|
75
|
+
export ASDF_DEFAULT_TOOL_VERSIONS_FILENAME=.tool-versions
|
|
76
|
+
export ASDF_NODEJS_VERSION=
|
|
77
|
+
export ASDF_PYTHON_VERSION=
|
|
78
|
+
export ASDF_GOLANG_VERSION=
|
|
79
|
+
export ASDF_RUBY_VERSION=
|
|
80
|
+
export ASDF_RUST_VERSION=
|
|
81
|
+
export ASDF_SKIP_RESHIM=1
|
|
82
|
+
export COREPACK_HOME=/opt/asdf-data/corepack
|
|
83
|
+
export COREPACK_DEFAULT_TO_LATEST=0
|
|
84
|
+
export COREPACK_ENV_FILE=0
|
|
85
|
+
export COREPACK_NPM_REGISTRY=https://registry.npmjs.org
|
|
86
|
+
export COREPACK_ENABLE_NETWORK=0
|
|
87
|
+
export COREPACK_ENABLE_AUTO_PIN=0
|
|
88
|
+
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
89
|
+
export COREPACK_ENABLE_PROJECT_SPEC=1
|
|
90
|
+
export COREPACK_ENABLE_STRICT=1
|
|
91
|
+
export COREPACK_ENABLE_UNSAFE_CUSTOM_URLS=0
|
|
92
|
+
export COREPACK_INTEGRITY_CHECK=1
|
|
93
|
+
export NPM_CONFIG_PREFIX="$HOME/.local/share/uai-npm"
|
|
94
|
+
export npm_config_prefix="$NPM_CONFIG_PREFIX"
|
|
95
|
+
export NPM_CONFIG_CACHE="$HOME/.cache/uai-npm"
|
|
96
|
+
export npm_config_cache="$NPM_CONFIG_CACHE"
|
|
97
|
+
export NPM_CONFIG_LOGS_DIR="$NPM_CONFIG_CACHE/_logs"
|
|
98
|
+
export npm_config_logs_dir="$NPM_CONFIG_LOGS_DIR"
|
|
99
|
+
export NPM_CONFIG_UMASK=0002
|
|
100
|
+
export npm_config_umask=0002
|
|
101
|
+
export NODE_OPTIONS=
|
|
102
|
+
export NODE_PATH=
|
|
103
|
+
export PATH="/opt/asdf-data/shims:/opt/asdf/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$NPM_CONFIG_PREFIX/bin"
|
|
104
|
+
# Fixed here, not from the environment: the packaged path must not let a
|
|
105
|
+
# forged mount table or lower path defeat the shared-cache backstop below.
|
|
106
|
+
UAI_PROC_MOUNTS=/proc/mounts
|
|
107
|
+
UAI_ASDF_LOWER_DIR=/run/uai/asdf-lower
|
|
108
|
+
case "${BASH_SOURCE[0]}" in
|
|
109
|
+
/usr/local/bin/uai-init | /uai-init-current) ;;
|
|
110
|
+
*)
|
|
111
|
+
if [ -n "${UAI_INIT_TEST_ASDF_DATA_DIR:-}" ]; then
|
|
112
|
+
export ASDF_DATA_DIR="$UAI_INIT_TEST_ASDF_DATA_DIR"
|
|
113
|
+
export COREPACK_HOME="$ASDF_DATA_DIR/corepack"
|
|
114
|
+
fi
|
|
115
|
+
if [ -n "${UAI_INIT_TEST_PATH:-}" ]; then
|
|
116
|
+
export PATH="$UAI_INIT_TEST_PATH:$PATH"
|
|
117
|
+
fi
|
|
118
|
+
if [ -n "${UAI_INIT_TEST_PROC_MOUNTS:-}" ]; then
|
|
119
|
+
UAI_PROC_MOUNTS="$UAI_INIT_TEST_PROC_MOUNTS"
|
|
120
|
+
fi
|
|
121
|
+
if [ -n "${UAI_INIT_TEST_ASDF_LOWER_DIR:-}" ]; then
|
|
122
|
+
UAI_ASDF_LOWER_DIR="$UAI_INIT_TEST_ASDF_LOWER_DIR"
|
|
123
|
+
fi
|
|
124
|
+
;;
|
|
125
|
+
esac
|
|
126
|
+
ASDF_SH="$ASDF_DIR/asdf.sh"
|
|
27
127
|
|
|
28
128
|
log() { echo "uai-init: $*"; }
|
|
29
129
|
|
|
130
|
+
reject_writable_shared_runtime_cache() {
|
|
131
|
+
# The host proves the Docker mount metadata before invoking us. Keep an
|
|
132
|
+
# in-container backstop as well so a current initializer staged into a
|
|
133
|
+
# legacy/recovered container cannot become a shared-cache writer. This is a
|
|
134
|
+
# metadata/access check only: never create even a temporary shared file.
|
|
135
|
+
if [ ! -d "$ASDF_DATA_DIR" ]; then
|
|
136
|
+
log "error: shared runtime cache is missing; refusing dependency or editor startup"
|
|
137
|
+
return 1
|
|
138
|
+
fi
|
|
139
|
+
# ADR-108 (apple): /opt/asdf-data is an in-guest overlay — writable BY
|
|
140
|
+
# DESIGN, with every write landing in the task-private upper layer. That is
|
|
141
|
+
# safe exactly when the overlay is real (this mount table, not trust) and
|
|
142
|
+
# its lower is the read-only shared attachment; the shared volume then
|
|
143
|
+
# still cannot be written from this container. Any other writable shape
|
|
144
|
+
# remains the violation it always was.
|
|
145
|
+
# /usr/bin paths: present on both merged-usr Debian (in-container) and
|
|
146
|
+
# macOS (the direct source-tree test harness). Token-exact matching against
|
|
147
|
+
# comma-delimited mount options — substring matching accepted
|
|
148
|
+
# lowerdir=<lower>-evil — plus exactly ONE effective mount at the merged
|
|
149
|
+
# path (a later mount would shadow the proven one).
|
|
150
|
+
local overlay_lines="" any_lines="" overlay_opts=""
|
|
151
|
+
overlay_lines=$(/usr/bin/grep -c "^overlay ${ASDF_DATA_DIR} overlay " \
|
|
152
|
+
"$UAI_PROC_MOUNTS" 2>/dev/null) || overlay_lines=0
|
|
153
|
+
if [ "$overlay_lines" != "0" ]; then
|
|
154
|
+
if [ "$overlay_lines" != "1" ]; then
|
|
155
|
+
log "error: expected exactly one shared runtime cache overlay, found $overlay_lines; refusing dependency or editor startup"
|
|
156
|
+
return 1
|
|
157
|
+
fi
|
|
158
|
+
any_lines=$(/usr/bin/grep -c " ${ASDF_DATA_DIR} " "$UAI_PROC_MOUNTS" \
|
|
159
|
+
2>/dev/null) || any_lines=0
|
|
160
|
+
if [ "$any_lines" != "1" ]; then
|
|
161
|
+
log "error: unexpected extra mounts at the shared runtime cache; refusing dependency or editor startup"
|
|
162
|
+
return 1
|
|
163
|
+
fi
|
|
164
|
+
overlay_opts=$(/usr/bin/grep "^overlay ${ASDF_DATA_DIR} overlay " \
|
|
165
|
+
"$UAI_PROC_MOUNTS" | /usr/bin/cut -d" " -f4)
|
|
166
|
+
case ",$overlay_opts," in
|
|
167
|
+
*",lowerdir=${UAI_ASDF_LOWER_DIR},"*) ;;
|
|
168
|
+
*)
|
|
169
|
+
log "error: shared runtime cache overlay has a foreign lower; refusing dependency or editor startup"
|
|
170
|
+
return 1
|
|
171
|
+
;;
|
|
172
|
+
esac
|
|
173
|
+
case ",$overlay_opts," in
|
|
174
|
+
*",upperdir=/var/lib/uai/asdf-upper,"*) ;;
|
|
175
|
+
*)
|
|
176
|
+
log "error: shared runtime cache overlay has a foreign upper; refusing dependency or editor startup"
|
|
177
|
+
return 1
|
|
178
|
+
;;
|
|
179
|
+
esac
|
|
180
|
+
case ",$overlay_opts," in
|
|
181
|
+
*",workdir=/var/lib/uai/asdf-work,"*) ;;
|
|
182
|
+
*)
|
|
183
|
+
log "error: shared runtime cache overlay has a foreign workdir; refusing dependency or editor startup"
|
|
184
|
+
return 1
|
|
185
|
+
;;
|
|
186
|
+
esac
|
|
187
|
+
if [ ! -d "$UAI_ASDF_LOWER_DIR" ]; then
|
|
188
|
+
log "error: shared runtime cache lower attachment is missing; refusing dependency or editor startup"
|
|
189
|
+
return 1
|
|
190
|
+
fi
|
|
191
|
+
if [ -w "$UAI_ASDF_LOWER_DIR" ]; then
|
|
192
|
+
log "error: shared runtime cache lower attachment is writable; refusing dependency or editor startup"
|
|
193
|
+
return 1
|
|
194
|
+
fi
|
|
195
|
+
return 0
|
|
196
|
+
fi
|
|
197
|
+
if [ -w "$ASDF_DATA_DIR" ]; then
|
|
198
|
+
log "error: shared runtime cache is writable in the task container; refusing dependency or editor startup"
|
|
199
|
+
return 1
|
|
200
|
+
fi
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
prepare_private_npm_authority() {
|
|
204
|
+
local path prefix_probe="" cache_probe="" logs_probe=""
|
|
205
|
+
|
|
206
|
+
# These directories live in the task container layer, never on the shared
|
|
207
|
+
# asdf volume. Reject redirects before mkdir, then prove both exact write
|
|
208
|
+
# targets. The task has not started any agents yet, so there is no concurrent
|
|
209
|
+
# task process that can race these checks.
|
|
210
|
+
for path in \
|
|
211
|
+
"$HOME/.local" \
|
|
212
|
+
"$HOME/.local/share" \
|
|
213
|
+
"$NPM_CONFIG_PREFIX" \
|
|
214
|
+
"$HOME/.cache" \
|
|
215
|
+
"$NPM_CONFIG_CACHE" \
|
|
216
|
+
"$NPM_CONFIG_LOGS_DIR"
|
|
217
|
+
do
|
|
218
|
+
if [ -L "$path" ] || { [ -e "$path" ] && [ ! -d "$path" ]; }; then
|
|
219
|
+
log "error: private npm authority path is unsafe: $path"
|
|
220
|
+
return 1
|
|
221
|
+
fi
|
|
222
|
+
done
|
|
223
|
+
|
|
224
|
+
if ! /bin/mkdir -p -- \
|
|
225
|
+
"$NPM_CONFIG_PREFIX/bin" "$NPM_CONFIG_CACHE" "$NPM_CONFIG_LOGS_DIR"; then
|
|
226
|
+
log "error: could not prepare the private npm prefix/cache"
|
|
227
|
+
return 1
|
|
228
|
+
fi
|
|
229
|
+
for path in \
|
|
230
|
+
"$NPM_CONFIG_PREFIX" \
|
|
231
|
+
"$NPM_CONFIG_PREFIX/bin" \
|
|
232
|
+
"$NPM_CONFIG_CACHE" \
|
|
233
|
+
"$NPM_CONFIG_LOGS_DIR"
|
|
234
|
+
do
|
|
235
|
+
if [ -L "$path" ] || [ ! -d "$path" ]; then
|
|
236
|
+
log "error: private npm authority path changed during preparation: $path"
|
|
237
|
+
return 1
|
|
238
|
+
fi
|
|
239
|
+
done
|
|
240
|
+
|
|
241
|
+
prefix_probe=$(/usr/bin/mktemp \
|
|
242
|
+
"$NPM_CONFIG_PREFIX/.uai-write-probe.XXXXXX") || {
|
|
243
|
+
log "error: private npm prefix is not writable"
|
|
244
|
+
return 1
|
|
245
|
+
}
|
|
246
|
+
cache_probe=$(/usr/bin/mktemp \
|
|
247
|
+
"$NPM_CONFIG_CACHE/.uai-write-probe.XXXXXX") || {
|
|
248
|
+
/bin/rm -f -- "$prefix_probe"
|
|
249
|
+
log "error: private npm cache is not writable"
|
|
250
|
+
return 1
|
|
251
|
+
}
|
|
252
|
+
logs_probe=$(/usr/bin/mktemp \
|
|
253
|
+
"$NPM_CONFIG_LOGS_DIR/.uai-write-probe.XXXXXX") || {
|
|
254
|
+
/bin/rm -f -- "$prefix_probe" "$cache_probe"
|
|
255
|
+
log "error: private npm logs directory is not writable"
|
|
256
|
+
return 1
|
|
257
|
+
}
|
|
258
|
+
/bin/rm -f -- "$prefix_probe" "$cache_probe" "$logs_probe"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
reject_workspace_runtime_shadow() {
|
|
262
|
+
local shadow="$WORKSPACE/.tool-versions"
|
|
263
|
+
# The root is never a selected project. Any entry here changes asdf lookup
|
|
264
|
+
# for no-manifest projects without being part of the pre-start proof. Check
|
|
265
|
+
# metadata only so a FIFO/socket/device cannot block initialization.
|
|
266
|
+
if [ -L "$shadow" ] || [ -e "$shadow" ]; then
|
|
267
|
+
log "error: workspace-root .tool-versions appeared after runtime proof; refusing dependency or editor startup"
|
|
268
|
+
return 1
|
|
269
|
+
fi
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# Fail before sourcing asdf or executing any project input if this is an old
|
|
273
|
+
# writable task mount, or if task-private npm state redirects somewhere else.
|
|
274
|
+
reject_writable_shared_runtime_cache || exit 78
|
|
275
|
+
prepare_private_npm_authority || exit 78
|
|
276
|
+
|
|
30
277
|
# Make asdf + its shims available to this non-interactive shell. A bare
|
|
31
278
|
# `docker exec` sources no profile, so source asdf explicitly here.
|
|
32
279
|
if [ -f "$ASDF_SH" ]; then
|
|
33
280
|
# shellcheck disable=SC1090
|
|
34
281
|
. "$ASDF_SH"
|
|
35
282
|
else
|
|
36
|
-
log "warning: asdf not found at $ASDF_SH; runtime
|
|
283
|
+
log "warning: asdf not found at $ASDF_SH; dependency runtime resolution may fail"
|
|
37
284
|
fi
|
|
38
285
|
|
|
39
286
|
# ---------------------------------------------------------------------------
|
|
@@ -98,7 +345,7 @@ if [ ! -f "$cc_settings" ]; then
|
|
|
98
345
|
fi
|
|
99
346
|
|
|
100
347
|
# ---------------------------------------------------------------------------
|
|
101
|
-
# 1. Per-folder
|
|
348
|
+
# 1. Per-folder dependency install (runtimes are already materialized).
|
|
102
349
|
# ---------------------------------------------------------------------------
|
|
103
350
|
|
|
104
351
|
install_node_deps() {
|
|
@@ -117,10 +364,6 @@ install_node_deps() {
|
|
|
117
364
|
fi
|
|
118
365
|
fi
|
|
119
366
|
|
|
120
|
-
# corepack ships with Node and resolves pnpm/yarn per the packageManager
|
|
121
|
-
# field; harmless no-op when already enabled.
|
|
122
|
-
corepack enable >/dev/null 2>&1 || true
|
|
123
|
-
|
|
124
367
|
case "$pm" in
|
|
125
368
|
pnpm) log "pnpm install"; pnpm install ;;
|
|
126
369
|
yarn) log "yarn install"; yarn install ;;
|
|
@@ -148,20 +391,14 @@ install_python_deps() {
|
|
|
148
391
|
fi
|
|
149
392
|
}
|
|
150
393
|
|
|
151
|
-
|
|
394
|
+
install_folder_deps() {
|
|
152
395
|
local dir="$1" name
|
|
153
396
|
name=$(basename "$dir")
|
|
154
397
|
|
|
155
|
-
# Enter the folder; asdf auto-resolves
|
|
398
|
+
# Enter the folder; asdf auto-resolves either its already-materialized local
|
|
399
|
+
# declarations or the already-proven root image defaults.
|
|
156
400
|
cd "$dir" || { log "cannot cd into $name — skipping"; return; }
|
|
157
401
|
|
|
158
|
-
# Runtime install. asdf reads the in-scope .tool-versions and downloads any
|
|
159
|
-
# missing versions (cached on the /opt/asdf-data volume).
|
|
160
|
-
if command -v asdf >/dev/null 2>&1; then
|
|
161
|
-
log "asdf install ($name)"
|
|
162
|
-
asdf install || log "asdf install failed in $name — continuing"
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
402
|
# Dependency-install heuristic. Run every matcher that applies (a polyglot
|
|
166
403
|
# folder may have more than one); each is independently best-effort.
|
|
167
404
|
if [ -f package.json ]; then
|
|
@@ -180,23 +417,43 @@ install_folder() {
|
|
|
180
417
|
fi
|
|
181
418
|
}
|
|
182
419
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
log "no .tool-versions in $(basename "$folder") — skipping per-folder install"
|
|
420
|
+
reject_workspace_runtime_shadow || exit 78
|
|
421
|
+
|
|
422
|
+
if [ -d "$WORKSPACE" ] && [ -f "$RUNTIME_PROJECTS" ] \
|
|
423
|
+
&& [ ! -L "$RUNTIME_PROJECTS" ]; then
|
|
424
|
+
seen_runtime_projects="|"
|
|
425
|
+
while IFS= read -r project_slug || [ -n "$project_slug" ]; do
|
|
426
|
+
if [[ ! "$project_slug" =~ ^[a-z0-9-]{1,64}$ ]]; then
|
|
427
|
+
log "warning: unsafe runtime-project allowlist entry — skipping"
|
|
428
|
+
continue
|
|
193
429
|
fi
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
430
|
+
case "$seen_runtime_projects" in
|
|
431
|
+
*"|$project_slug|"*)
|
|
432
|
+
log "warning: duplicate runtime-project entry $project_slug — skipping"
|
|
433
|
+
continue
|
|
434
|
+
;;
|
|
435
|
+
esac
|
|
436
|
+
seen_runtime_projects="${seen_runtime_projects}${project_slug}|"
|
|
437
|
+
folder="$WORKSPACE/$project_slug"
|
|
438
|
+
git_marker="$folder/.git"
|
|
439
|
+
if [ -L "$folder" ] || [ ! -d "$folder" ] || [ -L "$git_marker" ] \
|
|
440
|
+
|| { [ ! -f "$git_marker" ] && [ ! -d "$git_marker" ]; }; then
|
|
441
|
+
log "warning: allowlisted project $project_slug is not a safe Git worktree — skipping"
|
|
442
|
+
continue
|
|
443
|
+
fi
|
|
444
|
+
reject_workspace_runtime_shadow || exit 78
|
|
445
|
+
# Run in a subshell so a `cd` (or a failing command under the relaxed
|
|
446
|
+
# error mode) in one folder never leaks into the next.
|
|
447
|
+
( install_folder_deps "$folder" )
|
|
448
|
+
done < "$RUNTIME_PROJECTS"
|
|
449
|
+
elif [ ! -d "$WORKSPACE" ]; then
|
|
197
450
|
log "workspace $WORKSPACE missing — scratchpad/no-project task, skipping installs"
|
|
451
|
+
else
|
|
452
|
+
log "warning: trusted runtime-project allowlist missing or unsafe — skipping installs"
|
|
198
453
|
fi
|
|
199
454
|
|
|
455
|
+
reject_workspace_runtime_shadow || exit 78
|
|
456
|
+
|
|
200
457
|
# ---------------------------------------------------------------------------
|
|
201
458
|
# 2. code-server — the Editor pane. Bound to 0.0.0.0:8080, auth disabled
|
|
202
459
|
# (reached only through the authenticated tunnel / editor proxy). Opens
|
|
@@ -263,7 +520,20 @@ if pgrep -f 'code-server.*--bind-addr' >/dev/null 2>&1; then
|
|
|
263
520
|
log "code-server already running"
|
|
264
521
|
else
|
|
265
522
|
log "launching code-server on 0.0.0.0:8080"
|
|
266
|
-
|
|
523
|
+
# Compose deliberately replaces the image PATH with Uai's runtime authority
|
|
524
|
+
# path, which excludes task-writable home bins. code-server is an image-owned
|
|
525
|
+
# standalone install, so invoke its exact baked path instead of relying on
|
|
526
|
+
# PATH. A source-tree test invocation may resolve its isolated stub, but the
|
|
527
|
+
# two production destinations never consult PATH for this executable.
|
|
528
|
+
code_server_bin=/home/node/.local/bin/code-server
|
|
529
|
+
case "${BASH_SOURCE[0]}" in
|
|
530
|
+
/usr/local/bin/uai-init | /uai-init-current) ;;
|
|
531
|
+
*)
|
|
532
|
+
test_code_server_bin=$(command -v code-server 2>/dev/null || true)
|
|
533
|
+
[ -z "$test_code_server_bin" ] || code_server_bin="$test_code_server_bin"
|
|
534
|
+
;;
|
|
535
|
+
esac
|
|
536
|
+
nohup "$code_server_bin" \
|
|
267
537
|
--user-data-dir "$cs_data_dir" \
|
|
268
538
|
--disable-workspace-trust \
|
|
269
539
|
--auth none \
|