@runuai/host 0.9.121 → 0.9.123
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/lib/container-runtime.ts +6 -0
- package/lib/machine-provider-aws.ts +5 -0
- package/lib/sidecar.ts +40 -0
- package/package.json +1 -1
- package/src/cli.ts +9 -0
- package/src/index.ts +4 -0
- package/src/main.ts +13 -7
package/lib/container-runtime.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
runtimeProviderStatePath,
|
|
25
25
|
type RuntimeProviderStateStore,
|
|
26
26
|
} from "./runtime-provider-state";
|
|
27
|
+
import { sidecarMode } from "./sidecar";
|
|
27
28
|
|
|
28
29
|
export type RuntimePreference = "auto" | "docker" | "apple-container";
|
|
29
30
|
|
|
@@ -1347,6 +1348,11 @@ export function resetContainerRuntimeSelectionForTests(): void {
|
|
|
1347
1348
|
|
|
1348
1349
|
/** Human-facing task failure, or null while the host can attempt Docker work. */
|
|
1349
1350
|
export function containerRuntimeProblem(): string | null {
|
|
1351
|
+
// ADR-133 sidecar mode: the task runs on this machine, not in a container;
|
|
1352
|
+
// there is no runtime to be ready. Live 2026-09-10: the first hostless
|
|
1353
|
+
// task-up reached its machine and was refused with the Docker-context
|
|
1354
|
+
// message below, because this gate never knew the machine had no Docker.
|
|
1355
|
+
if (sidecarMode()) return null;
|
|
1350
1356
|
if (state.status === "no-runtime") return state.message;
|
|
1351
1357
|
if (state.status !== "ready" || !operational) {
|
|
1352
1358
|
return (
|
|
@@ -38,6 +38,11 @@ export const AWS_MACHINE_TAG = "com.uai.machine";
|
|
|
38
38
|
* UAI_MACHINE_IMAGE resolves the newest published payload in its region. */
|
|
39
39
|
export const PAYLOAD_AMI_OWNER = "277057415208";
|
|
40
40
|
export const PAYLOAD_AMI_NAME_PREFIX = "uai-machine-payload";
|
|
41
|
+
/** Regions the payload AMI is published to (build-ami.sh bakes in the first
|
|
42
|
+
* and copies to the rest). A BYO-AWS runner elsewhere would find no image
|
|
43
|
+
* and fail every launch, so setup refuses other regions up front (owner,
|
|
44
|
+
* 2026-09-10: "only show the available ones for now"). */
|
|
45
|
+
export const PAYLOAD_AMI_REGIONS: readonly string[] = ["us-west-2", "us-east-1"];
|
|
41
46
|
/** Name of the security group auto-created in the default VPC when the host
|
|
42
47
|
* config names none. Inbound: all TCP from the HOST's egress /32 only —
|
|
43
48
|
* code-server runs auth-none and preview ports are plain HTTP, so exposure
|
package/lib/sidecar.ts
CHANGED
|
@@ -16,3 +16,43 @@ export function sidecarTaskId(): string | null {
|
|
|
16
16
|
export function sidecarMode(): boolean {
|
|
17
17
|
return sidecarTaskId() !== null;
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A sidecar is ready to run its task only once it holds a lease AND the
|
|
22
|
+
* credential store has been reconciled under it — before that there is no
|
|
23
|
+
* engine account, GitHub token, or project env on the machine, and a task-up
|
|
24
|
+
* that raced the first grant would clone without credentials and spawn
|
|
25
|
+
* agents that cannot sign in (the dispatch arrives the moment the bridge
|
|
26
|
+
* connects; the lease answer is one round trip behind it). Resolved by
|
|
27
|
+
* main.ts after the first grant's reconciles settle; never on an owned host.
|
|
28
|
+
*/
|
|
29
|
+
let markReady: (() => void) | null = null;
|
|
30
|
+
let ready = false;
|
|
31
|
+
export const sidecarReady: Promise<void> = new Promise<void>((resolve) => {
|
|
32
|
+
markReady = resolve;
|
|
33
|
+
});
|
|
34
|
+
export function markSidecarReady(): void {
|
|
35
|
+
if (ready) return;
|
|
36
|
+
ready = true;
|
|
37
|
+
markReady?.();
|
|
38
|
+
markReady = null;
|
|
39
|
+
}
|
|
40
|
+
export function isSidecarReady(): boolean {
|
|
41
|
+
return ready;
|
|
42
|
+
}
|
|
43
|
+
/** How long a task-up waits for the lease before giving up. */
|
|
44
|
+
export const SIDECAR_READY_TIMEOUT_MS = 3 * 60_000;
|
|
45
|
+
export function awaitSidecarReady(timeoutMs = SIDECAR_READY_TIMEOUT_MS): Promise<void> {
|
|
46
|
+
if (!sidecarMode() || ready) return Promise.resolve();
|
|
47
|
+
return new Promise<void>((resolve, reject) => {
|
|
48
|
+
const timer = setTimeout(
|
|
49
|
+
() => reject(new Error("the task's machine has no credentials yet (no lease granted)")),
|
|
50
|
+
timeoutMs,
|
|
51
|
+
);
|
|
52
|
+
timer.unref?.();
|
|
53
|
+
void sidecarReady.then(() => {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
resolve();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
TasksResponse,
|
|
110
110
|
} from "./ui/types";
|
|
111
111
|
import type { InstallContext, Installer } from "../scripts/install/types";
|
|
112
|
+
import { PAYLOAD_AMI_REGIONS } from "../lib/machine-provider-aws";
|
|
112
113
|
|
|
113
114
|
// --- tiny ANSI (no chalk) ---------------------------------------------------
|
|
114
115
|
|
|
@@ -636,6 +637,14 @@ async function cmdAwsSetup(rest: string[]): Promise<void> {
|
|
|
636
637
|
process.exitCode = 1;
|
|
637
638
|
return;
|
|
638
639
|
}
|
|
640
|
+
if (!PAYLOAD_AMI_REGIONS.includes(region)) {
|
|
641
|
+
console.error(
|
|
642
|
+
red(`task machines are not available in ${region} yet — Uai's machine image is published in ${PAYLOAD_AMI_REGIONS.join(", ")}`),
|
|
643
|
+
);
|
|
644
|
+
console.error(dim("pass --region with one of those; more regions are coming."));
|
|
645
|
+
process.exitCode = 1;
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
639
648
|
|
|
640
649
|
const identity = awsAdmin(["sts", "get-caller-identity"], region);
|
|
641
650
|
if (identity.status !== 0) {
|
package/src/index.ts
CHANGED
|
@@ -185,6 +185,7 @@ export type {
|
|
|
185
185
|
PreviewPortRuntime,
|
|
186
186
|
} from "../lib/preview-ports";
|
|
187
187
|
import { isEnvironmentTransportProvider } from "../lib/task-environment/types";
|
|
188
|
+
import { awaitSidecarReady } from "../lib/sidecar";
|
|
188
189
|
|
|
189
190
|
export const hostEvents: HostEventStream = {
|
|
190
191
|
subscribe(handler) {
|
|
@@ -824,6 +825,9 @@ export const hostCommands: HostCommands = {
|
|
|
824
825
|
"GitHub is connected on this host, but Uai could not prepare its credential for Git. Retry the task; if it persists, reconnect GitHub on this host.",
|
|
825
826
|
);
|
|
826
827
|
}
|
|
828
|
+
// ADR-133 sidecar: nothing to clone or spawn with until the lease
|
|
829
|
+
// has landed and the store is reconciled under it.
|
|
830
|
+
await awaitSidecarReady();
|
|
827
831
|
// Launch screen: the first stage boundary. A machine-backed task
|
|
828
832
|
// spends the next minute launching an EC2 instance; a container
|
|
829
833
|
// task goes straight to preparing its workspace.
|
package/src/main.ts
CHANGED
|
@@ -250,7 +250,7 @@ import {
|
|
|
250
250
|
} from "./protocol";
|
|
251
251
|
import { isEnvironmentTransportProvider } from "../lib/task-environment/types";
|
|
252
252
|
import { LeaseManager } from "../lib/lease";
|
|
253
|
-
import { sidecarMode, sidecarTaskId } from "../lib/sidecar";
|
|
253
|
+
import { markSidecarReady, sidecarMode, sidecarTaskId } from "../lib/sidecar";
|
|
254
254
|
import { declareAgentClisReady } from "../lib/standard-image";
|
|
255
255
|
|
|
256
256
|
setContainerRuntimeOperationalCheck(() => containerRuntimeProblem() === null);
|
|
@@ -464,8 +464,8 @@ configureEngineAccountStore({ hostId });
|
|
|
464
464
|
/** The credential-store reconciles. At boot on a host with its own AWS
|
|
465
465
|
* identity; after the first lease on a sidecar (ADR-133), which has no
|
|
466
466
|
* credentials before that. */
|
|
467
|
-
function reconcileStores(): void {
|
|
468
|
-
|
|
467
|
+
function reconcileStores(): Promise<void> {
|
|
468
|
+
const engineAccounts = reconcileEngineAccountsFromStore({ hostId })
|
|
469
469
|
.then((r) => {
|
|
470
470
|
if (r.skipped) return;
|
|
471
471
|
console.log(
|
|
@@ -477,7 +477,7 @@ function reconcileStores(): void {
|
|
|
477
477
|
`[engine-accounts] credential store reconcile failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
478
478
|
);
|
|
479
479
|
});
|
|
480
|
-
|
|
480
|
+
const userCredentials = reconcileUserCredentialsFromStore()
|
|
481
481
|
.then((r) => {
|
|
482
482
|
if (r.skipped) return;
|
|
483
483
|
console.log(
|
|
@@ -489,7 +489,7 @@ function reconcileStores(): void {
|
|
|
489
489
|
`[user-credentials] credential store reconcile failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
490
490
|
);
|
|
491
491
|
});
|
|
492
|
-
|
|
492
|
+
const projectEnv = reconcileProjectEnvFromStore()
|
|
493
493
|
.then((r) => {
|
|
494
494
|
if (r.skipped) return;
|
|
495
495
|
console.log(
|
|
@@ -501,6 +501,7 @@ function reconcileStores(): void {
|
|
|
501
501
|
`[project-env] credential store reconcile failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
502
502
|
);
|
|
503
503
|
});
|
|
504
|
+
return Promise.all([engineAccounts, userCredentials, projectEnv]).then(() => undefined);
|
|
504
505
|
}
|
|
505
506
|
// Image + agent-CLI maintenance runs where the compute is owned: an owned
|
|
506
507
|
// host keeps its own image current on an hourly schedule (a task-up reuses
|
|
@@ -527,7 +528,7 @@ configureTokenBroker({ repush: repushBrokeredCopies });
|
|
|
527
528
|
if (!sidecarMode()) {
|
|
528
529
|
startTokenBrokerLoop();
|
|
529
530
|
startCredentialStorePickup({ repush: repushBrokeredCopies });
|
|
530
|
-
reconcileStores();
|
|
531
|
+
void reconcileStores();
|
|
531
532
|
}
|
|
532
533
|
/**
|
|
533
534
|
* ADR-133 sidecar: the task-scoped AWS lease. Requested when the bridge is
|
|
@@ -545,7 +546,12 @@ const leaseManager: LeaseManager | null = sidecarMode()
|
|
|
545
546
|
},
|
|
546
547
|
onFirstGrant: () => {
|
|
547
548
|
console.log("[lease] granted — reconciling the credential store");
|
|
548
|
-
reconcileStores()
|
|
549
|
+
void reconcileStores().finally(() => {
|
|
550
|
+
// Task-up waits for this: only now are engine accounts, GitHub
|
|
551
|
+
// tokens, and project env on the machine (lib/sidecar.ts).
|
|
552
|
+
markSidecarReady();
|
|
553
|
+
console.log("[lease] credential store reconciled — the task may start");
|
|
554
|
+
});
|
|
549
555
|
startCredentialStorePickup({ repush: repushBrokeredCopies });
|
|
550
556
|
},
|
|
551
557
|
onGrant: (lease) => {
|