@claxedo/sandbox-manager 0.10.0 → 0.10.2
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 +36 -4
- package/dist/checkpoint-manager.mjs +8 -0
- package/dist/contract.d.ts +12 -1
- package/dist/driver-ensure-input.d.ts +21 -0
- package/dist/drivers/boat-machine.d.ts +90 -0
- package/dist/drivers/boat-preparation.test-support.d.ts +2 -0
- package/dist/drivers/boat-template.mjs +132 -81
- package/dist/drivers/boat.d.ts +0 -12
- package/dist/drivers/boat.mjs +136 -97
- package/dist/drivers/cloudflare.mjs +7 -3
- package/dist/drivers/docker.mjs +5 -0
- package/dist/drivers/local-brokering.d.ts +12 -0
- package/dist/drivers/local-brokering.mjs +11 -6
- package/dist/drivers/modal.mjs +5 -0
- package/dist/drivers/vercel.mjs +5 -0
- package/dist/image-name.mjs +5 -0
- package/dist/image.d.ts +1 -0
- package/dist/image.mjs +6 -0
- package/dist/index.mjs +82 -50
- package/dist/lease-failure.d.ts +17 -0
- package/dist/runtime-version.d.ts +21 -7
- package/dist/runtime-version.mjs +7 -2
- package/dist/start-telemetry.d.ts +5 -3
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -314,7 +314,7 @@ function isPrivateIpAddress(ip) {
|
|
|
314
314
|
return isPrivateIpv4(embedded);
|
|
315
315
|
return false;
|
|
316
316
|
}
|
|
317
|
-
var sandboxCheckpointUnusableCauses = ["expired", "
|
|
317
|
+
var sandboxCheckpointUnusableCauses = ["expired", "runtime_modified", "not_restorable"];
|
|
318
318
|
function isSandboxCheckpointUnusableCause(value) {
|
|
319
319
|
return sandboxCheckpointUnusableCauses.some((cause) => cause === value);
|
|
320
320
|
}
|
|
@@ -378,6 +378,17 @@ var SandboxRuntimeBootError = class extends Error {
|
|
|
378
378
|
this.name = "SandboxRuntimeBootError";
|
|
379
379
|
}
|
|
380
380
|
};
|
|
381
|
+
function isSandboxImageDownloadFailure(err) {
|
|
382
|
+
return err instanceof Error && "imageDownloadFailed" in err && err.imageDownloadFailed === true;
|
|
383
|
+
}
|
|
384
|
+
var SandboxImageDownloadError = class extends Error {
|
|
385
|
+
imageDownloadFailed = true;
|
|
386
|
+
constructor(reason) {
|
|
387
|
+
super(reason);
|
|
388
|
+
this.name = "SandboxImageDownloadError";
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
var SANDBOX_UPDATE_DOWNLOAD_INTERRUPTED = "update_download_interrupted";
|
|
381
392
|
function isSandboxCheckpointUnusable(err) {
|
|
382
393
|
return !!err && typeof err === "object" && isSandboxCheckpointUnusableCause(err.checkpointUnusable);
|
|
383
394
|
}
|
|
@@ -516,7 +527,8 @@ var SANDBOX_RUNTIME_UNHEALTHY = "runtime_unhealthy";
|
|
|
516
527
|
var SANDBOX_RETRY_CAP_EXCEEDED = "runtime_retry_cap_exceeded";
|
|
517
528
|
var CODED_REASONS = /* @__PURE__ */ new Map([
|
|
518
529
|
[SANDBOX_RUNTIME_UNHEALTHY, "The workspace machine stopped answering."],
|
|
519
|
-
[SANDBOX_RETRY_CAP_EXCEEDED, "Several tries in a row failed; try again later."]
|
|
530
|
+
[SANDBOX_RETRY_CAP_EXCEEDED, "Several tries in a row failed; try again later."],
|
|
531
|
+
[SANDBOX_UPDATE_DOWNLOAD_INTERRUPTED, "The update couldn't finish downloading; your workspace is still running. Try again."]
|
|
520
532
|
]);
|
|
521
533
|
function firstLine(text) {
|
|
522
534
|
return text.split("\n", 1)[0]?.trim() ?? text;
|
|
@@ -607,11 +619,19 @@ var PUBLIC_API_ERRORS = {
|
|
|
607
619
|
enrollment_paused: { status: 403, message: "enrollment_paused" },
|
|
608
620
|
enrollment_owner_ineligible: { status: 403, message: "enrollment_owner_ineligible" },
|
|
609
621
|
enrollment_key_version_mismatch: { status: 403, message: "enrollment_key_version_mismatch" },
|
|
622
|
+
enrollment_denied: { status: 403, message: "enrollment_denied" },
|
|
623
|
+
enrollment_approval_expired: { status: 403, message: "enrollment_approval_expired" },
|
|
624
|
+
enrollment_awaiting_approval: { status: 403, message: "enrollment_awaiting_approval" },
|
|
610
625
|
invitation_invalid: { status: 403, message: "invitation_invalid" },
|
|
611
626
|
invitation_expired: { status: 410, message: "invitation_expired" },
|
|
612
627
|
invitation_revoked: { status: 410, message: "invitation_revoked" },
|
|
613
628
|
invitation_redeemed: { status: 409, message: "invitation_redeemed" },
|
|
614
629
|
invitation_host_conflict: { status: 409, message: "invitation_host_conflict" },
|
|
630
|
+
pairing_invalid: { status: 403, message: "pairing_invalid" },
|
|
631
|
+
pairing_expired: { status: 410, message: "pairing_expired" },
|
|
632
|
+
pairing_used: { status: 409, message: "pairing_used" },
|
|
633
|
+
pairing_owner_signed_out: { status: 410, message: "pairing_owner_signed_out" },
|
|
634
|
+
pairing_decided: { status: 409, message: "pairing_decided" },
|
|
615
635
|
enrollment_generation_superseded: { status: 409, message: "enrollment_generation_superseded" },
|
|
616
636
|
host_assignment_outside_scope: { status: 400, message: "host_assignment_outside_scope" },
|
|
617
637
|
host_sealing_key_undeclared: { status: 409, message: "host_sealing_key_undeclared" },
|
|
@@ -865,6 +885,51 @@ async function refusedReason(leaseStore, workspaceId, epoch) {
|
|
|
865
885
|
return "runtime_lease_not_serving";
|
|
866
886
|
}
|
|
867
887
|
|
|
888
|
+
// src/driver-ensure-input.ts
|
|
889
|
+
function sandboxId(driver, workspaceId) {
|
|
890
|
+
return `${driver}-${workspaceId}`;
|
|
891
|
+
}
|
|
892
|
+
function leaseHostId(driver, workspaceId, lease, managerInput) {
|
|
893
|
+
return managerInput?.hostId ?? lease.hostId ?? sandboxId(driver.id, workspaceId);
|
|
894
|
+
}
|
|
895
|
+
function ensureHostInput(input) {
|
|
896
|
+
const egress = sandboxEgressDisposition(input.driver.metadata.egressControl, input.managerInput?.net);
|
|
897
|
+
const labels = {
|
|
898
|
+
app: input.appLabel,
|
|
899
|
+
workspaceId: input.workspaceId,
|
|
900
|
+
epoch: String(input.lease.epoch),
|
|
901
|
+
homeRegion: input.homeRegion,
|
|
902
|
+
...input.managerInput?.labels
|
|
903
|
+
};
|
|
904
|
+
return {
|
|
905
|
+
workspaceId: input.workspaceId,
|
|
906
|
+
hostId: leaseHostId(input.driver, input.workspaceId, input.lease, input.managerInput),
|
|
907
|
+
homeRegion: input.homeRegion,
|
|
908
|
+
epoch: input.lease.epoch,
|
|
909
|
+
labels,
|
|
910
|
+
bootSource: input.managerInput?.bootSource ?? (input.lease.checkpoint && input.lease.status !== "ready" && input.driver.metadata.persistence.resume === "replacement-restore" ? { kind: "driver-snapshot", snapshotId: input.lease.checkpoint.providerReference } : { kind: "default" }),
|
|
911
|
+
workspaceRoot: input.managerInput?.workspaceRoot ?? "/workspace",
|
|
912
|
+
runtimeCwd: input.managerInput?.runtimeCwd,
|
|
913
|
+
workspaceRuntimePort: input.managerInput?.workspaceRuntimePort ?? DEFAULT_WORKSPACE_RUNTIME_PORT,
|
|
914
|
+
env: { ...input.managerInput?.env, ...input.leaseEnv },
|
|
915
|
+
// Brokered secrets ride their own channel — NEVER merged into labels or env.
|
|
916
|
+
// Presence of the key, not its length, is the signal: `[]` is the caller
|
|
917
|
+
// withdrawing every brokered secret, and dropping it would reach the driver
|
|
918
|
+
// as "preserve what you have".
|
|
919
|
+
...input.managerInput?.secrets ? { secrets: input.managerInput.secrets } : {},
|
|
920
|
+
source: input.managerInput?.source,
|
|
921
|
+
...input.managerInput?.updateImage ? { updateImage: true } : {},
|
|
922
|
+
...input.lease.machineClass ?? input.managerInput?.machineClass ? { machineClass: input.lease.machineClass ?? input.managerInput?.machineClass } : {},
|
|
923
|
+
exposure: input.managerInput?.exposure ?? (input.driver.metadata.targetAccess === "loopback" ? { kind: "loopback" } : { kind: "relay" }),
|
|
924
|
+
// Withheld, never downgraded: `undefined` here is the same shape a caller
|
|
925
|
+
// that asked for no containment produces, so the throwing drivers take
|
|
926
|
+
// their allow-all path and their throws stay intact for anyone who hands
|
|
927
|
+
// them a policy directly.
|
|
928
|
+
...egress.action === "enforce" && input.managerInput?.net ? { net: sandboxNetworkPolicyWithBrokeredHosts(input.managerInput.net, input.managerInput.secrets) } : {},
|
|
929
|
+
snapshot: input.managerInput?.snapshot
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
868
933
|
// src/egress-warnings.ts
|
|
869
934
|
function egressUnenforcedMessage(input) {
|
|
870
935
|
const scope = input.workspaceId ? `workspace ${input.workspaceId}` : "every sandbox it provisions";
|
|
@@ -997,13 +1062,15 @@ function createSandboxStartRecorder(options) {
|
|
|
997
1062
|
* lease as it ends, so a poller reading the lease mid-boot sees how far the
|
|
998
1063
|
* start has come. `opened` writes the epoch's `lease_decision` before the
|
|
999
1064
|
* driver runs, so a stop that lands mid-boot finds the boot it ends on the
|
|
1000
|
-
* row. A serving lease is not starting
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1065
|
+
* row. A serving lease is not starting, unless the step is a person's
|
|
1066
|
+
* update, which is a start of its own on the same epoch. An epoch acquired
|
|
1067
|
+
* with a start it continues has that start and no phase yet; one acquired
|
|
1068
|
+
* without begins its own here.
|
|
1003
1069
|
*/
|
|
1004
1070
|
observe(lease, input) {
|
|
1005
|
-
if (lease.status === "ready") return void 0;
|
|
1006
|
-
|
|
1071
|
+
if (lease.status === "ready" && !input.update) return void 0;
|
|
1072
|
+
const begun = { requestedAt: input.enteredAt, startedAt: input.enteredAt, bootMode: input.bootMode, markedAt: input.enteredAt, phases: [] };
|
|
1073
|
+
let progress = lease.status === "ready" ? begun : lease.start ?? begun;
|
|
1007
1074
|
const ended = [];
|
|
1008
1075
|
let markedAt = progress.markedAt;
|
|
1009
1076
|
const mark = (phase) => {
|
|
@@ -1135,48 +1202,6 @@ var WORKSPACE_RUNTIME_PUBLIC_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
|
1135
1202
|
|
|
1136
1203
|
// src/manager.ts
|
|
1137
1204
|
var DEFAULT_APP_LABEL = "claxedo";
|
|
1138
|
-
function sandboxId(driver, workspaceId) {
|
|
1139
|
-
return `${driver}-${workspaceId}`;
|
|
1140
|
-
}
|
|
1141
|
-
function leaseHostId(driver, workspaceId, lease, managerInput) {
|
|
1142
|
-
return managerInput?.hostId ?? lease.hostId ?? sandboxId(driver.id, workspaceId);
|
|
1143
|
-
}
|
|
1144
|
-
function ensureHostInput(input) {
|
|
1145
|
-
const egress = sandboxEgressDisposition(input.driver.metadata.egressControl, input.managerInput?.net);
|
|
1146
|
-
const labels = {
|
|
1147
|
-
app: input.appLabel,
|
|
1148
|
-
workspaceId: input.workspaceId,
|
|
1149
|
-
epoch: String(input.lease.epoch),
|
|
1150
|
-
homeRegion: input.homeRegion,
|
|
1151
|
-
...input.managerInput?.labels
|
|
1152
|
-
};
|
|
1153
|
-
return {
|
|
1154
|
-
workspaceId: input.workspaceId,
|
|
1155
|
-
hostId: leaseHostId(input.driver, input.workspaceId, input.lease, input.managerInput),
|
|
1156
|
-
homeRegion: input.homeRegion,
|
|
1157
|
-
epoch: input.lease.epoch,
|
|
1158
|
-
labels,
|
|
1159
|
-
bootSource: input.managerInput?.bootSource ?? (input.lease.checkpoint && input.lease.status !== "ready" && input.driver.metadata.persistence.resume === "replacement-restore" ? { kind: "driver-snapshot", snapshotId: input.lease.checkpoint.providerReference } : { kind: "default" }),
|
|
1160
|
-
workspaceRoot: input.managerInput?.workspaceRoot ?? "/workspace",
|
|
1161
|
-
runtimeCwd: input.managerInput?.runtimeCwd,
|
|
1162
|
-
workspaceRuntimePort: input.managerInput?.workspaceRuntimePort ?? DEFAULT_WORKSPACE_RUNTIME_PORT,
|
|
1163
|
-
env: { ...input.managerInput?.env, ...input.leaseEnv },
|
|
1164
|
-
// Brokered secrets ride their own channel — NEVER merged into labels or env.
|
|
1165
|
-
// Presence of the key, not its length, is the signal: `[]` is the caller
|
|
1166
|
-
// withdrawing every brokered secret, and dropping it would reach the driver
|
|
1167
|
-
// as "preserve what you have".
|
|
1168
|
-
...input.managerInput?.secrets ? { secrets: input.managerInput.secrets } : {},
|
|
1169
|
-
source: input.managerInput?.source,
|
|
1170
|
-
...input.lease.machineClass ?? input.managerInput?.machineClass ? { machineClass: input.lease.machineClass ?? input.managerInput?.machineClass } : {},
|
|
1171
|
-
exposure: input.managerInput?.exposure ?? (input.driver.metadata.targetAccess === "loopback" ? { kind: "loopback" } : { kind: "relay" }),
|
|
1172
|
-
// Withheld, never downgraded: `undefined` here is the same shape a caller
|
|
1173
|
-
// that asked for no containment produces, so the throwing drivers take
|
|
1174
|
-
// their allow-all path and their throws stay intact for anyone who hands
|
|
1175
|
-
// them a policy directly.
|
|
1176
|
-
...egress.action === "enforce" && input.managerInput?.net ? { net: sandboxNetworkPolicyWithBrokeredHosts(input.managerInput.net, input.managerInput.secrets) } : {},
|
|
1177
|
-
snapshot: input.managerInput?.snapshot
|
|
1178
|
-
};
|
|
1179
|
-
}
|
|
1180
1205
|
function createSandboxManager(options) {
|
|
1181
1206
|
const persistence = validateSandboxPersistenceCapabilities(options.driver.metadata.persistence);
|
|
1182
1207
|
if (!persistence.valid) throw new Error(`Invalid sandbox persistence capabilities: ${persistence.reason}`);
|
|
@@ -1286,7 +1311,7 @@ function createSandboxManager(options) {
|
|
|
1286
1311
|
}
|
|
1287
1312
|
const resuming = Boolean(lease.sandboxId && ensure.bootSource?.kind === "default" && options.driver.resumeHost);
|
|
1288
1313
|
const bootMode = resuming && lease.url ? "resume" : ensure.bootSource?.kind === "driver-snapshot" ? "restore" : "cold-start";
|
|
1289
|
-
const start = starts.observe(lease, { bootMode, enteredAt, labels: ensure.labels });
|
|
1314
|
+
const start = starts.observe(lease, { bootMode, enteredAt, labels: ensure.labels, ...managerInput.updateImage ? { update: true } : {} });
|
|
1290
1315
|
await start?.opened();
|
|
1291
1316
|
ensure.onResource = async (resource) => {
|
|
1292
1317
|
const recorded = await options.leaseStore.recordTarget(workspaceId, lease.epoch, {
|
|
@@ -1352,6 +1377,10 @@ function createSandboxManager(options) {
|
|
|
1352
1377
|
const error = checkpointUnusable ? sandboxCheckpointUnusableError(checkpointUnusable, message) : bootFailed ? sandboxRuntimeBootFailedError(message) : startLimit ? sandboxProviderRateLimitedError(options.driver.id, startLimit) : isSandboxProviderKeyRefused(err) ? sandboxProviderKeyRefusedError(options.driver.id, message) : isSandboxStartRefusal(err) || sandboxLeaseEndedUnderStart(message) ? message : sandboxProviderFailedError(options.driver.id, message);
|
|
1353
1378
|
if (lease.status === "ready" && !bootFailed) {
|
|
1354
1379
|
const updated = await options.leaseStore.update(workspaceId, lease.epoch, { lastError: error }, "ready");
|
|
1380
|
+
if (managerInput.updateImage) {
|
|
1381
|
+
console.warn("A workspace's update to the deployment's image failed; it keeps serving its image", { workspaceId, epoch: lease.epoch, error });
|
|
1382
|
+
return { status: "unavailable", error: isSandboxImageDownloadFailure(err) ? SANDBOX_UPDATE_DOWNLOAD_INTERRUPTED : error, epoch: lease.epoch, homeRegion };
|
|
1383
|
+
}
|
|
1355
1384
|
if (updated) {
|
|
1356
1385
|
const resolved = await leaseTarget(updated);
|
|
1357
1386
|
if (resolved.status === "ready") return { ...resolved, stale: true };
|
|
@@ -1698,8 +1727,10 @@ export {
|
|
|
1698
1727
|
SANDBOX_RETRY_CAP_EXCEEDED,
|
|
1699
1728
|
SANDBOX_RUNTIME_UNHEALTHY,
|
|
1700
1729
|
SANDBOX_START_WINDOWS,
|
|
1730
|
+
SANDBOX_UPDATE_DOWNLOAD_INTERRUPTED,
|
|
1701
1731
|
SandboxCheckpointUnusableError,
|
|
1702
1732
|
SandboxHostGoneError,
|
|
1733
|
+
SandboxImageDownloadError,
|
|
1703
1734
|
SandboxProviderKeyRefusedError,
|
|
1704
1735
|
SandboxProviderRateLimitedError,
|
|
1705
1736
|
SandboxRuntimeBootError,
|
|
@@ -1719,6 +1750,7 @@ export {
|
|
|
1719
1750
|
isSandboxCheckpointUnusable,
|
|
1720
1751
|
isSandboxCheckpointUnusableCause,
|
|
1721
1752
|
isSandboxHostGone,
|
|
1753
|
+
isSandboxImageDownloadFailure,
|
|
1722
1754
|
isSandboxListingUnsupported,
|
|
1723
1755
|
isSandboxProviderKeyRefused,
|
|
1724
1756
|
isSandboxProviderRateLimited,
|
package/dist/lease-failure.d.ts
CHANGED
|
@@ -15,6 +15,23 @@ export declare class SandboxRuntimeBootError extends Error implements SandboxRun
|
|
|
15
15
|
readonly runtimeBootFailed: true;
|
|
16
16
|
constructor(reason: string);
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* A driver throws this shape when the image a start pulls did not finish
|
|
20
|
+
* downloading because the registry link kept breaking. Nothing the machine
|
|
21
|
+
* served has changed, so an update that fails this way leaves the workspace
|
|
22
|
+
* serving its image.
|
|
23
|
+
*/
|
|
24
|
+
export type SandboxImageDownloadFailure = {
|
|
25
|
+
imageDownloadFailed: true;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function isSandboxImageDownloadFailure(err: unknown): err is SandboxImageDownloadFailure;
|
|
29
|
+
export declare class SandboxImageDownloadError extends Error implements SandboxImageDownloadFailure {
|
|
30
|
+
readonly imageDownloadFailed: true;
|
|
31
|
+
constructor(reason: string);
|
|
32
|
+
}
|
|
33
|
+
/** The answer to a person's update whose image download kept breaking; the lease keeps the provider's own text. */
|
|
34
|
+
export declare const SANDBOX_UPDATE_DOWNLOAD_INTERRUPTED = "update_download_interrupted";
|
|
18
35
|
export { isSandboxCheckpointUnusableCause, sandboxCheckpointUnusableCauses, type SandboxCheckpointUnusableCause };
|
|
19
36
|
/**
|
|
20
37
|
* A driver throws this shape when the checkpoint a start restores cannot be
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
export declare function workspaceRuntimeVersion(): string;
|
|
2
2
|
/**
|
|
3
|
-
* The oldest workspace-runtime version
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* The oldest workspace-runtime version this control plane and relay serve, for
|
|
4
|
+
* every driver: a workspace's snapshot restored, or the older image a Boat wake
|
|
5
|
+
* keeps serving instead of waiting on the deployment's. A filesystem snapshot
|
|
6
|
+
* carries the runtime it was taken with, and a kept image the runtime it
|
|
7
|
+
* shipped, so a fix to the runtime reaches a workspace that keeps waking only
|
|
8
|
+
* when this rises. Below it a Cloudflare restore is refused and its person
|
|
9
|
+
* must start the workspace fresh, and a Boat wake boots the deployment's image.
|
|
10
|
+
*
|
|
11
|
+
* Raise it, and the workspace-runtime version with it in the same commit, for
|
|
12
|
+
* a runtime security fix or for a control-plane or relay contract an older
|
|
13
|
+
* runtime cannot speak, and name the first cloud image published at the new
|
|
14
|
+
* version: the version-skew suite (`bun run --cwd packages/harness flows:skew`)
|
|
15
|
+
* runs the current control plane and relay against exactly that image.
|
|
16
|
+
* Bumping the version with it keeps the comparison honest: every image below
|
|
17
|
+
* the new version was built before the change. What it does not cover: the
|
|
18
|
+
* images built after the floor's at the same version are trusted, not tested.
|
|
9
19
|
*/
|
|
10
|
-
export declare const
|
|
20
|
+
export declare const SERVED_WORKSPACE_RUNTIME_FLOOR: {
|
|
21
|
+
readonly version: "0.10.1";
|
|
22
|
+
readonly image: "ghcr.io/kyashrathore/claxedo-sandbox:workspace-runtime-0-10-1-40706b7356-agent-plugins-v8";
|
|
23
|
+
};
|
|
24
|
+
export declare const MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION: "0.10.1";
|
package/dist/runtime-version.mjs
CHANGED
|
@@ -3,8 +3,13 @@ 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
|
|
6
|
+
var SERVED_WORKSPACE_RUNTIME_FLOOR = {
|
|
7
|
+
version: "0.10.1",
|
|
8
|
+
image: "ghcr.io/kyashrathore/claxedo-sandbox:workspace-runtime-0-10-1-40706b7356-agent-plugins-v8"
|
|
9
|
+
};
|
|
10
|
+
var MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION = SERVED_WORKSPACE_RUNTIME_FLOOR.version;
|
|
7
11
|
export {
|
|
8
|
-
|
|
12
|
+
MINIMUM_SERVED_WORKSPACE_RUNTIME_VERSION,
|
|
13
|
+
SERVED_WORKSPACE_RUNTIME_FLOOR,
|
|
9
14
|
workspaceRuntimeVersion
|
|
10
15
|
};
|
|
@@ -16,14 +16,16 @@ export declare function createSandboxStartRecorder(options: {
|
|
|
16
16
|
* lease as it ends, so a poller reading the lease mid-boot sees how far the
|
|
17
17
|
* start has come. `opened` writes the epoch's `lease_decision` before the
|
|
18
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
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* row. A serving lease is not starting, unless the step is a person's
|
|
20
|
+
* update, which is a start of its own on the same epoch. An epoch acquired
|
|
21
|
+
* with a start it continues has that start and no phase yet; one acquired
|
|
22
|
+
* without begins its own here.
|
|
22
23
|
*/
|
|
23
24
|
observe(lease: SandboxLease, input: {
|
|
24
25
|
bootMode: SandboxBootMode;
|
|
25
26
|
enteredAt: number;
|
|
26
27
|
labels: Record<string, string>;
|
|
28
|
+
update?: true;
|
|
27
29
|
}): {
|
|
28
30
|
opened: () => Promise<void>;
|
|
29
31
|
end: (phase: SandboxStartPhase) => Promise<void>;
|
package/package.json
CHANGED
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"access": "public"
|
|
164
164
|
},
|
|
165
165
|
"name": "@claxedo/sandbox-manager",
|
|
166
|
-
"version": "0.10.
|
|
166
|
+
"version": "0.10.2",
|
|
167
167
|
"scripts": {
|
|
168
168
|
"build": "bun scripts/build.ts",
|
|
169
169
|
"prepublishOnly": "npm run build",
|
|
@@ -172,8 +172,8 @@
|
|
|
172
172
|
"test": "mkdir -p .artifacts/unit && node ../../script/test-deadline.mjs bun test src --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml"
|
|
173
173
|
},
|
|
174
174
|
"dependencies": {
|
|
175
|
-
"@claxedo/helpers": "0.2.
|
|
176
|
-
"@claxedo/sandbox-contract": "0.10.
|
|
175
|
+
"@claxedo/helpers": "0.2.2",
|
|
176
|
+
"@claxedo/sandbox-contract": "0.10.2",
|
|
177
177
|
"@vercel/sandbox": "3.3.0",
|
|
178
178
|
"modal": "0.10.1"
|
|
179
179
|
},
|