@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
|
@@ -14,8 +14,15 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
14
14
|
[ "$#" -eq 1 ] || emit_err "BAD_ARGS" "usage: task-down <taskId>" "args"
|
|
15
15
|
|
|
16
16
|
task_id="$1"
|
|
17
|
+
# Make the ASCII security grammar independent of the operator's collation.
|
|
18
|
+
export LC_ALL=C
|
|
19
|
+
if [ "${#task_id}" -gt 128 ] ||
|
|
20
|
+
[[ ! "$task_id" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
|
|
21
|
+
emit_err "BAD_ARGS" "invalid task id" "task identity validation"
|
|
22
|
+
fi
|
|
17
23
|
UAI_TASK_ID="$task_id"
|
|
18
24
|
setup_err_trap
|
|
25
|
+
validate_task_runtime_selection
|
|
19
26
|
|
|
20
27
|
step "TASK_NOT_FOUND" "read task row"
|
|
21
28
|
# Teardown must never depend on bookkeeping: a broken/missing command
|
|
@@ -26,51 +33,150 @@ task_json=$(db_get_task "$task_id" 2>/dev/null) || task_json="[]"
|
|
|
26
33
|
row_count=$(jq 'length' <<<"$task_json" 2>/dev/null) || row_count=0
|
|
27
34
|
if [ "$row_count" -lt 1 ]; then
|
|
28
35
|
log "task row unavailable — tearing down by derived names"
|
|
29
|
-
worktree_path=""
|
|
30
|
-
compose_project="$(compose_project_for_task "$task_id")"
|
|
31
36
|
else
|
|
32
|
-
|
|
33
|
-
compose_project=$(jq -r '.[0].compose_project // ""' <<<"$task_json")
|
|
34
|
-
fi
|
|
35
|
-
# A task-up failure can leave an error row with compose_project=NULL while its
|
|
36
|
-
# conventionally named container still exists. Bookkeeping must never suppress
|
|
37
|
-
# teardown; the compose project is deterministic from the task id.
|
|
38
|
-
[ -n "$compose_project" ] || compose_project=$(compose_project_for_task "$task_id")
|
|
39
|
-
|
|
40
|
-
# task_dir falls back to the derived path when the row's worktree_path is
|
|
41
|
-
# missing (e.g. crashed before persisting).
|
|
42
|
-
task_dir="$worktree_path"
|
|
43
|
-
if [ -z "$task_dir" ]; then
|
|
44
|
-
task_dir="$UAI_WORKSPACE_ROOT/tasks/$task_id"
|
|
37
|
+
UAI_RETRY_STATUS=$(jq -r '.[0].status // ""' <<<"$task_json")
|
|
45
38
|
fi
|
|
39
|
+
# Persisted runtime paths are recovery hints, never deletion authority. A
|
|
40
|
+
# corrupt row must not aim compose/rm outside this exact task identity.
|
|
41
|
+
compose_project=$(compose_project_for_task "$task_id")
|
|
42
|
+
task_dir="$UAI_WORKSPACE_ROOT/tasks/$task_id"
|
|
46
43
|
|
|
47
44
|
# -----------------------------------------------------------------------------
|
|
48
45
|
# 1. Compose stack.
|
|
49
46
|
# -----------------------------------------------------------------------------
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
if [ "$TASK_RUNTIME_MODE" = "apple-container" ]; then
|
|
49
|
+
step "COMPOSE_DOWN_FAILED" "remove task container"
|
|
50
|
+
app_container=$(apple_app_container_for_task "$task_id")
|
|
51
|
+
derived_image="uai-task-${task_id}"
|
|
52
|
+
# Ownership proof before destruction (review 2026-08-18 rounds 5+6): every
|
|
53
|
+
# container Uai creates carries com.uai.task=<id>. Destruction requires the
|
|
54
|
+
# inspect response to prove EXACT identity — exactly one record, whose
|
|
55
|
+
# top-level id is the requested name, carrying the exact label — the same
|
|
56
|
+
# response-confusion discipline recovery applies. A container merely
|
|
57
|
+
# OCCUPYING the reserved name, or a confused/multi-record answer, is not
|
|
58
|
+
# authority to delete anything.
|
|
59
|
+
if existing_json=$(crt inspect "$app_container" 2>/dev/null); then
|
|
60
|
+
existing_ok=$(printf '%s' "$existing_json" \
|
|
61
|
+
| jq -r --arg name "$app_container" --arg tid "$task_id" '
|
|
62
|
+
if (type == "array" and length == 1
|
|
63
|
+
and (.[0] | type == "object")
|
|
64
|
+
and .[0].id == $name
|
|
65
|
+
and ((.[0].configuration.labels["com.uai.task"] // "") == $tid))
|
|
66
|
+
then "ok" else "mismatch" end' 2>/dev/null) \
|
|
67
|
+
|| existing_ok="mismatch"
|
|
68
|
+
if [ "$existing_ok" != "ok" ]; then
|
|
69
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
70
|
+
"A container named $app_container exists but could not be proven as this task's own (exact identity + ownership label); Uai will not delete a container it cannot prove it owns. Remove or rename it, then retry." \
|
|
71
|
+
"remove task container"
|
|
72
|
+
fi
|
|
73
|
+
crt stop --time 10 "$app_container" >/dev/null 2>&1 || true
|
|
74
|
+
crt delete --force "$app_container" >/dev/null 2>&1 || true
|
|
75
|
+
fi
|
|
76
|
+
# A derived image is task-private; removing it mirrors `down --rmi local`.
|
|
77
|
+
crt image delete "$derived_image" >/dev/null 2>&1 || true
|
|
78
|
+
# Destruction is not best-effort: prove the container is gone before the
|
|
79
|
+
# writable Git data below is deleted or success is reported. A CLI failure
|
|
80
|
+
# that does not name this container as not-found proves nothing — the
|
|
81
|
+
# workspace and its Git data must survive until absence is confirmed. The
|
|
82
|
+
# Apple runtime has no per-task networks, sidecars, or anonymous volumes.
|
|
83
|
+
if crt inspect "$app_container" >/dev/null 2>&1; then
|
|
84
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
85
|
+
"task runtime resources remain after teardown" \
|
|
86
|
+
"container verification after cleanup"
|
|
87
|
+
fi
|
|
88
|
+
if ! apple_container_proven_absent "$app_container"; then
|
|
89
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
90
|
+
"could not verify task container removal; refusing to delete the task workspace" \
|
|
91
|
+
"container verification after cleanup"
|
|
92
|
+
fi
|
|
55
93
|
else
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
step "COMPOSE_DOWN_FAILED" "docker compose down -v --rmi local"
|
|
95
|
+
if [ ! -L "$task_dir" ] && [ -f "$task_dir/.uai/docker-compose.yml" ]; then
|
|
96
|
+
docker compose -p "$compose_project" -f "$task_dir/.uai/docker-compose.yml" \
|
|
97
|
+
down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
|
|
98
|
+
else
|
|
99
|
+
# Fallback: tear down by project name only.
|
|
100
|
+
docker compose -p "$compose_project" down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
|
|
101
|
+
fi
|
|
102
|
+
# `compose down` is intentionally best-effort because an already-absent
|
|
103
|
+
# stack can make a config-less fallback fail. Destruction itself is not
|
|
104
|
+
# best-effort: before deleting the worktree or reporting success, prove that
|
|
105
|
+
# no task-owned container/network/volume remains under exact labels.
|
|
106
|
+
preview_containers=""
|
|
107
|
+
if ! preview_containers=$(docker ps -aq \
|
|
108
|
+
--filter "label=com.runuai.preview-sidecar.task=$task_id"); then
|
|
109
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
110
|
+
"could not verify preview teardown for task $task_id" \
|
|
111
|
+
"docker ps after preview teardown"
|
|
112
|
+
fi
|
|
113
|
+
if [ -n "$preview_containers" ]; then
|
|
114
|
+
# Docker ids contain no whitespace; intentional splitting passes each exact
|
|
115
|
+
# id as its own argv entry.
|
|
116
|
+
# shellcheck disable=SC2086
|
|
117
|
+
docker rm -f $preview_containers >/dev/null 2>&1 || \
|
|
118
|
+
emit_err "COMPOSE_DOWN_FAILED" "could not remove task preview sidecars" "docker rm previews"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
remaining_containers=""
|
|
122
|
+
if ! remaining_containers=$(docker ps -aq \
|
|
123
|
+
--filter "label=com.docker.compose.project=$compose_project"); then
|
|
124
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
125
|
+
"could not verify teardown for compose project $compose_project" \
|
|
126
|
+
"docker ps after compose down"
|
|
127
|
+
fi
|
|
128
|
+
if [ -n "$remaining_containers" ]; then
|
|
129
|
+
# shellcheck disable=SC2086
|
|
130
|
+
docker rm -f $remaining_containers >/dev/null 2>&1 || \
|
|
131
|
+
emit_err "COMPOSE_DOWN_FAILED" "could not remove compose containers" "docker rm after compose down"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
remaining_networks=""
|
|
135
|
+
if ! remaining_networks=$(docker network ls -q \
|
|
136
|
+
--filter "label=com.docker.compose.project=$compose_project"); then
|
|
137
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
138
|
+
"could not verify compose networks for $compose_project" \
|
|
139
|
+
"docker network ls after compose down"
|
|
140
|
+
fi
|
|
141
|
+
if [ -n "$remaining_networks" ]; then
|
|
142
|
+
# shellcheck disable=SC2086
|
|
143
|
+
docker network rm $remaining_networks >/dev/null 2>&1 || \
|
|
144
|
+
emit_err "COMPOSE_DOWN_FAILED" "could not remove compose networks" "docker network rm"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
remaining_volumes=""
|
|
148
|
+
if ! remaining_volumes=$(docker volume ls -q \
|
|
149
|
+
--filter "label=com.docker.compose.project=$compose_project"); then
|
|
150
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
151
|
+
"could not verify compose volumes for $compose_project" \
|
|
152
|
+
"docker volume ls after compose down"
|
|
153
|
+
fi
|
|
154
|
+
if [ -n "$remaining_volumes" ]; then
|
|
155
|
+
# shellcheck disable=SC2086
|
|
156
|
+
docker volume rm $remaining_volumes >/dev/null 2>&1 || \
|
|
157
|
+
emit_err "COMPOSE_DOWN_FAILED" "could not remove compose volumes" "docker volume rm"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Re-query every exact label. A successful remove command is not itself proof,
|
|
161
|
+
# and a failed query is unknown rather than empty.
|
|
162
|
+
verified_previews=""
|
|
163
|
+
verified_containers=""
|
|
164
|
+
verified_networks=""
|
|
165
|
+
verified_volumes=""
|
|
166
|
+
if ! verified_previews=$(docker ps -aq --filter "label=com.runuai.preview-sidecar.task=$task_id") ||
|
|
167
|
+
! verified_containers=$(docker ps -aq --filter "label=com.docker.compose.project=$compose_project") ||
|
|
168
|
+
! verified_networks=$(docker network ls -q --filter "label=com.docker.compose.project=$compose_project") ||
|
|
169
|
+
! verified_volumes=$(docker volume ls -q --filter "label=com.docker.compose.project=$compose_project"); then
|
|
170
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
171
|
+
"could not verify task runtime cleanup" \
|
|
172
|
+
"docker label verification after cleanup"
|
|
173
|
+
fi
|
|
174
|
+
if [ -n "$verified_previews" ] || [ -n "$verified_containers" ] ||
|
|
175
|
+
[ -n "$verified_networks" ] || [ -n "$verified_volumes" ]; then
|
|
176
|
+
emit_err "COMPOSE_DOWN_FAILED" \
|
|
177
|
+
"task runtime resources remain after teardown" \
|
|
178
|
+
"docker label verification after cleanup"
|
|
179
|
+
fi
|
|
74
180
|
fi
|
|
75
181
|
|
|
76
182
|
# -----------------------------------------------------------------------------
|
|
@@ -80,10 +186,12 @@ fi
|
|
|
80
186
|
# that retired `repo.git` path is never trusted by task-up again.
|
|
81
187
|
# -----------------------------------------------------------------------------
|
|
82
188
|
|
|
83
|
-
if [ -
|
|
189
|
+
if [ -e "$task_dir" ] || [ -L "$task_dir" ]; then
|
|
84
190
|
step "WORKTREE_REMOVE_FAILED" "rm -rf task dir"
|
|
85
191
|
rm -rf "$task_dir"
|
|
86
192
|
fi
|
|
193
|
+
[ ! -e "$task_dir" ] && [ ! -L "$task_dir" ] || \
|
|
194
|
+
emit_err "WORKTREE_REMOVE_FAILED" "task directory remains after teardown" "task dir verification"
|
|
87
195
|
|
|
88
196
|
# -----------------------------------------------------------------------------
|
|
89
197
|
# 3. Persist final state.
|
|
@@ -17,6 +17,8 @@ UAI_TASK_ID="$task_id"
|
|
|
17
17
|
setup_err_trap
|
|
18
18
|
|
|
19
19
|
step "TASK_NOT_FOUND" "read task row"
|
|
20
|
+
validate_task_runtime_selection
|
|
21
|
+
|
|
20
22
|
task_json=$(db_get_task "$task_id")
|
|
21
23
|
[ "$(jq 'length' <<<"$task_json")" -ge 1 ] \
|
|
22
24
|
|| emit_err "TASK_NOT_FOUND" "no such task: $task_id" "read task row"
|
|
@@ -24,11 +26,25 @@ task_json=$(db_get_task "$task_id")
|
|
|
24
26
|
compose_project=$(jq -r '.[0].compose_project // ""' <<<"$task_json")
|
|
25
27
|
worktree_path=$(jq -r '.[0].worktree_path // ""' <<<"$task_json")
|
|
26
28
|
|
|
27
|
-
# Containers running under this
|
|
29
|
+
# Containers running under this task, JSON array of names.
|
|
28
30
|
step "DOCKER_PS_FAILED" "docker ps"
|
|
29
31
|
containers_json="[]"
|
|
30
|
-
if [
|
|
31
|
-
|
|
32
|
+
if [ "$TASK_RUNTIME_MODE" = "apple-container" ]; then
|
|
33
|
+
# Absence (inspect exit 1) is a definitive "not running" answer, never an
|
|
34
|
+
# unknown-state error; only a live "running" state lists the container.
|
|
35
|
+
apple_app=$(apple_app_container_for_task "$task_id")
|
|
36
|
+
apple_state=$(crt inspect "$apple_app" 2>/dev/null \
|
|
37
|
+
| jq -r '.[0].status.state // empty' 2>/dev/null) || apple_state=""
|
|
38
|
+
if [ "$apple_state" = "running" ]; then
|
|
39
|
+
containers_json=$(jq -nc --arg n "$apple_app" '[$n]')
|
|
40
|
+
fi
|
|
41
|
+
elif [ -n "$compose_project" ]; then
|
|
42
|
+
if ! raw=$(docker ps --filter "label=com.docker.compose.project=$compose_project" --format '{{.Names}}'); then
|
|
43
|
+
jq -nc \
|
|
44
|
+
--arg m "docker ps did not answer; runtime state is unknown" \
|
|
45
|
+
'{ok:false,error:{code:"DOCKER_PS_FAILED",message:$m,step:"docker ps"}}' >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
32
48
|
if [ -n "$raw" ]; then
|
|
33
49
|
containers_json=$(printf '%s\n' "$raw" | jq -R . | jq -sc .)
|
|
34
50
|
fi
|