@claxedo/sandbox-manager 0.9.0 → 0.10.0
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 +233 -26
- package/dist/checkpoint-manager.d.ts +29 -4
- package/dist/checkpoint-manager.mjs +133 -24
- package/dist/command.d.ts +9 -2
- package/dist/command.mjs +8 -8
- package/dist/contract.d.ts +687 -0
- package/dist/driver-billing.d.ts +29 -0
- package/dist/driver-catalog.d.ts +42 -7
- package/dist/driver-catalog.mjs +115 -161
- package/dist/drivers/boat-client.d.ts +95 -0
- package/dist/drivers/boat-named-snapshots.d.ts +31 -0
- package/dist/drivers/boat-template.d.ts +49 -0
- package/dist/drivers/boat-template.mjs +1196 -0
- package/dist/drivers/boat.d.ts +64 -0
- package/dist/drivers/boat.mjs +990 -0
- package/dist/drivers/cloudflare.d.ts +3 -3
- package/dist/drivers/cloudflare.mjs +208 -147
- package/dist/drivers/docker.d.ts +4 -3
- package/dist/drivers/docker.mjs +154 -126
- package/dist/drivers/local-brokering.d.ts +14 -0
- package/dist/drivers/local-brokering.mjs +1029 -0
- package/dist/drivers/modal.d.ts +2 -2
- package/dist/drivers/modal.mjs +121 -111
- package/dist/drivers/transient-error.d.ts +2 -0
- package/dist/drivers/vercel.d.ts +3 -2
- package/dist/drivers/vercel.mjs +151 -115
- package/dist/egress-policy.d.ts +134 -0
- package/dist/egress-warnings.d.ts +22 -0
- package/dist/garbage-collect.d.ts +14 -0
- package/dist/hosted-network-policy.d.ts +9 -2
- package/dist/hosted-network-policy.mjs +218 -1
- package/dist/image-name.d.ts +12 -2
- package/dist/image-name.mjs +14 -5
- package/dist/image.d.ts +1 -20
- package/dist/image.mjs +11 -88
- package/dist/index.d.ts +8 -578
- package/dist/index.mjs +1379 -320
- package/dist/lease-failure.d.ts +173 -0
- package/dist/lease-lifecycle.d.ts +65 -0
- package/dist/lease-retry-wait.d.ts +8 -0
- package/dist/lease-start.d.ts +21 -0
- package/dist/lease-types.d.ts +4 -3
- package/dist/manager.d.ts +2 -0
- package/dist/runtime-env.d.ts +4 -2
- package/dist/runtime-env.mjs +37 -2
- package/dist/runtime-snapshot.d.ts +49 -0
- package/dist/runtime-version.d.ts +9 -0
- package/dist/runtime-version.mjs +2 -0
- package/dist/service-url-exposure.d.ts +0 -1
- package/dist/service-url-exposure.mjs +0 -9
- package/dist/start-fresh.d.ts +17 -0
- package/dist/start-telemetry.d.ts +37 -0
- package/dist/stores/memory.d.ts +1 -1
- package/dist/stores/memory.mjs +63 -52
- package/package.json +26 -42
- package/dist/daytona-allow-list.d.ts +0 -16
- package/dist/daytona-allow-list.mjs +0 -11
- package/dist/drivers/box.d.ts +0 -52
- package/dist/drivers/box.mjs +0 -574
- package/dist/drivers/daytona.d.ts +0 -163
- package/dist/drivers/daytona.mjs +0 -683
- package/dist/drivers/exe.d.ts +0 -26
- package/dist/drivers/exe.mjs +0 -492
- package/dist/drivers/fetch-bridge.d.ts +0 -10
- package/dist/drivers/fetch-bridge.mjs +0 -126
- package/dist/lease-policy.d.ts +0 -52
- package/dist/lease-policy.mjs +0 -290
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { isSandboxCheckpointUnusableCause, sandboxCheckpointUnusableCauses, type SandboxCheckpointUnusableCause } from "@claxedo/sandbox-contract";
|
|
2
|
+
import type { SandboxLease } from "./contract";
|
|
3
|
+
/**
|
|
4
|
+
* A driver's ensure throws this shape when the runtime process exited before it
|
|
5
|
+
* was ever ready. The cause is in the boot itself (a repository credential that
|
|
6
|
+
* is gone, a branch that does not exist), so polling the driver again cannot
|
|
7
|
+
* heal it. Structural for the reason `SandboxListingUnsupported` is.
|
|
8
|
+
*/
|
|
9
|
+
export type SandboxRuntimeBootFailure = {
|
|
10
|
+
runtimeBootFailed: true;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function isSandboxRuntimeBootFailure(err: unknown): err is SandboxRuntimeBootFailure;
|
|
14
|
+
export declare class SandboxRuntimeBootError extends Error implements SandboxRuntimeBootFailure {
|
|
15
|
+
readonly runtimeBootFailed: true;
|
|
16
|
+
constructor(reason: string);
|
|
17
|
+
}
|
|
18
|
+
export { isSandboxCheckpointUnusableCause, sandboxCheckpointUnusableCauses, type SandboxCheckpointUnusableCause };
|
|
19
|
+
/**
|
|
20
|
+
* A driver throws this shape when the checkpoint a start restores cannot be
|
|
21
|
+
* used. Every start from it is refused the same way, and the image is never
|
|
22
|
+
* booted in its place: only the person's Start fresh, which drops the
|
|
23
|
+
* checkpoint, lets the next start boot the image.
|
|
24
|
+
*/
|
|
25
|
+
export type SandboxCheckpointUnusable = {
|
|
26
|
+
checkpointUnusable: SandboxCheckpointUnusableCause;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function isSandboxCheckpointUnusable(err: unknown): err is SandboxCheckpointUnusable;
|
|
30
|
+
export declare class SandboxCheckpointUnusableError extends Error implements SandboxCheckpointUnusable {
|
|
31
|
+
readonly checkpointUnusable: SandboxCheckpointUnusableCause;
|
|
32
|
+
constructor(checkpointUnusable: SandboxCheckpointUnusableCause, reason: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A driver throws this shape when the provider says the host a lease names no
|
|
36
|
+
* longer exists: deleted outside the control plane, or its stopped disk
|
|
37
|
+
* expired. Nothing on that host can serve or be stopped again, so the lease
|
|
38
|
+
* ends and the next start places a new host.
|
|
39
|
+
*/
|
|
40
|
+
export type SandboxHostGone = {
|
|
41
|
+
hostGone: true;
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function isSandboxHostGone(err: unknown): err is SandboxHostGone;
|
|
45
|
+
export declare class SandboxHostGoneError extends Error implements SandboxHostGone {
|
|
46
|
+
readonly hostGone: true;
|
|
47
|
+
constructor(reason: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A driver throws this shape when its provider refused the key the driver was
|
|
51
|
+
* made with (an HTTP 401 or 403 to the key itself). Retrying cannot heal it;
|
|
52
|
+
* a new key can, which is why the person is told where keys are kept.
|
|
53
|
+
*/
|
|
54
|
+
export type SandboxProviderKeyRefused = {
|
|
55
|
+
providerKeyRefused: true;
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
export declare function isSandboxProviderKeyRefused(err: unknown): err is SandboxProviderKeyRefused;
|
|
59
|
+
export declare class SandboxProviderKeyRefusedError extends Error implements SandboxProviderKeyRefused {
|
|
60
|
+
readonly providerKeyRefused: true;
|
|
61
|
+
constructor(reason: string);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A driver throws this shape when Claxedo, not the provider, refuses the
|
|
65
|
+
* start, and `message` is already the one line a person reads: the lease
|
|
66
|
+
* keeps it as it is, where any other driver error is the provider's.
|
|
67
|
+
*/
|
|
68
|
+
export type SandboxStartRefusal = {
|
|
69
|
+
startRefused: true;
|
|
70
|
+
message: string;
|
|
71
|
+
};
|
|
72
|
+
export declare function isSandboxStartRefusal(err: unknown): err is SandboxStartRefusal;
|
|
73
|
+
export declare class SandboxStartRefusedError extends Error implements SandboxStartRefusal {
|
|
74
|
+
readonly startRefused: true;
|
|
75
|
+
constructor(reason: string);
|
|
76
|
+
}
|
|
77
|
+
/** The error a lease keeps when `driver`'s provider refused its key; the driver's message stays for the operator. */
|
|
78
|
+
export declare function sandboxProviderKeyRefusedError(driver: string, message: string): string;
|
|
79
|
+
/** The error a lease keeps when `driver` failed at its provider; the driver's message, which names hosts, requests and provider codes, stays for the operator. */
|
|
80
|
+
export declare function sandboxProviderFailedError(driver: string, message: string): string;
|
|
81
|
+
export declare const SANDBOX_START_WINDOWS: readonly ["minute", "hour", "day"];
|
|
82
|
+
export type SandboxStartWindow = (typeof SANDBOX_START_WINDOWS)[number];
|
|
83
|
+
/** A provider's start limit an account used up: `limit` starts per `window`. */
|
|
84
|
+
export type SandboxStartLimit = {
|
|
85
|
+
window: SandboxStartWindow;
|
|
86
|
+
limit: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* A driver throws this shape when its provider refused a start because the
|
|
90
|
+
* account's start limit is used up. A minute's or an hour's limit is waited
|
|
91
|
+
* out before the next attempt; a day's is not retried on its own, since every
|
|
92
|
+
* attempt until it frees up is refused again, and a person trying again runs it.
|
|
93
|
+
*/
|
|
94
|
+
export type SandboxProviderRateLimited = {
|
|
95
|
+
providerRateLimited: SandboxStartLimit;
|
|
96
|
+
message: string;
|
|
97
|
+
};
|
|
98
|
+
export declare function isSandboxProviderRateLimited(err: unknown): err is SandboxProviderRateLimited;
|
|
99
|
+
export declare class SandboxProviderRateLimitedError extends Error implements SandboxProviderRateLimited {
|
|
100
|
+
readonly providerRateLimited: SandboxStartLimit;
|
|
101
|
+
constructor(providerRateLimited: SandboxStartLimit, reason: string);
|
|
102
|
+
}
|
|
103
|
+
/** How long a start waits out a used-up limit before it is tried again; a day's limit has no automatic retry. */
|
|
104
|
+
export declare function sandboxStartLimitRetryMs(window: SandboxStartWindow): number | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* The error a lease keeps when `driver`'s provider refused a start at its
|
|
107
|
+
* start limit. Claxedo waits out a minute's or an hour's limit itself, and
|
|
108
|
+
* each surface tells when that next try runs, so only a day's line names what
|
|
109
|
+
* the person does.
|
|
110
|
+
*/
|
|
111
|
+
export declare function sandboxProviderRateLimitedError(driver: string, limit: SandboxStartLimit): string;
|
|
112
|
+
/** The used-up window when a lease's error is a start the provider refused at its start limit. */
|
|
113
|
+
export declare function sandboxProviderRateLimit(error: string | undefined): SandboxStartWindow | undefined;
|
|
114
|
+
export declare function sandboxRuntimeBootFailedError(reason: string): string;
|
|
115
|
+
export declare function sandboxCheckpointUnusableError(cause: SandboxCheckpointUnusableCause, reason: string): string;
|
|
116
|
+
/** The cause and the driver's one line when an ensure error says the checkpoint a start restores cannot be used. */
|
|
117
|
+
export declare function sandboxCheckpointUnusable(error: string | undefined): {
|
|
118
|
+
cause: SandboxCheckpointUnusableCause;
|
|
119
|
+
message: string;
|
|
120
|
+
} | undefined;
|
|
121
|
+
export declare function sandboxHostGoneError(reason: string): string;
|
|
122
|
+
/** The error a lease stopped for a runtime that answered nothing for `forMs` keeps until its next start. */
|
|
123
|
+
export declare function sandboxRuntimeUnresponsiveError(forMs: number): string;
|
|
124
|
+
/** The one line a person is told for why a stopped lease stopped, when it was neither a person's stop nor an idle one. */
|
|
125
|
+
export declare function sandboxStopReason(error: string | undefined): string | undefined;
|
|
126
|
+
export declare function workspaceRuntimeBootReason(diagnostic: string): string | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* What a driver keeps of a runtime's log within `maxChars`. A Node stack
|
|
129
|
+
* under the boot-failure line is longer than the bound a driver puts on the
|
|
130
|
+
* tail (600 chars on Boat, 2026-10-15 staging), so a tail alone keeps the
|
|
131
|
+
* frames and loses the one line that names the reason.
|
|
132
|
+
*/
|
|
133
|
+
export declare function runtimeBootDiagnostic(log: string, maxChars: number): string;
|
|
134
|
+
/**
|
|
135
|
+
* The boot's own reason when an ensure error says a start cannot boot until
|
|
136
|
+
* what it boots from changes: a runtime whose boot failed, or a checkpoint
|
|
137
|
+
* that cannot be used.
|
|
138
|
+
*/
|
|
139
|
+
export declare function sandboxRuntimeBootFailure(error: string | undefined): string | undefined;
|
|
140
|
+
/** The error a lease keeps when the runtime it serves stopped answering its health reports. */
|
|
141
|
+
export declare const SANDBOX_RUNTIME_UNHEALTHY = "runtime_unhealthy";
|
|
142
|
+
/** The error a capped lease answers when it kept none of its own. */
|
|
143
|
+
export declare const SANDBOX_RETRY_CAP_EXCEEDED = "runtime_retry_cap_exceeded";
|
|
144
|
+
/**
|
|
145
|
+
* The one line a person is told for a start step's error: the runtime's own
|
|
146
|
+
* reason for a boot that failed, the provider's refusal in Claxedo's words,
|
|
147
|
+
* else the error's first line.
|
|
148
|
+
*/
|
|
149
|
+
export declare function sandboxStartFailureReason(error: string): string;
|
|
150
|
+
export declare function sandboxLeaseEndedUnderStart(error: string | undefined): boolean;
|
|
151
|
+
export declare function sandboxLeaseFailure(lease: SandboxLease, input: {
|
|
152
|
+
now: number;
|
|
153
|
+
maxRetryCount: number;
|
|
154
|
+
}): SandboxLeaseFailure | undefined;
|
|
155
|
+
/**
|
|
156
|
+
* `boot` carries the runtime's own reason, which repeats until what it boots
|
|
157
|
+
* from changes; `checkpoint_unusable` a start whose checkpoint cannot be
|
|
158
|
+
* restored for its `cause`, refused until the person starts the workspace
|
|
159
|
+
* fresh; `unhealthy` is a serving runtime that stopped answering; and
|
|
160
|
+
* `provider` the driver's error. `retrying` holds while the lease's scheduled
|
|
161
|
+
* retry is still ahead and its retry budget is not spent: a start in progress
|
|
162
|
+
* is waiting it out, and the next ensure runs it.
|
|
163
|
+
*/
|
|
164
|
+
export type SandboxLeaseFailure = {
|
|
165
|
+
kind: "boot" | "unhealthy" | "provider";
|
|
166
|
+
message: string;
|
|
167
|
+
retrying: boolean;
|
|
168
|
+
} | {
|
|
169
|
+
kind: "checkpoint_unusable";
|
|
170
|
+
cause: SandboxCheckpointUnusableCause;
|
|
171
|
+
message: string;
|
|
172
|
+
retrying: false;
|
|
173
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { SandboxLease } from "./contract";
|
|
2
|
+
import type { SandboxCheckpointUnusableCause, SandboxStartWindow } from "./lease-failure";
|
|
3
|
+
/** Who stopped the host: a person or the plane's explicit stop, the idle policy, the plane for a runtime that stopped answering, or the provider on its own. */
|
|
4
|
+
export type SandboxStopCause = "explicit" | "idle" | "unresponsive" | "provider";
|
|
5
|
+
/** `unsaved`: the stop ended the machine without capturing it; `unfrozen`: it captured files whose runtime did not freeze first. */
|
|
6
|
+
export type SandboxStopOutcome = {
|
|
7
|
+
unsaved?: true;
|
|
8
|
+
unfrozen?: true;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The lease transitions a deployment meters. A lease epoch is one boot: a wake
|
|
12
|
+
* acquires a new epoch with its own start, so `activeMs` (the epoch's start to
|
|
13
|
+
* its stop or destroy) is the machine time that boot spent, and is present only
|
|
14
|
+
* when the lease was serving at that moment. A destroyed epoch is `hostGone`
|
|
15
|
+
* when the provider lost its host, rather than Claxedo destroying it. A failed
|
|
16
|
+
* epoch whose start goes
|
|
17
|
+
* on in the next epoch is `retrying`; the start the person waits on has not
|
|
18
|
+
* failed until an epoch fails without one. `startLimit` is the provider's
|
|
19
|
+
* start-limit window an epoch was refused at. `hostLeftRunning` is a failed
|
|
20
|
+
* start whose host the manager could not stop, so it may still bill, and
|
|
21
|
+
* `checkpointUnusable` the cause of one refused because the checkpoint it
|
|
22
|
+
* restores cannot be used. `startedFresh` is the person dropping the lease's
|
|
23
|
+
* checkpoint so the next start boots the image; its `checkpointUnusable` is
|
|
24
|
+
* the cause the lease's last start was refused for, when it was. A stop is
|
|
25
|
+
* `unsaved` when it ended the machine without capturing it, so the lease
|
|
26
|
+
* keeps the checkpoint it had before, and `unfrozen` when it captured the
|
|
27
|
+
* files of a runtime that did not freeze first.
|
|
28
|
+
*/
|
|
29
|
+
export type SandboxLifecycleEvent = {
|
|
30
|
+
workspaceId: string;
|
|
31
|
+
epoch: number;
|
|
32
|
+
driver: string;
|
|
33
|
+
labels: Record<string, string>;
|
|
34
|
+
} & (({
|
|
35
|
+
kind: "stopped";
|
|
36
|
+
cause: SandboxStopCause;
|
|
37
|
+
activeMs?: number;
|
|
38
|
+
} & SandboxStopOutcome) | {
|
|
39
|
+
kind: "destroyed";
|
|
40
|
+
hostGone: boolean;
|
|
41
|
+
activeMs?: number;
|
|
42
|
+
} | ({
|
|
43
|
+
kind: "failed";
|
|
44
|
+
} & SandboxStartFailureFacts) | {
|
|
45
|
+
kind: "startedFresh";
|
|
46
|
+
checkpointUnusable?: SandboxCheckpointUnusableCause;
|
|
47
|
+
});
|
|
48
|
+
type SandboxStartFailureFacts = {
|
|
49
|
+
bootFailed: boolean;
|
|
50
|
+
retrying: boolean;
|
|
51
|
+
startLimit?: SandboxStartWindow;
|
|
52
|
+
hostLeftRunning?: true;
|
|
53
|
+
checkpointUnusable?: SandboxCheckpointUnusableCause;
|
|
54
|
+
};
|
|
55
|
+
export declare function createLifecycleReporter(sink: ((event: SandboxLifecycleEvent) => void) | undefined, now: () => number): {
|
|
56
|
+
stopped: (lease: SandboxLease, cause: SandboxStopCause, outcome?: SandboxStopOutcome) => void;
|
|
57
|
+
/** `endedAt` is when the host was last proven serving: a stop the provider made on its own is learned after the fact. */
|
|
58
|
+
hostStopped: (lease: SandboxLease, endedAt?: number) => void;
|
|
59
|
+
destroyed: (lease: SandboxLease) => void;
|
|
60
|
+
/** `endedAt` is when the host was last proven serving: the provider's loss is learned after the fact. */
|
|
61
|
+
hostGone: (lease: SandboxLease, endedAt?: number) => void;
|
|
62
|
+
failed: (lease: SandboxLease, failure: SandboxStartFailureFacts) => void;
|
|
63
|
+
startedFresh: (lease: SandboxLease, checkpointUnusable: SandboxCheckpointUnusableCause | undefined) => void;
|
|
64
|
+
};
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SandboxLeaseStore } from "./contract";
|
|
2
|
+
/**
|
|
3
|
+
* Ends the wait a failed start left on the workspace's lease, its retry budget
|
|
4
|
+
* with it, for a change that may have fixed what the start failed on: the next
|
|
5
|
+
* start runs the driver at once, as a start of its own. True when a wait was
|
|
6
|
+
* lifted; a lease that is not waiting on a failed start is left as it is.
|
|
7
|
+
*/
|
|
8
|
+
export declare function liftSandboxRetryWait(leaseStore: SandboxLeaseStore, workspaceId: string): Promise<boolean>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SandboxBootMode, SandboxDriver, SandboxLease, SandboxStartProgress } from "./contract";
|
|
2
|
+
/**
|
|
3
|
+
* The boot path a lease is on, read from its row alone, for the provisioning
|
|
4
|
+
* answers given before a driver step runs, so the connect UI keeps its honest
|
|
5
|
+
* label across the whole wait. Mirrors the predicates the manager's step uses
|
|
6
|
+
* when no caller boot source is set: the driver-snapshot default and its
|
|
7
|
+
* `resuming`. A host that never published a URL never served, so resuming it
|
|
8
|
+
* is the workspace's first start as the person sees it, not a wake.
|
|
9
|
+
*/
|
|
10
|
+
export declare function leaseBootMode(lease: SandboxLease, driver: Pick<SandboxDriver, "metadata" | "resumeHost">): SandboxBootMode;
|
|
11
|
+
/**
|
|
12
|
+
* The start the next epoch continues: the one a stale epoch was taken over
|
|
13
|
+
* in, or the one a provider failure interrupted while its retry is scheduled
|
|
14
|
+
* and its budget lasts. A boot failure, a spent budget, a lifted retry wait, a
|
|
15
|
+
* lost serving runtime and a stop all end the start the person was waiting
|
|
16
|
+
* on; the next epoch is a start of its own.
|
|
17
|
+
*/
|
|
18
|
+
export declare function continuedStart(lease: SandboxLease | undefined, input: {
|
|
19
|
+
now: number;
|
|
20
|
+
maxRetryCount: number;
|
|
21
|
+
}): SandboxStartProgress | undefined;
|
package/dist/lease-types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { SandboxCheckpointReference, SandboxPersistenceCapabilities, SandboxRestoreStatus } from "./
|
|
1
|
+
import type { SandboxCheckpointReference, SandboxMachineClass, SandboxPersistenceCapabilities, SandboxRestoreStatus, SandboxStartProgress } from "./contract";
|
|
2
2
|
export type SandboxLeaseRowStatus = "pending" | "acquiring" | "starting" | "ready" | "unhealthy" | "backoff" | "stopping" | "stopped" | "destroyed" | "failed";
|
|
3
|
-
export type SandboxComputeClass = "small" | "medium" | "large" | "gpu";
|
|
4
3
|
export type SandboxLeaseRow = {
|
|
5
4
|
workspace_id: string;
|
|
6
5
|
lease_id: string;
|
|
@@ -21,6 +20,7 @@ export type SandboxLeaseRow = {
|
|
|
21
20
|
driver_resource_id: string | null;
|
|
22
21
|
driver_snapshot_id: string | null;
|
|
23
22
|
sandbox_id: string | null;
|
|
23
|
+
routing_id?: string | null;
|
|
24
24
|
url: string | null;
|
|
25
25
|
retry_count: number;
|
|
26
26
|
next_retry_at: number | null;
|
|
@@ -28,7 +28,7 @@ export type SandboxLeaseRow = {
|
|
|
28
28
|
last_activity_at: number | null;
|
|
29
29
|
last_health_failure_at: number | null;
|
|
30
30
|
last_error: string | null;
|
|
31
|
-
|
|
31
|
+
machine_class: SandboxMachineClass | null;
|
|
32
32
|
accel_base_image_id: string | null;
|
|
33
33
|
accel_prepared_image_id: string | null;
|
|
34
34
|
accel_snapshot_id: string | null;
|
|
@@ -36,6 +36,7 @@ export type SandboxLeaseRow = {
|
|
|
36
36
|
checkpoint: SandboxCheckpointReference | null;
|
|
37
37
|
persistence: SandboxPersistenceCapabilities | null;
|
|
38
38
|
restore: SandboxRestoreStatus | null;
|
|
39
|
+
start: SandboxStartProgress | null;
|
|
39
40
|
created_at: number;
|
|
40
41
|
updated_at: number;
|
|
41
42
|
};
|
package/dist/runtime-env.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export type WorkspaceRuntimeControlEnv = {
|
|
|
5
5
|
managementJwksUrl?: string;
|
|
6
6
|
sessionAuthorityUrl?: string;
|
|
7
7
|
};
|
|
8
|
+
export declare function workspaceRuntimeIdentityEnvConflicts(env: Record<string, string> | undefined): string[];
|
|
9
|
+
export declare function assertWorkspaceRuntimeIdentityEnv(env: Record<string, string> | undefined): void;
|
|
10
|
+
export declare function workspaceRuntimeBootSecrets(env: Record<string, string>): string[];
|
|
8
11
|
export declare function workspaceRuntimeBootEnv(input: Parameters<typeof workspaceRuntimeTargetEnv>[0] & Parameters<typeof workspaceRuntimeSourceEnv>[0] & {
|
|
9
12
|
env?: Record<string, string>;
|
|
10
|
-
|
|
13
|
+
nativeHarness?: string;
|
|
11
14
|
controlEnv?: WorkspaceRuntimeControlEnv;
|
|
12
15
|
}): Record<string, string>;
|
|
13
16
|
export declare function workspaceRuntimeTargetEnv(input: {
|
|
@@ -30,6 +33,5 @@ export declare function workspaceRuntimeServiceExposureEnv(input: {
|
|
|
30
33
|
driver: SandboxDriverID;
|
|
31
34
|
source: "driver-service-url";
|
|
32
35
|
access: "private" | "public" | "driver-authenticated" | "unknown";
|
|
33
|
-
fallbackAccess?: "private" | "public" | "driver-authenticated" | "unknown";
|
|
34
36
|
note?: string;
|
|
35
37
|
}): Record<string, string>;
|
package/dist/runtime-env.mjs
CHANGED
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
// src/runtime-env.ts
|
|
2
|
+
var WORKSPACE_RUNTIME_IDENTITY_ENV_KEYS = [
|
|
3
|
+
"WORKSPACE_RUNTIME_WORKSPACE_ID",
|
|
4
|
+
"WORKSPACE_RUNTIME_HOST_ID",
|
|
5
|
+
"WORKSPACE_RUNTIME_RELAY_WORKSPACE_IDS"
|
|
6
|
+
];
|
|
7
|
+
function workspaceRuntimeIdentityEnvConflicts(env) {
|
|
8
|
+
if (!env) return [];
|
|
9
|
+
return WORKSPACE_RUNTIME_IDENTITY_ENV_KEYS.filter((key) => env[key] !== void 0);
|
|
10
|
+
}
|
|
11
|
+
function assertWorkspaceRuntimeIdentityEnv(env) {
|
|
12
|
+
const conflicts = workspaceRuntimeIdentityEnvConflicts(env);
|
|
13
|
+
if (conflicts.length === 0) return;
|
|
14
|
+
throw new Error(
|
|
15
|
+
`sandbox env cannot set runtime identity ${conflicts.join(", ")}: the driver's placement owns the identity its target reports and the lease records`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
var WORKSPACE_RUNTIME_PUBLIC_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
19
|
+
...WORKSPACE_RUNTIME_IDENTITY_ENV_KEYS,
|
|
20
|
+
"WORKSPACE_RUNTIME_DIRECTORY",
|
|
21
|
+
"WORKSPACE_RUNTIME_PORT",
|
|
22
|
+
"WORKSPACE_RUNTIME_HOST",
|
|
23
|
+
"WORKSPACE_RUNTIME_SOURCE_KIND",
|
|
24
|
+
"WORKSPACE_RUNTIME_GIT_REPO_URL",
|
|
25
|
+
"WORKSPACE_RUNTIME_GIT_BRANCH",
|
|
26
|
+
"WORKSPACE_RUNTIME_NATIVE_HARNESS",
|
|
27
|
+
"WORKSPACE_RUNTIME_RELAY_JWKS_URL",
|
|
28
|
+
"WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL",
|
|
29
|
+
"WORKSPACE_RUNTIME_SESSION_AUTHORITY_URL"
|
|
30
|
+
]);
|
|
31
|
+
function workspaceRuntimeBootSecrets(env) {
|
|
32
|
+
return Object.entries(env).flatMap(([key, value]) => WORKSPACE_RUNTIME_PUBLIC_ENV_KEYS.has(key) ? [] : [value]);
|
|
33
|
+
}
|
|
2
34
|
function workspaceRuntimeBootEnv(input) {
|
|
35
|
+
assertWorkspaceRuntimeIdentityEnv(input.env);
|
|
3
36
|
const env = {
|
|
4
37
|
...workspaceRuntimeTargetEnv(input),
|
|
5
38
|
...workspaceRuntimeSourceEnv(input),
|
|
6
39
|
...input.env
|
|
7
40
|
};
|
|
8
|
-
if (input.
|
|
41
|
+
if (input.nativeHarness) env.WORKSPACE_RUNTIME_NATIVE_HARNESS = input.nativeHarness;
|
|
9
42
|
if (input.controlEnv?.relayJwksUrl) env.WORKSPACE_RUNTIME_RELAY_JWKS_URL = input.controlEnv.relayJwksUrl;
|
|
10
43
|
if (input.controlEnv?.relayVerifyPem) env.WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEM = input.controlEnv.relayVerifyPem;
|
|
11
44
|
if (input.controlEnv?.managementJwksUrl) env.WORKSPACE_RUNTIME_MANAGEMENT_JWKS_URL = input.controlEnv.managementJwksUrl;
|
|
@@ -35,12 +68,14 @@ function workspaceRuntimeServiceExposureEnv(input) {
|
|
|
35
68
|
WORKSPACE_RUNTIME_SERVICE_EXPOSURE_SOURCE: input.source,
|
|
36
69
|
WORKSPACE_RUNTIME_SERVICE_EXPOSURE_ACCESS: input.access,
|
|
37
70
|
WORKSPACE_RUNTIME_SERVICE_EXPOSURE_DRIVER: input.driver,
|
|
38
|
-
...input.fallbackAccess ? { WORKSPACE_RUNTIME_SERVICE_EXPOSURE_FALLBACK_ACCESS: input.fallbackAccess } : {},
|
|
39
71
|
...input.note ? { WORKSPACE_RUNTIME_SERVICE_EXPOSURE_NOTE: input.note } : {}
|
|
40
72
|
};
|
|
41
73
|
}
|
|
42
74
|
export {
|
|
75
|
+
assertWorkspaceRuntimeIdentityEnv,
|
|
43
76
|
workspaceRuntimeBootEnv,
|
|
77
|
+
workspaceRuntimeBootSecrets,
|
|
78
|
+
workspaceRuntimeIdentityEnvConflicts,
|
|
44
79
|
workspaceRuntimeServiceExposureEnv,
|
|
45
80
|
workspaceRuntimeSourceEnv,
|
|
46
81
|
workspaceRuntimeTargetEnv
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { SandboxLeaseStore, SandboxMutationResult, SandboxRuntimeSnapshotInput } from "./contract";
|
|
2
|
+
/**
|
|
3
|
+
* How a deployment moves a lease's retry budget when its runtime reports on
|
|
4
|
+
* itself. Two answers, both deliberate:
|
|
5
|
+
*
|
|
6
|
+
* - `demote` — an unhealthy report stops the lease resolving for routing and
|
|
7
|
+
* moves nothing else, so a reporting sandbox cannot spend a budget it does
|
|
8
|
+
* not own; the next `ensure` decides what happens. The default, and what a
|
|
9
|
+
* manager whose only observer is the runtime itself wants.
|
|
10
|
+
* - `retry-budget` — the report also spends the budget, the way a supervisor
|
|
11
|
+
* running its own health monitor over the same sandbox already spends it.
|
|
12
|
+
* Chosen where a lease has TWO observers of one sandbox: leaving them to
|
|
13
|
+
* write different shapes for the same unhealthy minute is the divergence
|
|
14
|
+
* this removes. It must hand the budget back on the next serving report,
|
|
15
|
+
* or a flapping runtime walks its own lease into a terminal `failed`.
|
|
16
|
+
*/
|
|
17
|
+
export type SandboxRuntimeLivenessPolicy = {
|
|
18
|
+
kind: "demote";
|
|
19
|
+
} | {
|
|
20
|
+
kind: "retry-budget";
|
|
21
|
+
/** `undefined` means the budget is spent: the lease stays unavailable until an operator acts. */
|
|
22
|
+
nextRetryAt: (input: {
|
|
23
|
+
retryCount: number;
|
|
24
|
+
now: number;
|
|
25
|
+
}) => number | undefined;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The one writer of a lease's liveness, shared by `SandboxManager`'s
|
|
29
|
+
* register/heartbeat and by any supervisor that probes the same question for
|
|
30
|
+
* itself. A second implementation of this sequence is how one deployment ends
|
|
31
|
+
* up able to revive a lease another deployment refuses to.
|
|
32
|
+
*
|
|
33
|
+
* Liveness only: identity stays whatever `recordTarget` wrote from the
|
|
34
|
+
* driver's answer (@see SandboxRuntimeSnapshotInput). It takes a
|
|
35
|
+
* `SandboxLeaseStore` and no driver, because recording that a sandbox is
|
|
36
|
+
* alive must not depend on provider credentials the deployment may no longer
|
|
37
|
+
* hold.
|
|
38
|
+
*
|
|
39
|
+
* Fenced twice — on the epoch the reporter names, and on the status read here
|
|
40
|
+
* — because the guards below and the write are separate transactions, and a
|
|
41
|
+
* stop, a destroy or a replacement epoch can land between them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function applySandboxRuntimeSnapshot(input: {
|
|
44
|
+
leaseStore: SandboxLeaseStore;
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
snapshot: SandboxRuntimeSnapshotInput;
|
|
47
|
+
now?: () => number;
|
|
48
|
+
liveness?: SandboxRuntimeLivenessPolicy;
|
|
49
|
+
}): Promise<SandboxMutationResult>;
|
|
@@ -1 +1,10 @@
|
|
|
1
1
|
export declare function workspaceRuntimeVersion(): string;
|
|
2
|
+
/**
|
|
3
|
+
* The oldest workspace-runtime version a driver may restore a workspace's
|
|
4
|
+
* snapshot into running again. A filesystem snapshot carries the runtime it
|
|
5
|
+
* was taken with, so a fix to the runtime reaches a workspace that keeps
|
|
6
|
+
* waking only when this rises. Raise it, together with the runtime version,
|
|
7
|
+
* for a security-relevant runtime change alone: every snapshot below it is
|
|
8
|
+
* refused and its person must start the workspace fresh.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MINIMUM_RESTORABLE_WORKSPACE_RUNTIME_VERSION = "0.5.2";
|
package/dist/runtime-version.mjs
CHANGED
|
@@ -3,6 +3,8 @@ var DEFAULT_WORKSPACE_RUNTIME_VERSION = "0.5.2";
|
|
|
3
3
|
function workspaceRuntimeVersion() {
|
|
4
4
|
return process.env.WORKSPACE_RUNTIME_VERSION || DEFAULT_WORKSPACE_RUNTIME_VERSION;
|
|
5
5
|
}
|
|
6
|
+
var MINIMUM_RESTORABLE_WORKSPACE_RUNTIME_VERSION = "0.5.2";
|
|
6
7
|
export {
|
|
8
|
+
MINIMUM_RESTORABLE_WORKSPACE_RUNTIME_VERSION,
|
|
7
9
|
workspaceRuntimeVersion
|
|
8
10
|
};
|
|
@@ -3,7 +3,6 @@ export type SandboxServiceUrlExposure = {
|
|
|
3
3
|
source: "driver-service-url";
|
|
4
4
|
access: "private" | "public" | "driver-authenticated" | "unknown";
|
|
5
5
|
driver: SandboxDriverID;
|
|
6
|
-
fallbackAccess?: "private" | "public" | "driver-authenticated" | "unknown";
|
|
7
6
|
note?: string;
|
|
8
7
|
};
|
|
9
8
|
export declare function sandboxServiceUrlExposure(driver: SandboxDriverID): SandboxServiceUrlExposure;
|
|
@@ -8,15 +8,6 @@ function sandboxServiceUrlExposure(driver) {
|
|
|
8
8
|
note: "Docker service URLs resolve to a local loopback port on the host."
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
if (driver === "daytona") {
|
|
12
|
-
return {
|
|
13
|
-
source: "driver-service-url",
|
|
14
|
-
access: "driver-authenticated",
|
|
15
|
-
driver,
|
|
16
|
-
fallbackAccess: "public",
|
|
17
|
-
note: "Daytona uses a signed preview URL when available and falls back to a preview link."
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
11
|
return {
|
|
21
12
|
source: "driver-service-url",
|
|
22
13
|
access: "public",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SandboxDriver, SandboxLease, SandboxLeaseStore, SandboxMutationResult, SandboxResource } from "./contract";
|
|
2
|
+
import type { createLifecycleReporter } from "./lease-lifecycle";
|
|
3
|
+
/**
|
|
4
|
+
* Drops the lease's checkpoint, restore record and the failure it left, so
|
|
5
|
+
* the next start boots the image and clones the repository. Refused while a
|
|
6
|
+
* start or the runtime runs: a running workspace's files are not yet in any
|
|
7
|
+
* checkpoint, and a start in flight would restore what this drops.
|
|
8
|
+
*/
|
|
9
|
+
export declare function startSandboxFresh(input: {
|
|
10
|
+
workspaceId: string;
|
|
11
|
+
driver: SandboxDriver;
|
|
12
|
+
leaseStore: SandboxLeaseStore;
|
|
13
|
+
lifecycle: ReturnType<typeof createLifecycleReporter>;
|
|
14
|
+
resource: (lease: SandboxLease) => SandboxResource | undefined;
|
|
15
|
+
now: number;
|
|
16
|
+
maxRetryCount: number;
|
|
17
|
+
}): Promise<SandboxMutationResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SandboxStartPhase } from "@claxedo/sandbox-contract";
|
|
2
|
+
import type { SandboxBootMode, SandboxLease, SandboxLeaseStore, SandboxStartPhaseEvent, SandboxStartPhasesReport } from "./contract";
|
|
3
|
+
/**
|
|
4
|
+
* The one writer of a lease epoch's start: each phase is persisted into the
|
|
5
|
+
* lease before it is emitted, at most once per epoch, so a phase observed by
|
|
6
|
+
* two pollers or reported twice by a runtime reaches the sink once.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createSandboxStartRecorder(options: {
|
|
9
|
+
leaseStore: SandboxLeaseStore;
|
|
10
|
+
driver: string;
|
|
11
|
+
now: () => number;
|
|
12
|
+
onStartPhase?: (event: SandboxStartPhaseEvent) => void;
|
|
13
|
+
}): {
|
|
14
|
+
/**
|
|
15
|
+
* The phases one provision call observes, each timed and written into the
|
|
16
|
+
* lease as it ends, so a poller reading the lease mid-boot sees how far the
|
|
17
|
+
* start has come. `opened` writes the epoch's `lease_decision` before the
|
|
18
|
+
* driver runs, so a stop that lands mid-boot finds the boot it ends on the
|
|
19
|
+
* row. A serving lease is not starting. An epoch acquired with a start it
|
|
20
|
+
* continues has that start and no phase yet; one acquired without begins
|
|
21
|
+
* its own here.
|
|
22
|
+
*/
|
|
23
|
+
observe(lease: SandboxLease, input: {
|
|
24
|
+
bootMode: SandboxBootMode;
|
|
25
|
+
enteredAt: number;
|
|
26
|
+
labels: Record<string, string>;
|
|
27
|
+
}): {
|
|
28
|
+
opened: () => Promise<void>;
|
|
29
|
+
end: (phase: SandboxStartPhase) => Promise<void>;
|
|
30
|
+
} | undefined;
|
|
31
|
+
report(workspaceId: string, input: SandboxStartPhasesReport): Promise<void>;
|
|
32
|
+
markAfter(workspaceId: string, input: {
|
|
33
|
+
epoch: number;
|
|
34
|
+
phase: SandboxStartPhase;
|
|
35
|
+
notBefore: number;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
};
|
package/dist/stores/memory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SandboxLeaseStore, SandboxLease, SandboxRegion } from "
|
|
1
|
+
import type { SandboxLeaseStore, SandboxLease, SandboxRegion } from "../contract";
|
|
2
2
|
export declare function createMemoryLeaseStore(seed?: SandboxLease[]): SandboxLeaseStore;
|
|
3
3
|
export declare function sandboxLease(input: Partial<SandboxLease> & {
|
|
4
4
|
workspaceId: string;
|