@claxedo/sandbox-manager 0.5.1

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/command.d.ts +3 -0
  4. package/dist/command.mjs +18 -0
  5. package/dist/constants.d.ts +1 -0
  6. package/dist/constants.mjs +7 -0
  7. package/dist/daytona-allow-list.d.ts +5 -0
  8. package/dist/daytona-allow-list.mjs +9 -0
  9. package/dist/defaults.d.ts +3 -0
  10. package/dist/defaults.mjs +14 -0
  11. package/dist/driver-catalog.d.ts +62 -0
  12. package/dist/driver-catalog.mjs +206 -0
  13. package/dist/drivers/cloudflare-egress.d.ts +40 -0
  14. package/dist/drivers/cloudflare-egress.mjs +115 -0
  15. package/dist/drivers/cloudflare.d.ts +37 -0
  16. package/dist/drivers/cloudflare.mjs +151 -0
  17. package/dist/drivers/daytona.d.ts +94 -0
  18. package/dist/drivers/daytona.mjs +235 -0
  19. package/dist/drivers/docker.d.ts +36 -0
  20. package/dist/drivers/docker.mjs +329 -0
  21. package/dist/drivers/fetch-bridge.d.ts +10 -0
  22. package/dist/drivers/fetch-bridge.mjs +104 -0
  23. package/dist/drivers/modal.d.ts +68 -0
  24. package/dist/drivers/modal.mjs +227 -0
  25. package/dist/drivers/vercel.d.ts +109 -0
  26. package/dist/drivers/vercel.mjs +297 -0
  27. package/dist/image-name.d.ts +7 -0
  28. package/dist/image.d.ts +8 -0
  29. package/dist/image.mjs +124 -0
  30. package/dist/index.d.ts +316 -0
  31. package/dist/index.mjs +330 -0
  32. package/dist/lease-policy.d.ts +49 -0
  33. package/dist/lease-policy.mjs +126 -0
  34. package/dist/lease-types.d.ts +89 -0
  35. package/dist/lease-types.mjs +1 -0
  36. package/dist/runtime-env.d.ts +24 -0
  37. package/dist/runtime-env.mjs +35 -0
  38. package/dist/runtime-version.d.ts +1 -0
  39. package/dist/runtime-version.mjs +24 -0
  40. package/dist/service-url-exposure.d.ts +10 -0
  41. package/dist/service-url-exposure.mjs +46 -0
  42. package/dist/stores/memory.d.ts +6 -0
  43. package/dist/stores/memory.mjs +117 -0
  44. package/package.json +178 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,330 @@
1
+ import { createRequire as __createRequire } from 'module';var require = __createRequire(import.meta.url);
2
+
3
+ // src/constants.ts
4
+ var DEFAULT_WORKSPACE_RUNTIME_PORT = 2593;
5
+
6
+ // src/index.ts
7
+ function applySandboxLeasePatch(current, patch, updatedAt) {
8
+ return {
9
+ ...current,
10
+ ...patch.status === void 0 ? {} : { status: patch.status },
11
+ ...patch.sandboxId === void 0 ? {} : { sandboxId: patch.sandboxId },
12
+ ...patch.url === void 0 ? {} : { url: patch.url },
13
+ ...patch.hostId === void 0 ? {} : { hostId: patch.hostId },
14
+ ...patch.driverResourceId === void 0 ? {} : { driverResourceId: patch.driverResourceId },
15
+ ...patch.retryCount === void 0 ? {} : { retryCount: patch.retryCount },
16
+ ...patch.lastHeartbeatAt === void 0 ? {} : { lastHeartbeatAt: patch.lastHeartbeatAt },
17
+ ...patch.lastActivityAt === void 0 ? {} : { lastActivityAt: patch.lastActivityAt },
18
+ ...patch.labels === void 0 ? {} : { labels: patch.labels },
19
+ ...patch.nextRetryAt === void 0 ? {} : { nextRetryAt: patch.nextRetryAt ?? void 0 },
20
+ ...patch.lastError === void 0 ? {} : { lastError: patch.lastError ?? void 0 },
21
+ updatedAt
22
+ };
23
+ }
24
+ var DEFAULT_APP_LABEL = "claxedo";
25
+ function sandboxId(driver, workspaceId) {
26
+ return `${driver}-${workspaceId}`;
27
+ }
28
+ function ensureHostInput(input) {
29
+ const labels = {
30
+ app: input.appLabel,
31
+ workspaceId: input.workspaceId,
32
+ epoch: String(input.lease.epoch),
33
+ homeRegion: input.homeRegion,
34
+ ...input.managerInput?.labels
35
+ };
36
+ return {
37
+ workspaceId: input.workspaceId,
38
+ hostId: input.managerInput?.hostId ?? input.lease.hostId ?? sandboxId(input.driver.id, input.workspaceId),
39
+ homeRegion: input.homeRegion,
40
+ epoch: input.lease.epoch,
41
+ labels,
42
+ bootSource: input.managerInput?.bootSource ?? { kind: "default" },
43
+ workspaceRoot: input.managerInput?.workspaceRoot ?? "/workspace",
44
+ runtimeCwd: input.managerInput?.runtimeCwd,
45
+ workspaceRuntimePort: input.managerInput?.workspaceRuntimePort ?? DEFAULT_WORKSPACE_RUNTIME_PORT,
46
+ env: input.managerInput?.env ?? {},
47
+ // Brokered secrets ride their own channel — NEVER merged into labels or env.
48
+ ...input.managerInput?.secrets?.length ? { secrets: input.managerInput.secrets } : {},
49
+ source: input.managerInput?.source,
50
+ exposure: input.managerInput?.exposure ?? (input.driver.metadata.targetAccess === "loopback" ? { kind: "loopback" } : { kind: "relay" }),
51
+ net: input.managerInput?.net,
52
+ snapshot: input.managerInput?.snapshot
53
+ };
54
+ }
55
+ function createSandboxManager(options) {
56
+ const staleAfterMs = options.staleAfterMs ?? 6e4;
57
+ const retryAfterMs = options.retryAfterMs ?? 2e3;
58
+ const now = options.now ?? Date.now;
59
+ const retryDelayMs = options.retryDelayMs ?? ((retryCount) => Math.min(6e4, 1e3 * 2 ** Math.max(0, retryCount - 1)));
60
+ const maxRetryCount = options.maxRetryCount ?? Number.POSITIVE_INFINITY;
61
+ const retryCapCooldownMs = options.retryCapCooldownMs ?? 10 * 6e4;
62
+ async function leaseTarget(lease) {
63
+ if (lease.status !== "ready" || !lease.sandboxId || !lease.url || !lease.hostId) {
64
+ return { status: "unavailable", reason: "runtime_lease_not_ready" };
65
+ }
66
+ return {
67
+ status: "ready",
68
+ sandboxId: lease.sandboxId,
69
+ url: lease.url,
70
+ workspaceId: lease.workspaceId,
71
+ hostId: lease.hostId,
72
+ driverResourceId: lease.driverResourceId,
73
+ labels: lease.labels,
74
+ driver: {
75
+ id: lease.driver,
76
+ resourceId: lease.driverResourceId ?? lease.sandboxId
77
+ },
78
+ epoch: lease.epoch,
79
+ homeRegion: lease.homeRegion
80
+ };
81
+ }
82
+ async function provision(workspaceId, lease, homeRegion, managerInput) {
83
+ try {
84
+ const ensure = ensureHostInput({
85
+ driver: options.driver,
86
+ workspaceId,
87
+ homeRegion,
88
+ lease,
89
+ managerInput,
90
+ appLabel: options.appLabel ?? DEFAULT_APP_LABEL
91
+ });
92
+ if (ensure.secrets?.length && options.driver.metadata.secretBrokering === "none") {
93
+ return {
94
+ status: "unavailable",
95
+ error: "secret_brokering_unsupported",
96
+ epoch: lease.epoch,
97
+ homeRegion
98
+ };
99
+ }
100
+ const target = lease.sandboxId && ensure.bootSource?.kind === "default" && options.driver.resumeHost ? await options.driver.resumeHost({ lease, ensure }) : await options.driver.ensureHost(ensure);
101
+ if ("provisioning" in target) {
102
+ await options.leaseStore.update(workspaceId, lease.epoch, {
103
+ status: "acquiring",
104
+ nextRetryAt: now() + target.retryAfterMs
105
+ });
106
+ return {
107
+ status: "provisioning",
108
+ retryAfterMs: target.retryAfterMs,
109
+ epoch: lease.epoch,
110
+ homeRegion
111
+ };
112
+ }
113
+ const updated = await options.leaseStore.update(workspaceId, lease.epoch, {
114
+ status: "ready",
115
+ sandboxId: target.sandboxId,
116
+ url: target.url,
117
+ hostId: target.hostId,
118
+ driverResourceId: target.driverResourceId,
119
+ retryCount: 0,
120
+ nextRetryAt: null,
121
+ lastError: null,
122
+ labels: target.labels ?? ensure.labels
123
+ });
124
+ const resolved = await leaseTarget(updated ?? lease);
125
+ if (resolved.status === "ready") return resolved;
126
+ return {
127
+ status: "unavailable",
128
+ error: resolved.reason,
129
+ epoch: lease.epoch,
130
+ homeRegion
131
+ };
132
+ } catch (err) {
133
+ const error = err instanceof Error ? err.message : String(err);
134
+ if (lease.status === "ready") {
135
+ const updated = await options.leaseStore.update(workspaceId, lease.epoch, { lastError: error });
136
+ const resolved = await leaseTarget(updated ?? lease);
137
+ if (resolved.status === "ready") return resolved;
138
+ }
139
+ const nextRetryCount = lease.retryCount + 1;
140
+ const retryCapped = nextRetryCount >= maxRetryCount;
141
+ const failed = await options.leaseStore.recordFailure(
142
+ workspaceId,
143
+ lease.epoch,
144
+ error,
145
+ now() + (retryCapped ? retryCapCooldownMs : retryDelayMs(nextRetryCount))
146
+ );
147
+ return {
148
+ status: "unavailable",
149
+ retryAfterMs: failed?.nextRetryAt ? Math.max(0, failed.nextRetryAt - now()) : retryAfterMs,
150
+ error,
151
+ epoch: lease.epoch,
152
+ homeRegion
153
+ };
154
+ }
155
+ }
156
+ return {
157
+ async ensure(workspaceId, input) {
158
+ const existing = await options.leaseStore.get(workspaceId);
159
+ if (existing?.nextRetryAt && existing.nextRetryAt > now()) {
160
+ if (existing.status === "acquiring") {
161
+ return {
162
+ status: "provisioning",
163
+ retryAfterMs: existing.nextRetryAt - now(),
164
+ epoch: existing.epoch,
165
+ homeRegion: existing.homeRegion
166
+ };
167
+ }
168
+ return {
169
+ status: "unavailable",
170
+ retryAfterMs: existing.nextRetryAt - now(),
171
+ error: existing.lastError,
172
+ epoch: existing.epoch,
173
+ homeRegion: existing.homeRegion
174
+ };
175
+ }
176
+ if (existing?.status === "unavailable" && existing.retryCount >= maxRetryCount && existing.nextRetryAt === void 0) {
177
+ return {
178
+ status: "unavailable",
179
+ error: existing.lastError ?? "runtime_retry_cap_exceeded",
180
+ epoch: existing.epoch,
181
+ homeRegion: existing.homeRegion
182
+ };
183
+ }
184
+ if (existing?.status === "ready") {
185
+ return provision(workspaceId, existing, existing.homeRegion, input);
186
+ }
187
+ if (existing?.status === "acquiring" && existing.nextRetryAt !== void 0 && now() - existing.updatedAt < staleAfterMs) {
188
+ return provision(workspaceId, existing, existing.homeRegion, input);
189
+ }
190
+ const acquired = await options.leaseStore.acquire(workspaceId, {
191
+ homeRegion: input.homeRegion,
192
+ driver: options.driver.id,
193
+ staleAfterMs,
194
+ now: now()
195
+ });
196
+ if (!acquired.acquired) {
197
+ return {
198
+ status: "provisioning",
199
+ retryAfterMs: acquired.retryAfterMs,
200
+ epoch: acquired.lease.epoch,
201
+ homeRegion: acquired.lease.homeRegion
202
+ };
203
+ }
204
+ return provision(workspaceId, acquired.lease, input.homeRegion, input);
205
+ },
206
+ async register(workspaceId, input) {
207
+ return await updateFromRuntimeSnapshot(workspaceId, input);
208
+ },
209
+ async heartbeat(workspaceId, input) {
210
+ return await updateFromRuntimeSnapshot(workspaceId, input);
211
+ },
212
+ async target(workspaceId) {
213
+ const lease = await options.leaseStore.get(workspaceId);
214
+ if (!lease) return { status: "unavailable", reason: "runtime_lease_missing" };
215
+ return leaseTarget(lease);
216
+ },
217
+ async touch(workspaceId) {
218
+ const target = await this.target(workspaceId);
219
+ if (target.status !== "ready") return { touched: false, status: "missing" };
220
+ await options.driver.touch?.(target);
221
+ return { touched: true, status: "ready" };
222
+ },
223
+ async snapshot(workspaceId) {
224
+ const target = await this.target(workspaceId);
225
+ if (target.status !== "ready") return { ok: false, reason: target.reason };
226
+ if (!options.driver.snapshot) return { ok: false, reason: "snapshot_unsupported" };
227
+ return { ok: true, ...await options.driver.snapshot(target) };
228
+ },
229
+ async stop(workspaceId) {
230
+ const target = await this.target(workspaceId);
231
+ if (target.status !== "ready") return { ok: false, reason: target.reason };
232
+ if (options.driver.metadata.hostStopBehavior !== "not-supported") {
233
+ await (options.driver.suspend ?? options.driver.stop)?.(target);
234
+ } else {
235
+ await options.driver.stop?.(target);
236
+ }
237
+ await options.leaseStore.update(workspaceId, target.epoch, { status: "stopped" });
238
+ return { ok: true, status: "stopped" };
239
+ },
240
+ async destroy(workspaceId) {
241
+ const target = await this.target(workspaceId);
242
+ if (target.status !== "ready") return { ok: false, reason: target.reason };
243
+ await options.driver.destroy?.(target);
244
+ await options.leaseStore.update(workspaceId, target.epoch, { status: "destroyed" });
245
+ return { ok: true, status: "destroyed" };
246
+ },
247
+ async release(workspaceId) {
248
+ const lease = await options.leaseStore.get(workspaceId);
249
+ if (!lease) return { released: false };
250
+ await options.leaseStore.release(workspaceId);
251
+ return { released: true };
252
+ },
253
+ async garbageCollect() {
254
+ if (!options.driver.list) {
255
+ return {
256
+ destroyed: [],
257
+ kept: [],
258
+ skipped: [],
259
+ failed: []
260
+ };
261
+ }
262
+ const leases = new Map((await options.leaseStore.list()).map((lease) => [lease.workspaceId, lease]));
263
+ const result = {
264
+ destroyed: [],
265
+ kept: [],
266
+ skipped: [],
267
+ failed: []
268
+ };
269
+ const appLabel = options.appLabel ?? DEFAULT_APP_LABEL;
270
+ for (const target of await options.driver.list()) {
271
+ if (target.labels?.app !== appLabel) {
272
+ result.skipped.push({ target, reason: "unmanaged_app_label" });
273
+ continue;
274
+ }
275
+ const workspaceId = target.labels.workspaceId;
276
+ const epoch = target.labels.epoch;
277
+ if (!workspaceId || !epoch) {
278
+ result.skipped.push({ target, reason: "missing_runtime_labels" });
279
+ continue;
280
+ }
281
+ const lease = leases.get(workspaceId);
282
+ if (lease?.status === "ready" && String(lease.epoch) === epoch && lease.hostId === target.hostId && lease.sandboxId === target.sandboxId) {
283
+ result.kept.push(target);
284
+ continue;
285
+ }
286
+ if (lease?.status === "acquiring" && String(lease.epoch) === epoch) {
287
+ result.kept.push(target);
288
+ continue;
289
+ }
290
+ if (!options.driver.destroy) {
291
+ result.skipped.push({ target, reason: "destroy_unsupported" });
292
+ continue;
293
+ }
294
+ try {
295
+ await options.driver.destroy(target);
296
+ result.destroyed.push(target);
297
+ } catch (err) {
298
+ result.failed.push({ target, error: err instanceof Error ? err.message : String(err) });
299
+ }
300
+ }
301
+ return result;
302
+ },
303
+ list: () => options.leaseStore.list()
304
+ };
305
+ async function updateFromRuntimeSnapshot(workspaceId, input) {
306
+ const current = await options.leaseStore.get(workspaceId);
307
+ if (!current) return { ok: false, reason: "runtime_lease_missing" };
308
+ const expectedEpoch = input.epoch ?? current.epoch;
309
+ if (current.epoch !== expectedEpoch) return { ok: false, reason: "runtime_lease_epoch_mismatch" };
310
+ const timestamp = input.now ?? now();
311
+ const status = input.status === "unhealthy" || input.ok === false ? "unavailable" : input.ok ? "ready" : current.status;
312
+ const updated = await options.leaseStore.update(workspaceId, expectedEpoch, {
313
+ status,
314
+ ...input.sandboxId ? { sandboxId: input.sandboxId } : {},
315
+ ...input.url ? { url: input.url } : {},
316
+ ...input.hostId ? { hostId: input.hostId } : input.sandboxId ? { hostId: input.sandboxId } : {},
317
+ ...input.driverResourceId ? { driverResourceId: input.driverResourceId } : input.sandboxId ? { driverResourceId: input.sandboxId } : {},
318
+ lastHeartbeatAt: timestamp,
319
+ ...input.active ? { lastActivityAt: timestamp } : {},
320
+ ...input.ok ? { lastError: null } : {}
321
+ });
322
+ if (!updated) return { ok: false, reason: "runtime_lease_epoch_mismatch" };
323
+ return { ok: true, status: updated.status };
324
+ }
325
+ }
326
+ export {
327
+ DEFAULT_WORKSPACE_RUNTIME_PORT,
328
+ applySandboxLeasePatch,
329
+ createSandboxManager
330
+ };
@@ -0,0 +1,49 @@
1
+ import type { SandboxLeaseRow } from "./lease-types";
2
+ import type { SandboxDriverID } from "./driver-catalog";
3
+ export type SandboxDecision = {
4
+ action: "skip";
5
+ reason: string;
6
+ } | {
7
+ action: "wait";
8
+ until: number;
9
+ reason: string;
10
+ } | {
11
+ action: "resume";
12
+ reason: string;
13
+ } | {
14
+ action: "restore_snapshot";
15
+ snapshot_id: string;
16
+ reason: string;
17
+ } | {
18
+ action: "start_prepared";
19
+ image_id: string;
20
+ reason: string;
21
+ } | {
22
+ action: "start_fresh";
23
+ reason: string;
24
+ } | {
25
+ action: "stop_idle";
26
+ reason: string;
27
+ } | {
28
+ action: "mark_failed";
29
+ reason: string;
30
+ };
31
+ export type SandboxDecisionConfig = {
32
+ maxRetries: number;
33
+ idleMs: number;
34
+ backoffMaxMs: number;
35
+ healthTimeoutMs: number;
36
+ };
37
+ export type SandboxDriverPlacement = {
38
+ canPauseAndRestartSameResource: boolean;
39
+ canCreateFilesystemSnapshot: boolean;
40
+ canStartFromPreparedImage: boolean;
41
+ canStopExplicitly: boolean;
42
+ hasHealthProbe: boolean;
43
+ };
44
+ export declare const DEFAULT_WORKSPACE_HOST_DECISION_CONFIG: SandboxDecisionConfig;
45
+ export declare function sandboxDriverPlacement(driver: SandboxDriverID): SandboxDriverPlacement;
46
+ export declare function decideSandboxStart(lease: SandboxLeaseRow, placement: SandboxDriverPlacement, now: number, config?: SandboxDecisionConfig): SandboxDecision;
47
+ export declare function decideSandboxHealthFailure(lease: SandboxLeaseRow, _placement: SandboxDriverPlacement, now: number, config?: SandboxDecisionConfig): SandboxDecision;
48
+ export declare function decideSandboxIdle(lease: SandboxLeaseRow, activeHoldCount: number, now: number, config?: SandboxDecisionConfig): SandboxDecision;
49
+ export declare function nextSandboxRetryAt(retryCount: number, now: number, config?: SandboxDecisionConfig): number | null;
@@ -0,0 +1,126 @@
1
+ import { createRequire as __createRequire } from 'module';var require = __createRequire(import.meta.url);
2
+
3
+ // src/lease-policy.ts
4
+ var DEFAULT_WORKSPACE_HOST_DECISION_CONFIG = {
5
+ maxRetries: 8,
6
+ idleMs: 10 * 60 * 1e3,
7
+ backoffMaxMs: 3e4,
8
+ healthTimeoutMs: 6e4
9
+ };
10
+ var DRIVER_PLACEMENT = {
11
+ daytona: {
12
+ canPauseAndRestartSameResource: true,
13
+ canCreateFilesystemSnapshot: false,
14
+ canStartFromPreparedImage: true,
15
+ canStopExplicitly: true,
16
+ hasHealthProbe: true
17
+ },
18
+ modal: {
19
+ canPauseAndRestartSameResource: false,
20
+ canCreateFilesystemSnapshot: true,
21
+ canStartFromPreparedImage: true,
22
+ canStopExplicitly: true,
23
+ hasHealthProbe: true
24
+ },
25
+ vercel: {
26
+ canPauseAndRestartSameResource: false,
27
+ canCreateFilesystemSnapshot: true,
28
+ canStartFromPreparedImage: false,
29
+ canStopExplicitly: true,
30
+ hasHealthProbe: true
31
+ },
32
+ cloudflare: {
33
+ canPauseAndRestartSameResource: false,
34
+ canCreateFilesystemSnapshot: true,
35
+ canStartFromPreparedImage: false,
36
+ canStopExplicitly: false,
37
+ hasHealthProbe: true
38
+ },
39
+ docker: {
40
+ canPauseAndRestartSameResource: true,
41
+ canCreateFilesystemSnapshot: false,
42
+ canStartFromPreparedImage: true,
43
+ canStopExplicitly: true,
44
+ hasHealthProbe: true
45
+ }
46
+ };
47
+ function sandboxDriverPlacement(driver) {
48
+ return DRIVER_PLACEMENT[driver];
49
+ }
50
+ function decideSandboxStart(lease, placement, now, config = DEFAULT_WORKSPACE_HOST_DECISION_CONFIG) {
51
+ if (lease.status === "ready") {
52
+ return { action: "skip", reason: "already ready" };
53
+ }
54
+ if (lease.status === "failed") {
55
+ return { action: "mark_failed", reason: `terminal failure: ${lease.last_error ?? "unknown"}` };
56
+ }
57
+ if (lease.status === "backoff") {
58
+ if (lease.retry_count >= config.maxRetries) {
59
+ return { action: "mark_failed", reason: `exceeded max retries (${config.maxRetries})` };
60
+ }
61
+ if (lease.next_retry_at && now < lease.next_retry_at) {
62
+ return { action: "wait", until: lease.next_retry_at, reason: "backoff timer not elapsed" };
63
+ }
64
+ }
65
+ if (lease.status === "acquiring" || lease.status === "starting") {
66
+ return { action: "skip", reason: `already ${lease.status}` };
67
+ }
68
+ return decideStartMethod(lease, placement);
69
+ }
70
+ function decideSandboxHealthFailure(lease, _placement, now, config = DEFAULT_WORKSPACE_HOST_DECISION_CONFIG) {
71
+ if (lease.status !== "ready" && lease.status !== "unhealthy") {
72
+ return { action: "skip", reason: `status is ${lease.status}, health check not applicable` };
73
+ }
74
+ const retryCount = lease.retry_count + 1;
75
+ if (retryCount > config.maxRetries) {
76
+ return { action: "mark_failed", reason: `health failures exceeded max retries (${config.maxRetries})` };
77
+ }
78
+ const backoff = Math.min(config.backoffMaxMs, 1e3 * 2 ** Math.max(0, retryCount - 1));
79
+ return { action: "wait", until: now + backoff, reason: `health failure #${retryCount}, backoff ${backoff}ms` };
80
+ }
81
+ function decideSandboxIdle(lease, activeHoldCount, now, config = DEFAULT_WORKSPACE_HOST_DECISION_CONFIG) {
82
+ if (lease.status !== "ready") {
83
+ return { action: "skip", reason: `status is ${lease.status}, not ready` };
84
+ }
85
+ if (activeHoldCount > 0) {
86
+ return { action: "skip", reason: `${activeHoldCount} active hold(s)` };
87
+ }
88
+ const lastActivity = lease.last_activity_at ?? lease.created_at;
89
+ const idleFor = now - lastActivity;
90
+ if (idleFor < config.idleMs) {
91
+ return { action: "wait", until: lastActivity + config.idleMs, reason: `idle for ${idleFor}ms, need ${config.idleMs}ms` };
92
+ }
93
+ return { action: "stop_idle", reason: `idle for ${idleFor}ms (threshold: ${config.idleMs}ms)` };
94
+ }
95
+ function nextSandboxRetryAt(retryCount, now, config = DEFAULT_WORKSPACE_HOST_DECISION_CONFIG) {
96
+ if (retryCount >= config.maxRetries) return null;
97
+ return now + Math.min(config.backoffMaxMs, 1e3 * 2 ** Math.max(0, retryCount));
98
+ }
99
+ function decideStartMethod(lease, placement) {
100
+ if (lease.sandbox_id && placement.canPauseAndRestartSameResource) {
101
+ return { action: "resume", reason: `resume sandbox ${lease.sandbox_id}` };
102
+ }
103
+ if (lease.accel_snapshot_id && placement.canCreateFilesystemSnapshot) {
104
+ return {
105
+ action: "restore_snapshot",
106
+ snapshot_id: lease.accel_snapshot_id,
107
+ reason: `restore snapshot ${lease.accel_snapshot_id}`
108
+ };
109
+ }
110
+ if (lease.accel_prepared_image_id && placement.canStartFromPreparedImage) {
111
+ return {
112
+ action: "start_prepared",
113
+ image_id: lease.accel_prepared_image_id,
114
+ reason: `start from prepared image ${lease.accel_prepared_image_id}`
115
+ };
116
+ }
117
+ return { action: "start_fresh", reason: "no acceleration available, cold start" };
118
+ }
119
+ export {
120
+ DEFAULT_WORKSPACE_HOST_DECISION_CONFIG,
121
+ decideSandboxHealthFailure,
122
+ decideSandboxIdle,
123
+ decideSandboxStart,
124
+ nextSandboxRetryAt,
125
+ sandboxDriverPlacement
126
+ };
@@ -0,0 +1,89 @@
1
+ import type { SandboxDriverID } from "./driver-catalog";
2
+ export type SandboxLeaseRowStatus = "pending" | "acquiring" | "starting" | "ready" | "unhealthy" | "backoff" | "stopping" | "stopped" | "failed";
3
+ export type SandboxComputeClass = "small" | "medium" | "large" | "gpu";
4
+ export type SandboxLeaseRow = {
5
+ workspace_id: string;
6
+ lease_id: string;
7
+ home_region?: string;
8
+ epoch: number;
9
+ status: SandboxLeaseRowStatus;
10
+ driver: SandboxDriverID;
11
+ driver_resource_id: string | null;
12
+ driver_snapshot_id: string | null;
13
+ sandbox_id: string | null;
14
+ url: string | null;
15
+ retry_count: number;
16
+ next_retry_at: number | null;
17
+ last_heartbeat_at: number | null;
18
+ last_activity_at: number | null;
19
+ last_health_failure_at: number | null;
20
+ last_error: string | null;
21
+ compute_class: SandboxComputeClass | null;
22
+ accel_base_image_id: string | null;
23
+ accel_prepared_image_id: string | null;
24
+ accel_snapshot_id: string | null;
25
+ created_at: number;
26
+ updated_at: number;
27
+ };
28
+ export type SandboxHoldRowOwnerType = "session" | "stream" | "pty" | "process" | "system";
29
+ export type SandboxHoldRow = {
30
+ hold_id: string;
31
+ workspace_id: string;
32
+ owner_type: SandboxHoldRowOwnerType;
33
+ owner_id: string;
34
+ reason: string;
35
+ expires_at: number | null;
36
+ updated_at: number;
37
+ };
38
+ export type SandboxEpochEnvelope = {
39
+ workspace_id: string;
40
+ lease_id: string;
41
+ epoch: number;
42
+ sandbox_id: string;
43
+ };
44
+ export type SandboxRowAcquireResult = {
45
+ lease: SandboxLeaseRow;
46
+ bumped_epoch: boolean;
47
+ };
48
+ export type SandboxRowHeartbeatResult = {
49
+ accepted: true;
50
+ } | {
51
+ accepted: false;
52
+ reason: "stale_epoch" | "unknown_workspace" | "wrong_lease";
53
+ };
54
+ export type SandboxRowReconnectResult = {
55
+ accepted: true;
56
+ lease: SandboxLeaseRow;
57
+ } | {
58
+ accepted: false;
59
+ reason: "stale_epoch" | "unknown_workspace" | "wrong_lease" | "wrong_sandbox";
60
+ };
61
+ export type SandboxRowEvent = {
62
+ type: "lease_acquired";
63
+ lease: SandboxLeaseRow;
64
+ } | {
65
+ type: "lease_updated";
66
+ lease: SandboxLeaseRow;
67
+ changed: (keyof SandboxLeaseRow)[];
68
+ } | {
69
+ type: "lease_released";
70
+ workspace_id: string;
71
+ reason: string;
72
+ } | {
73
+ type: "hold_acquired";
74
+ hold: SandboxHoldRow;
75
+ } | {
76
+ type: "hold_released";
77
+ hold_id: string;
78
+ workspace_id: string;
79
+ } | {
80
+ type: "epoch_bumped";
81
+ workspace_id: string;
82
+ old_epoch: number;
83
+ new_epoch: number;
84
+ } | {
85
+ type: "heartbeat_rejected";
86
+ workspace_id: string;
87
+ envelope: SandboxEpochEnvelope;
88
+ reason: string;
89
+ };
@@ -0,0 +1 @@
1
+ import { createRequire as __createRequire } from 'module';var require = __createRequire(import.meta.url);
@@ -0,0 +1,24 @@
1
+ import type { SandboxDriverID } from "./driver-catalog";
2
+ export declare function workspaceRuntimeTargetEnv(input: {
3
+ workspaceId: string;
4
+ hostId?: string;
5
+ directory: string;
6
+ port: number;
7
+ host?: string;
8
+ }): Record<string, string>;
9
+ export declare function workspaceRuntimeSourceEnv(input: {
10
+ source?: {
11
+ kind: "git";
12
+ repoUrl: string;
13
+ branch?: string;
14
+ } | {
15
+ kind: "empty";
16
+ };
17
+ }): Record<string, string>;
18
+ export declare function workspaceRuntimeServiceExposureEnv(input: {
19
+ driver: SandboxDriverID;
20
+ source: "driver-service-url";
21
+ access: "private" | "public" | "driver-authenticated" | "unknown";
22
+ fallbackAccess?: "private" | "public" | "driver-authenticated" | "unknown";
23
+ note?: string;
24
+ }): Record<string, string>;
@@ -0,0 +1,35 @@
1
+ import { createRequire as __createRequire } from 'module';var require = __createRequire(import.meta.url);
2
+
3
+ // src/runtime-env.ts
4
+ function workspaceRuntimeTargetEnv(input) {
5
+ return {
6
+ WORKSPACE_RUNTIME_WORKSPACE_ID: input.workspaceId,
7
+ ...input.hostId ? { WORKSPACE_RUNTIME_HOST_ID: input.hostId } : {},
8
+ WORKSPACE_RUNTIME_DIRECTORY: input.directory,
9
+ WORKSPACE_RUNTIME_PORT: String(input.port),
10
+ ...input.host ? { WORKSPACE_RUNTIME_HOST: input.host } : {}
11
+ };
12
+ }
13
+ function workspaceRuntimeSourceEnv(input) {
14
+ if (!input.source || input.source.kind === "empty") return { WORKSPACE_RUNTIME_SOURCE_KIND: "empty" };
15
+ return {
16
+ WORKSPACE_RUNTIME_SOURCE_KIND: "git",
17
+ WORKSPACE_RUNTIME_GIT_REPO_URL: input.source.repoUrl,
18
+ ...input.source.branch ? { WORKSPACE_RUNTIME_GIT_BRANCH: input.source.branch } : {}
19
+ };
20
+ }
21
+ function workspaceRuntimeServiceExposureEnv(input) {
22
+ return {
23
+ ...input.driver === "docker" ? { WORKSPACE_RUNTIME_HOST: "0.0.0.0" } : {},
24
+ WORKSPACE_RUNTIME_SERVICE_EXPOSURE_SOURCE: input.source,
25
+ WORKSPACE_RUNTIME_SERVICE_EXPOSURE_ACCESS: input.access,
26
+ WORKSPACE_RUNTIME_SERVICE_EXPOSURE_DRIVER: input.driver,
27
+ ...input.fallbackAccess ? { WORKSPACE_RUNTIME_SERVICE_EXPOSURE_FALLBACK_ACCESS: input.fallbackAccess } : {},
28
+ ...input.note ? { WORKSPACE_RUNTIME_SERVICE_EXPOSURE_NOTE: input.note } : {}
29
+ };
30
+ }
31
+ export {
32
+ workspaceRuntimeServiceExposureEnv,
33
+ workspaceRuntimeSourceEnv,
34
+ workspaceRuntimeTargetEnv
35
+ };
@@ -0,0 +1 @@
1
+ export declare function workspaceRuntimeVersion(): string;
@@ -0,0 +1,24 @@
1
+ import { createRequire as __createRequire } from 'module';var require = __createRequire(import.meta.url);
2
+
3
+ // src/runtime-version.ts
4
+ import { createRequire } from "module";
5
+ var require2 = createRequire(import.meta.url);
6
+ var versionCache;
7
+ function workspaceRuntimeVersion() {
8
+ if (versionCache) return versionCache;
9
+ const envVersion = process.env.WORKSPACE_RUNTIME_VERSION;
10
+ if (envVersion) {
11
+ versionCache = envVersion;
12
+ return versionCache;
13
+ }
14
+ try {
15
+ versionCache = require2("@claxedo/workspace-runtime/package.json").version;
16
+ return versionCache;
17
+ } catch {
18
+ }
19
+ versionCache = "0.0.0";
20
+ return versionCache;
21
+ }
22
+ export {
23
+ workspaceRuntimeVersion
24
+ };