@runuai/host 0.9.14 → 0.9.43
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 +33 -2
- 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 +3051 -200
- package/lib/preview-sidecar.ts +68 -14
- 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 +956 -0
- package/lib/task-environment/index.ts +364 -0
- package/lib/task-environment/legacy-adoption.ts +459 -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 +1405 -107
- 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 +766 -42
- 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
package/lib/preview-sidecar.ts
CHANGED
|
@@ -9,18 +9,30 @@
|
|
|
9
9
|
* (always local — no registry pull) under `node` with a ~5-line `net` TCP proxy;
|
|
10
10
|
* node streams give backpressure + handle WebSockets (Metro/HMR).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The cache deduplicates creation, but every tunnel open re-proves the exact
|
|
13
|
+
* sidecar container and its current Docker mapping. A process-lifetime cached
|
|
14
|
+
* localhost port is unsafe across an unnoticed daemon restart: another process
|
|
15
|
+
* can inherit that port before the bridge reconnects and triggers recovery.
|
|
16
16
|
*/
|
|
17
17
|
import net from "node:net";
|
|
18
18
|
|
|
19
19
|
import { dockerCli } from "./docker-exec";
|
|
20
|
+
import { inspectTunnelContainer } from "./tunnel-runtime";
|
|
20
21
|
|
|
21
22
|
/** Port the proxy listens on inside the sidecar (published to a random host port). */
|
|
22
23
|
const INNER_PORT = 9000;
|
|
23
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Exported for every "which container is the task's APP" scan: sidecars can
|
|
26
|
+
* legitimately wear the task's compose project/service labels (teardown
|
|
27
|
+
* finds them by project), so any container carrying THIS label must be
|
|
28
|
+
* excluded before counting app containers. Live 2026-08-18: one browser
|
|
29
|
+
* sidecar with compose labels made legacy adoption count two app containers
|
|
30
|
+
* and fail the whole runtime selection closed — every task on the host read
|
|
31
|
+
* as "still starting".
|
|
32
|
+
*/
|
|
33
|
+
export const PREVIEW_SIDECAR_TASK_LABEL = "com.runuai.preview-sidecar.task";
|
|
34
|
+
const LABEL = PREVIEW_SIDECAR_TASK_LABEL;
|
|
35
|
+
const TARGET_LABEL = "com.runuai.preview-sidecar.target";
|
|
24
36
|
|
|
25
37
|
/** (taskId,name,containerPort) -> published 127.0.0.1 host port of a live sidecar. */
|
|
26
38
|
const cache = new Map<string, number>();
|
|
@@ -135,13 +147,18 @@ async function imageOf(appContainer: string): Promise<string | null> {
|
|
|
135
147
|
return r.status === 0 && r.stdout.trim() ? r.stdout.trim() : null;
|
|
136
148
|
}
|
|
137
149
|
|
|
138
|
-
/**
|
|
150
|
+
/** Live Docker proof -> the published 127.0.0.1 host port and labels, or null. */
|
|
151
|
+
async function publishedSidecar(
|
|
152
|
+
name: string,
|
|
153
|
+
): Promise<{ port: number; labels: Record<string, string> } | null> {
|
|
154
|
+
const proof = await inspectTunnelContainer(name, INNER_PORT);
|
|
155
|
+
return proof.kind === "running" && proof.published?.host === "127.0.0.1"
|
|
156
|
+
? { port: proof.published.port, labels: proof.labels }
|
|
157
|
+
: null;
|
|
158
|
+
}
|
|
159
|
+
|
|
139
160
|
async function publishedPort(name: string): Promise<number | null> {
|
|
140
|
-
|
|
141
|
-
timeoutMs: 5_000,
|
|
142
|
-
});
|
|
143
|
-
const m = r.stdout.match(/127\.0\.0\.1:(\d+)/);
|
|
144
|
-
return r.status === 0 && m ? Number(m[1]) : null;
|
|
161
|
+
return (await publishedSidecar(name))?.port ?? null;
|
|
145
162
|
}
|
|
146
163
|
|
|
147
164
|
async function createSidecar(args: {
|
|
@@ -152,6 +169,7 @@ async function createSidecar(args: {
|
|
|
152
169
|
}): Promise<number | null> {
|
|
153
170
|
const name = sidecarName(args.taskId, args.name);
|
|
154
171
|
const appContainer = `${args.composeProject}-app-1`;
|
|
172
|
+
const target = `${appContainer}:${args.containerPort}`;
|
|
155
173
|
|
|
156
174
|
// Reuse a still-running sidecar from a previous host process (--rm keeps it up
|
|
157
175
|
// across a host restart; the in-memory cache was cleared). `docker port` is an
|
|
@@ -163,9 +181,20 @@ async function createSidecar(args: {
|
|
|
163
181
|
// sidecar serves `socket hang up` forever: the recovery path invalidates the
|
|
164
182
|
// CACHE on a failed connect, then lands right back here and returns the same
|
|
165
183
|
// corpse. Nothing could break the cycle short of removing it by hand.
|
|
166
|
-
const existing = await
|
|
184
|
+
const existing = await publishedSidecar(name);
|
|
167
185
|
if (existing !== null) {
|
|
168
|
-
|
|
186
|
+
// The Docker name is stable per (task, preview name), while a task-scoped
|
|
187
|
+
// preview may later point that name at another container port. The
|
|
188
|
+
// process-local cache key carries the port, but it vanishes on host
|
|
189
|
+
// restart; without a durable target label a healthy old proxy would be
|
|
190
|
+
// reused and silently route the authenticated request to the wrong port.
|
|
191
|
+
// Legacy unlabeled sidecars rebuild once.
|
|
192
|
+
if (
|
|
193
|
+
existing.labels[TARGET_LABEL] === target &&
|
|
194
|
+
(await probeSidecar(existing.port))
|
|
195
|
+
) {
|
|
196
|
+
return existing.port;
|
|
197
|
+
}
|
|
169
198
|
await dockerCli(["rm", "-f", name], { timeoutMs: 10_000 });
|
|
170
199
|
}
|
|
171
200
|
|
|
@@ -179,6 +208,7 @@ async function createSidecar(args: {
|
|
|
179
208
|
"--name", name,
|
|
180
209
|
"--network", `${args.composeProject}_default`,
|
|
181
210
|
"--label", `${LABEL}=${args.taskId}`,
|
|
211
|
+
"--label", `${TARGET_LABEL}=${target}`,
|
|
182
212
|
"-p", `127.0.0.1::${INNER_PORT}`,
|
|
183
213
|
"--entrypoint", "node",
|
|
184
214
|
image,
|
|
@@ -224,7 +254,31 @@ export async function ensurePreviewSidecar(args: {
|
|
|
224
254
|
}): Promise<number | null> {
|
|
225
255
|
const key = cacheKey(args.taskId, args.name, args.containerPort);
|
|
226
256
|
const cached = cache.get(key);
|
|
227
|
-
if (cached !== undefined)
|
|
257
|
+
if (cached !== undefined) {
|
|
258
|
+
const current = await publishedSidecar(sidecarName(args.taskId, args.name));
|
|
259
|
+
const expectedTarget =
|
|
260
|
+
`${args.composeProject}-app-1:${args.containerPort}`;
|
|
261
|
+
const app =
|
|
262
|
+
current?.port === cached &&
|
|
263
|
+
current.labels[TARGET_LABEL] === expectedTarget
|
|
264
|
+
? await inspectTunnelContainer(
|
|
265
|
+
`${args.composeProject}-app-1`,
|
|
266
|
+
args.containerPort,
|
|
267
|
+
)
|
|
268
|
+
: null;
|
|
269
|
+
// The sidecar's PID is the proxy itself, and it resolves the app by stable
|
|
270
|
+
// Compose DNS name for each connection. Proving both exact containers are
|
|
271
|
+
// running avoids the old 150ms socket-settle penalty on every browser asset
|
|
272
|
+
// while still rejecting a localhost port inherited after daemon restart.
|
|
273
|
+
if (
|
|
274
|
+
current?.port === cached &&
|
|
275
|
+
current.labels[TARGET_LABEL] === expectedTarget &&
|
|
276
|
+
app?.kind === "running"
|
|
277
|
+
) {
|
|
278
|
+
return cached;
|
|
279
|
+
}
|
|
280
|
+
cache.delete(key);
|
|
281
|
+
}
|
|
228
282
|
|
|
229
283
|
let pending = inflight.get(key);
|
|
230
284
|
if (!pending) {
|