@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
package/lib/db.ts
CHANGED
package/lib/docker-exec.ts
CHANGED
|
@@ -23,34 +23,84 @@ export interface DockerResult {
|
|
|
23
23
|
status: number | null;
|
|
24
24
|
stdout: string;
|
|
25
25
|
stderr: string;
|
|
26
|
+
/** True when tailOutputBytes discarded earlier diagnostic output. */
|
|
27
|
+
outputTruncated?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export function dockerCli(
|
|
29
31
|
args: string[],
|
|
30
|
-
opts: {
|
|
32
|
+
opts: {
|
|
33
|
+
input?: string;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
/** Optional aggregate stdout+stderr cap for cardinality-sensitive probes.
|
|
36
|
+
* Exceeding it kills the child and resolves with status:null. */
|
|
37
|
+
maxOutputBytes?: number;
|
|
38
|
+
/** Keep draining the child but retain only this many trailing bytes from
|
|
39
|
+
* each output stream. Use for legitimately verbose bounded operations
|
|
40
|
+
* whose complete compiler/install log must not accumulate in host RAM. */
|
|
41
|
+
tailOutputBytes?: number;
|
|
42
|
+
/** Selection probes can intentionally ignore this process's pinned host. */
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
} = {},
|
|
31
45
|
): Promise<DockerResult> {
|
|
32
46
|
return new Promise<DockerResult>((resolve) => {
|
|
33
47
|
let stdout = "";
|
|
34
48
|
let stderr = "";
|
|
49
|
+
let stdoutTruncated = false;
|
|
50
|
+
let stderrTruncated = false;
|
|
35
51
|
let settled = false;
|
|
36
|
-
const child = spawn("docker", args, {
|
|
52
|
+
const child = spawn("docker", args, {
|
|
53
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
54
|
+
env: opts.env,
|
|
55
|
+
});
|
|
37
56
|
const timer = setTimeout(() => {
|
|
38
57
|
child.kill("SIGKILL");
|
|
58
|
+
// A SIGKILLed client normally closes at once, but an unreapable child
|
|
59
|
+
// (D-state, wedged pipe) must not hold this promise open forever: the
|
|
60
|
+
// caller's own deadline already expired, so settle as failed after a
|
|
61
|
+
// short grace even if `close` never fires.
|
|
62
|
+
const settle = setTimeout(() => {
|
|
63
|
+
stderr += `docker did not settle after its ${opts.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms deadline`;
|
|
64
|
+
finish(null);
|
|
65
|
+
}, 5_000);
|
|
66
|
+
settle.unref?.();
|
|
39
67
|
}, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
40
68
|
timer.unref?.();
|
|
41
69
|
const finish = (status: number | null) => {
|
|
42
70
|
if (settled) return;
|
|
43
71
|
settled = true;
|
|
44
72
|
clearTimeout(timer);
|
|
45
|
-
resolve({
|
|
73
|
+
resolve({
|
|
74
|
+
status,
|
|
75
|
+
stdout,
|
|
76
|
+
stderr,
|
|
77
|
+
outputTruncated: stdoutTruncated || stderrTruncated || undefined,
|
|
78
|
+
});
|
|
46
79
|
};
|
|
47
80
|
child.stdout?.setEncoding("utf8");
|
|
48
81
|
child.stderr?.setEncoding("utf8");
|
|
49
82
|
child.stdout?.on("data", (c: string) => {
|
|
50
|
-
|
|
83
|
+
if (settled) return;
|
|
84
|
+
if (outputWouldExceedLimit(stdout, stderr, c, opts.maxOutputBytes)) {
|
|
85
|
+
stderr += "docker output exceeded configured limit";
|
|
86
|
+
child.kill("SIGKILL");
|
|
87
|
+
finish(null);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const tailed = appendOutputTail(stdout, c, opts.tailOutputBytes);
|
|
91
|
+
stdout = tailed.output;
|
|
92
|
+
stdoutTruncated ||= tailed.truncated;
|
|
51
93
|
});
|
|
52
94
|
child.stderr?.on("data", (c: string) => {
|
|
53
|
-
|
|
95
|
+
if (settled) return;
|
|
96
|
+
if (outputWouldExceedLimit(stdout, stderr, c, opts.maxOutputBytes)) {
|
|
97
|
+
child.kill("SIGKILL");
|
|
98
|
+
finish(null);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const tailed = appendOutputTail(stderr, c, opts.tailOutputBytes);
|
|
102
|
+
stderr = tailed.output;
|
|
103
|
+
stderrTruncated ||= tailed.truncated;
|
|
54
104
|
});
|
|
55
105
|
child.on("error", (err: Error) => {
|
|
56
106
|
stderr += err.message;
|
|
@@ -70,3 +120,35 @@ export function dockerCli(
|
|
|
70
120
|
child.stdin?.end();
|
|
71
121
|
});
|
|
72
122
|
}
|
|
123
|
+
|
|
124
|
+
function appendOutputTail(
|
|
125
|
+
current: string,
|
|
126
|
+
chunk: string,
|
|
127
|
+
limit: number | undefined,
|
|
128
|
+
): { output: string; truncated: boolean } {
|
|
129
|
+
if (limit === undefined) return { output: current + chunk, truncated: false };
|
|
130
|
+
const bounded = Math.max(0, Math.floor(limit));
|
|
131
|
+
const combined = Buffer.from(current + chunk);
|
|
132
|
+
if (combined.byteLength <= bounded) {
|
|
133
|
+
return { output: combined.toString("utf8"), truncated: false };
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
output: combined.subarray(combined.byteLength - bounded).toString("utf8"),
|
|
137
|
+
truncated: true,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function outputWouldExceedLimit(
|
|
142
|
+
stdout: string,
|
|
143
|
+
stderr: string,
|
|
144
|
+
chunk: string,
|
|
145
|
+
limit: number | undefined,
|
|
146
|
+
): boolean {
|
|
147
|
+
return (
|
|
148
|
+
limit !== undefined &&
|
|
149
|
+
Buffer.byteLength(stdout) +
|
|
150
|
+
Buffer.byteLength(stderr) +
|
|
151
|
+
Buffer.byteLength(chunk) >
|
|
152
|
+
limit
|
|
153
|
+
);
|
|
154
|
+
}
|
package/lib/engine-accounts.ts
CHANGED
|
@@ -29,7 +29,12 @@ import { join } from "node:path";
|
|
|
29
29
|
import { eq } from "drizzle-orm";
|
|
30
30
|
|
|
31
31
|
import { getDb, schema } from "./db";
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
taskContainerBackend,
|
|
34
|
+
taskContainerCli,
|
|
35
|
+
} from "./task-container-cli";
|
|
36
|
+
import { RUNTIME_AUTHORITY_ENV, runtimeAuthorityDockerExecArgs } from "./runtime-authority";
|
|
37
|
+
import type { TaskEnvironmentHandle } from "./task-environment/types";
|
|
33
38
|
import { openAesGcm, sealAesGcm } from "./secrets";
|
|
34
39
|
import { newId } from "./ulid";
|
|
35
40
|
|
|
@@ -354,9 +359,46 @@ async function copyDirInto(
|
|
|
354
359
|
hostDir: string,
|
|
355
360
|
containerDir: string,
|
|
356
361
|
container: string,
|
|
362
|
+
environment?: Pick<TaskEnvironmentHandle, "exec" | "copy">,
|
|
357
363
|
): Promise<void> {
|
|
364
|
+
if (environment) {
|
|
365
|
+
const run = async (argv: [string, ...string[]], step: string) => {
|
|
366
|
+
const result = await environment.exec({
|
|
367
|
+
argv,
|
|
368
|
+
user: "root",
|
|
369
|
+
env: RUNTIME_AUTHORITY_ENV,
|
|
370
|
+
timeoutMs: EXEC_TIMEOUT_MS,
|
|
371
|
+
maxOutputBytes: 64 * 1024,
|
|
372
|
+
});
|
|
373
|
+
if (result.exitCode !== 0) {
|
|
374
|
+
const detail = Buffer.from(result.stderr).toString("utf8").trim().slice(0, 200);
|
|
375
|
+
throw new Error(
|
|
376
|
+
`${step} failed (${result.exitCode === null ? "environment timeout" : `exit ${result.exitCode}`})` +
|
|
377
|
+
(detail ? `: ${detail}` : ""),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
await run(["/bin/mkdir", "-p", containerDir], `mkdir ${containerDir}`);
|
|
382
|
+
await environment.copy({
|
|
383
|
+
direction: "into",
|
|
384
|
+
source: hostDir,
|
|
385
|
+
destination: containerDir,
|
|
386
|
+
});
|
|
387
|
+
await run(
|
|
388
|
+
["/bin/chown", "-R", "node:node", containerDir],
|
|
389
|
+
`chown -R node:node ${containerDir}`,
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
// Backend-aware fallback (2026-08-18 sweep + review round 4): the apple
|
|
394
|
+
// path normally arrives with an environment handle above; when it does
|
|
395
|
+
// not, the bundled CLI speaks these exec/cp verbs — but the CALLER's name
|
|
396
|
+
// is the Docker replica shape, which does not exist on apple.
|
|
397
|
+
const target = taskContainerBackend().apple
|
|
398
|
+
? container.replace(/-app-1$/, "-app")
|
|
399
|
+
: container;
|
|
358
400
|
const must = async (args: string[], step: string): Promise<void> => {
|
|
359
|
-
const res = await
|
|
401
|
+
const res = await taskContainerCli(args, { timeoutMs: EXEC_TIMEOUT_MS });
|
|
360
402
|
if (res.status !== 0) {
|
|
361
403
|
const detail = res.stderr.trim().slice(0, 200);
|
|
362
404
|
throw new Error(
|
|
@@ -366,13 +408,32 @@ async function copyDirInto(
|
|
|
366
408
|
}
|
|
367
409
|
};
|
|
368
410
|
await must(
|
|
369
|
-
[
|
|
411
|
+
[
|
|
412
|
+
"exec",
|
|
413
|
+
"-u",
|
|
414
|
+
"root",
|
|
415
|
+
...runtimeAuthorityDockerExecArgs(),
|
|
416
|
+
target,
|
|
417
|
+
"/bin/mkdir",
|
|
418
|
+
"-p",
|
|
419
|
+
containerDir,
|
|
420
|
+
],
|
|
370
421
|
`mkdir ${containerDir}`,
|
|
371
422
|
);
|
|
372
423
|
// Copy the dir CONTENTS (trailing /.) into the target dir.
|
|
373
|
-
await must(["cp", `${hostDir}/.`, `${
|
|
424
|
+
await must(["cp", `${hostDir}/.`, `${target}:${containerDir}`], "docker cp account dir");
|
|
374
425
|
await must(
|
|
375
|
-
[
|
|
426
|
+
[
|
|
427
|
+
"exec",
|
|
428
|
+
"-u",
|
|
429
|
+
"root",
|
|
430
|
+
...runtimeAuthorityDockerExecArgs(),
|
|
431
|
+
target,
|
|
432
|
+
"/bin/chown",
|
|
433
|
+
"-R",
|
|
434
|
+
"node:node",
|
|
435
|
+
containerDir,
|
|
436
|
+
],
|
|
376
437
|
`chown -R node:node ${containerDir}`,
|
|
377
438
|
);
|
|
378
439
|
}
|
|
@@ -393,6 +454,7 @@ export async function provisionEngineAccounts(
|
|
|
393
454
|
container: string,
|
|
394
455
|
kinds: Iterable<string>,
|
|
395
456
|
seams: Partial<AccountSeams> = {},
|
|
457
|
+
environment?: Pick<TaskEnvironmentHandle, "exec" | "copy">,
|
|
396
458
|
): Promise<ProvisionEngineAccountsResult> {
|
|
397
459
|
const s = withDefaults(seams);
|
|
398
460
|
const seen = new Set<string>();
|
|
@@ -413,6 +475,7 @@ export async function provisionEngineAccounts(
|
|
|
413
475
|
account.configDir.hostDir,
|
|
414
476
|
account.configDir.containerDir,
|
|
415
477
|
container,
|
|
478
|
+
environment,
|
|
416
479
|
);
|
|
417
480
|
copied += 1;
|
|
418
481
|
} catch (err) {
|