@giselles-ai/sandkit 0.1.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/adapters/drizzle.d.ts +83 -0
  4. package/dist/adapters/drizzle.js +9 -0
  5. package/dist/adapters/drizzle.js.map +1 -0
  6. package/dist/adapters/memory.d.ts +6 -0
  7. package/dist/adapters/memory.js +8 -0
  8. package/dist/adapters/memory.js.map +1 -0
  9. package/dist/adapters/sqlite-bun.d.ts +7 -0
  10. package/dist/adapters/sqlite-bun.js +8 -0
  11. package/dist/adapters/sqlite-bun.js.map +1 -0
  12. package/dist/bin.js +697 -0
  13. package/dist/bin.js.map +1 -0
  14. package/dist/chunk-7DLK7LOM.js +44 -0
  15. package/dist/chunk-7DLK7LOM.js.map +1 -0
  16. package/dist/chunk-BDPTYR6V.js +407 -0
  17. package/dist/chunk-BDPTYR6V.js.map +1 -0
  18. package/dist/chunk-CSOBTLWV.js +202 -0
  19. package/dist/chunk-CSOBTLWV.js.map +1 -0
  20. package/dist/chunk-DLGUA3H7.js +9 -0
  21. package/dist/chunk-DLGUA3H7.js.map +1 -0
  22. package/dist/chunk-FSDVHEEX.js +45 -0
  23. package/dist/chunk-FSDVHEEX.js.map +1 -0
  24. package/dist/chunk-HVYCAAZQ.js +25 -0
  25. package/dist/chunk-HVYCAAZQ.js.map +1 -0
  26. package/dist/chunk-LC3IYBAL.js +100 -0
  27. package/dist/chunk-LC3IYBAL.js.map +1 -0
  28. package/dist/chunk-REGOUXVI.js +58 -0
  29. package/dist/chunk-REGOUXVI.js.map +1 -0
  30. package/dist/chunk-RMMOQD5Y.js +211 -0
  31. package/dist/chunk-RMMOQD5Y.js.map +1 -0
  32. package/dist/chunk-UDFWES6J.js +486 -0
  33. package/dist/chunk-UDFWES6J.js.map +1 -0
  34. package/dist/chunk-VISDS5T7.js +202 -0
  35. package/dist/chunk-VISDS5T7.js.map +1 -0
  36. package/dist/chunk-XM4HGRXW.js +37 -0
  37. package/dist/chunk-XM4HGRXW.js.map +1 -0
  38. package/dist/cli/index.d.ts +19 -0
  39. package/dist/cli/index.js +397 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/index.d.ts +78 -0
  42. package/dist/index.js +1102 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/integrations/mock.d.ts +19 -0
  45. package/dist/integrations/mock.js +207 -0
  46. package/dist/integrations/mock.js.map +1 -0
  47. package/dist/integrations/vercel.d.ts +7 -0
  48. package/dist/integrations/vercel.js +400 -0
  49. package/dist/integrations/vercel.js.map +1 -0
  50. package/dist/policies/ai-gateway.d.ts +15 -0
  51. package/dist/policies/ai-gateway.js +12 -0
  52. package/dist/policies/ai-gateway.js.map +1 -0
  53. package/dist/policies/codex.d.ts +10 -0
  54. package/dist/policies/codex.js +12 -0
  55. package/dist/policies/codex.js.map +1 -0
  56. package/dist/policies/gemini.d.ts +10 -0
  57. package/dist/policies/gemini.js +12 -0
  58. package/dist/policies/gemini.js.map +1 -0
  59. package/dist/schema/index.d.ts +60 -0
  60. package/dist/schema/index.js +31 -0
  61. package/dist/schema/index.js.map +1 -0
  62. package/dist/types-BCgprbo8.d.ts +47 -0
  63. package/dist/types-BEKQnjeb.d.ts +139 -0
  64. package/dist/types-Cy36bS1j.d.ts +138 -0
  65. package/package.json +126 -0
package/dist/index.js ADDED
@@ -0,0 +1,1102 @@
1
+ import {
2
+ github
3
+ } from "./chunk-XM4HGRXW.js";
4
+ import "./chunk-UDFWES6J.js";
5
+ import "./chunk-BDPTYR6V.js";
6
+ import "./chunk-RMMOQD5Y.js";
7
+ import {
8
+ createMemoryAdapter
9
+ } from "./chunk-CSOBTLWV.js";
10
+ import {
11
+ codex
12
+ } from "./chunk-FSDVHEEX.js";
13
+ import {
14
+ gemini
15
+ } from "./chunk-7DLK7LOM.js";
16
+ import {
17
+ aiGateway
18
+ } from "./chunk-REGOUXVI.js";
19
+ import {
20
+ getSandboxDriverFactory
21
+ } from "./chunk-HVYCAAZQ.js";
22
+ import "./chunk-DLGUA3H7.js";
23
+ import {
24
+ allowAll,
25
+ allowService,
26
+ allowServices,
27
+ assertWorkspacePolicyIsDurable,
28
+ denyAll,
29
+ describeWorkspacePolicy,
30
+ parseWorkspacePolicy,
31
+ redactWorkspacePolicy,
32
+ serializeWorkspacePolicy
33
+ } from "./chunk-VISDS5T7.js";
34
+
35
+ // src/core/context.ts
36
+ function resolveDeprecatedNetworkOption(options) {
37
+ if (options.network !== void 0) {
38
+ throw new Error(
39
+ "SandkitOptions.network has been removed. Use createWorkspace({ policy }), workspace.setPolicy(...), or per-run policy overrides instead."
40
+ );
41
+ }
42
+ }
43
+ function createSandkitContext(options) {
44
+ if (!options) {
45
+ throw new Error("SandkitOptions is required. Set sandbox to a Sandkit sandbox provider.");
46
+ }
47
+ resolveDeprecatedNetworkOption(options);
48
+ if (!options.sandbox) {
49
+ throw new Error(
50
+ "SandkitOptions.sandbox is required. Set it to a Sandkit sandbox provider (for example, vercelSandbox(...))."
51
+ );
52
+ }
53
+ return {
54
+ adapter: options.database ?? createMemoryAdapter(),
55
+ driverFactory: getSandboxDriverFactory(options.sandbox),
56
+ options
57
+ };
58
+ }
59
+
60
+ // src/core/workspace-policy.ts
61
+ var WORKSPACE_POLICY_METADATA_KEY = "sandkit:policy";
62
+ function corruptionError(reason) {
63
+ return new Error(`Sandkit durable state corruption in sandkit_workspaces.metadata: ${reason}`);
64
+ }
65
+ function readWorkspacePolicy(workspace, fallback = allowAll()) {
66
+ const value = workspace.metadata?.[WORKSPACE_POLICY_METADATA_KEY];
67
+ if (value === void 0) {
68
+ return fallback;
69
+ }
70
+ try {
71
+ return parseWorkspacePolicy(value);
72
+ } catch (error) {
73
+ throw corruptionError(error instanceof Error ? error.message : "invalid workspace policy");
74
+ }
75
+ }
76
+ function asWorkspacePolicyMetadata(policy) {
77
+ assertWorkspacePolicyIsDurable(policy);
78
+ return {
79
+ [WORKSPACE_POLICY_METADATA_KEY]: serializeWorkspacePolicy(policy)
80
+ };
81
+ }
82
+ function removeWorkspacePolicyMetadata(metadata) {
83
+ if (!metadata || typeof metadata !== "object") {
84
+ return metadata;
85
+ }
86
+ if (!Object.prototype.hasOwnProperty.call(metadata, WORKSPACE_POLICY_METADATA_KEY)) {
87
+ return metadata;
88
+ }
89
+ const { [WORKSPACE_POLICY_METADATA_KEY]: _policyMetadata, ...rest } = metadata;
90
+ return rest;
91
+ }
92
+ function asWorkspacePolicyPatch(policy) {
93
+ return {
94
+ metadata: asWorkspacePolicyMetadata(policy)
95
+ };
96
+ }
97
+ function describeWorkspacePolicyId(policy) {
98
+ return describeWorkspacePolicy(policy);
99
+ }
100
+ function asPolicySnapshotConfig(policy) {
101
+ return redactWorkspacePolicy(policy);
102
+ }
103
+
104
+ // src/core/workspace-sandbox-config.ts
105
+ var WORKSPACE_SANDBOX_CONFIG_METADATA_KEY = "sandkit:sandbox-config";
106
+ function isFinitePort(value) {
107
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65535;
108
+ }
109
+ function normalizeExposedPorts(ports) {
110
+ if (ports === void 0) {
111
+ return [];
112
+ }
113
+ if (!Array.isArray(ports)) {
114
+ throw new Error("exposedPorts must be an array of positive integers in [1, 65535]");
115
+ }
116
+ const normalized = ports.map((value, index) => {
117
+ if (!isFinitePort(value)) {
118
+ throw new Error(`exposedPorts[${index}] must be a positive integer in [1, 65535]`);
119
+ }
120
+ return value;
121
+ });
122
+ return [...normalized];
123
+ }
124
+ function corruptionError2(reason) {
125
+ return new Error(`Sandkit durable state corruption in sandkit_workspaces.metadata: ${reason}`);
126
+ }
127
+ function readWorkspaceSandboxConfigValue(value) {
128
+ if (value === void 0) {
129
+ return {};
130
+ }
131
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
132
+ throw new Error("Expected sandbox config metadata object");
133
+ }
134
+ const candidate = value;
135
+ const ports = normalizeExposedPorts(candidate.exposedPorts);
136
+ return ports.length === 0 ? {} : { exposedPorts: ports };
137
+ }
138
+ function normalizeWorkspaceSandboxCreateConfig(config) {
139
+ if (config === void 0) {
140
+ return {};
141
+ }
142
+ return {
143
+ exposedPorts: normalizeExposedPorts(config.exposedPorts)
144
+ };
145
+ }
146
+ function asWorkspaceSandboxConfigMetadata(config) {
147
+ if (!config.exposedPorts || config.exposedPorts.length === 0) {
148
+ return {};
149
+ }
150
+ return {
151
+ [WORKSPACE_SANDBOX_CONFIG_METADATA_KEY]: {
152
+ exposedPorts: [...config.exposedPorts]
153
+ }
154
+ };
155
+ }
156
+ function removeWorkspaceSandboxConfigMetadata(metadata) {
157
+ if (!metadata || typeof metadata !== "object") {
158
+ return metadata;
159
+ }
160
+ if (!Object.prototype.hasOwnProperty.call(metadata, WORKSPACE_SANDBOX_CONFIG_METADATA_KEY)) {
161
+ return metadata;
162
+ }
163
+ const { [WORKSPACE_SANDBOX_CONFIG_METADATA_KEY]: _sandboxConfigMetadata, ...rest } = metadata;
164
+ return rest;
165
+ }
166
+ function readWorkspaceSandboxConfig(workspace) {
167
+ const metadataValue = workspace.metadata?.[WORKSPACE_SANDBOX_CONFIG_METADATA_KEY];
168
+ if (metadataValue === void 0) {
169
+ return {};
170
+ }
171
+ try {
172
+ return readWorkspaceSandboxConfigValue(metadataValue);
173
+ } catch (error) {
174
+ throw corruptionError2(
175
+ error instanceof Error ? error.message : "invalid workspace sandbox config"
176
+ );
177
+ }
178
+ }
179
+
180
+ // src/core/workspace-state.ts
181
+ var SANDBOX_METADATA_KEY = "sandkit:sandbox";
182
+ function isRecord(value) {
183
+ return typeof value === "object" && value !== null && !Array.isArray(value);
184
+ }
185
+ function isPersistedSandboxState(value) {
186
+ if (!isRecord(value)) {
187
+ return false;
188
+ }
189
+ if (typeof value.kind !== "string" || typeof value.sessionId !== "string") {
190
+ return false;
191
+ }
192
+ return value.state === void 0 || isJsonValue(value.state);
193
+ }
194
+ function isJsonValue(value) {
195
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
196
+ return true;
197
+ }
198
+ if (Array.isArray(value)) {
199
+ return value.every((item) => isJsonValue(item));
200
+ }
201
+ if (!isRecord(value)) {
202
+ return false;
203
+ }
204
+ return Object.values(value).every((item) => isJsonValue(item));
205
+ }
206
+ function isIsoTimestamp(value) {
207
+ return typeof value === "string" && !Number.isNaN(Date.parse(value));
208
+ }
209
+ function isSerializedSessionLease(value) {
210
+ if (!isRecord(value)) {
211
+ return false;
212
+ }
213
+ return isIsoTimestamp(value.observedAt) && isIsoTimestamp(value.expiresAt);
214
+ }
215
+ function readWorkspaceSessionPolicy(value) {
216
+ if (value === void 0) {
217
+ return void 0;
218
+ }
219
+ try {
220
+ return parseWorkspacePolicy(value);
221
+ } catch {
222
+ return void 0;
223
+ }
224
+ }
225
+ function toSnapshotCommit(snapshot) {
226
+ return { kind: "snapshot", state: snapshot };
227
+ }
228
+ function toSerializedWorkspaceState(state) {
229
+ switch (state.kind) {
230
+ case "cold":
231
+ return { kind: "cold" };
232
+ case "session":
233
+ return {
234
+ kind: "session",
235
+ sessionId: state.sandboxId,
236
+ lease: {
237
+ observedAt: state.lease.observedAt,
238
+ expiresAt: state.lease.expiresAt
239
+ },
240
+ policy: state.sessionPolicy === void 0 ? void 0 : serializeWorkspacePolicy(state.sessionPolicy)
241
+ };
242
+ case "snapshot":
243
+ return { kind: "snapshot", state: state.commit.state };
244
+ default:
245
+ return { kind: "cold" };
246
+ }
247
+ }
248
+ function makeSnapshotCommit(state) {
249
+ return toSnapshotCommit(state);
250
+ }
251
+ function asWorkspaceSandboxStateMetadata(state) {
252
+ return {
253
+ [SANDBOX_METADATA_KEY]: toSerializedWorkspaceState(state)
254
+ };
255
+ }
256
+ function readWorkspaceSandboxState(workspace) {
257
+ const value = workspace.metadata?.[SANDBOX_METADATA_KEY];
258
+ if (!isRecord(value)) {
259
+ return { kind: "cold" };
260
+ }
261
+ const raw = value;
262
+ if (raw.kind === "session" && typeof raw.sessionId === "string" && isSerializedSessionLease(raw.lease)) {
263
+ return {
264
+ kind: "session",
265
+ sandboxId: raw.sessionId,
266
+ lease: {
267
+ sandboxId: raw.sessionId,
268
+ observedAt: raw.lease.observedAt,
269
+ expiresAt: raw.lease.expiresAt
270
+ },
271
+ sessionPolicy: readWorkspaceSessionPolicy(raw.policy)
272
+ };
273
+ }
274
+ if ((raw.kind === "session" || raw.kind === "sandbox-session") && typeof raw.sessionId === "string") {
275
+ return { kind: "cold" };
276
+ }
277
+ if (raw.kind === "snapshot") {
278
+ const snapshot = isPersistedSandboxState(raw.state) ? raw.state : null;
279
+ if (!snapshot) {
280
+ return { kind: "cold" };
281
+ }
282
+ return {
283
+ kind: "snapshot",
284
+ commit: toSnapshotCommit(snapshot)
285
+ };
286
+ }
287
+ return { kind: "cold" };
288
+ }
289
+ function readWorkspaceSandboxLease(workspace) {
290
+ return workspaceSandboxLeaseFromState(readWorkspaceSandboxState(workspace));
291
+ }
292
+ function workspaceSandboxLeaseFromState(state) {
293
+ if (state.kind !== "session") {
294
+ return null;
295
+ }
296
+ const expiresAtMs = Date.parse(state.lease.expiresAt);
297
+ if (!Number.isFinite(expiresAtMs)) {
298
+ return null;
299
+ }
300
+ const remainingMs = Math.max(0, expiresAtMs - Date.now());
301
+ if (remainingMs <= 0) {
302
+ return null;
303
+ }
304
+ return {
305
+ sandboxId: state.sandboxId,
306
+ observedAt: state.lease.observedAt,
307
+ expiresAt: state.lease.expiresAt,
308
+ remainingMs
309
+ };
310
+ }
311
+ function isWorkspaceSessionStateExpired(state, nowMs = Date.now()) {
312
+ if (state.kind !== "session") {
313
+ return false;
314
+ }
315
+ return Date.parse(state.lease.expiresAt) <= nowMs;
316
+ }
317
+ function transitionToCold(at = (/* @__PURE__ */ new Date()).toISOString()) {
318
+ const nextState = { kind: "cold" };
319
+ return {
320
+ nextState,
321
+ patch: {
322
+ metadata: asWorkspaceSandboxStateMetadata(nextState),
323
+ sandboxId: null,
324
+ lastResumedAt: at
325
+ }
326
+ };
327
+ }
328
+ function transitionToSession(sandboxId, lease, sessionPolicy) {
329
+ const nextState = {
330
+ kind: "session",
331
+ sandboxId,
332
+ lease,
333
+ sessionPolicy
334
+ };
335
+ return {
336
+ nextState,
337
+ patch: {
338
+ metadata: asWorkspaceSandboxStateMetadata(nextState),
339
+ sandboxId,
340
+ lastResumedAt: lease.observedAt
341
+ }
342
+ };
343
+ }
344
+ function transitionAfterCommandCommit(commit, at) {
345
+ const nextState = {
346
+ kind: "snapshot",
347
+ commit
348
+ };
349
+ return {
350
+ nextState,
351
+ patch: {
352
+ metadata: asWorkspaceSandboxStateMetadata(nextState),
353
+ sandboxId: commit.state.sessionId,
354
+ lastResumedAt: at
355
+ }
356
+ };
357
+ }
358
+ function toDriverResumeState(state) {
359
+ if (state.kind === "snapshot") {
360
+ return state.commit.state;
361
+ }
362
+ if (state.kind === "session") {
363
+ return {
364
+ kind: "sandbox-session",
365
+ sessionId: state.sandboxId
366
+ };
367
+ }
368
+ return null;
369
+ }
370
+ async function persistSandboxTransition(store, workspaceId, transition) {
371
+ const record = await store.updateWorkspace(workspaceId, transition.patch);
372
+ return {
373
+ record,
374
+ state: transition.nextState
375
+ };
376
+ }
377
+
378
+ // src/core/sandbox.ts
379
+ var ManagedSandbox = class {
380
+ #driver;
381
+ #onCommit;
382
+ #runLifecycle;
383
+ #resolveDefaultPolicy;
384
+ constructor(driver, resolveDefaultPolicy, onCommit, runLifecycle) {
385
+ this.#driver = driver;
386
+ this.#resolveDefaultPolicy = resolveDefaultPolicy;
387
+ this.#onCommit = onCommit;
388
+ this.#runLifecycle = runLifecycle;
389
+ }
390
+ get id() {
391
+ return this.#driver.id;
392
+ }
393
+ async runCommand(inputOrCommand, args = []) {
394
+ const normalized = await this.normalizeRunCommandInput(inputOrCommand, args);
395
+ this.ensureCommandShape(normalized.command, normalized.args);
396
+ return this.runUnitOfWork(
397
+ normalized.command,
398
+ normalized.args,
399
+ normalized.policy,
400
+ () => this.executeCommand(normalized.command, normalized.args, normalized.policy)
401
+ );
402
+ }
403
+ async normalizeRunCommandInput(inputOrCommand, args) {
404
+ if (typeof inputOrCommand === "string") {
405
+ return {
406
+ command: inputOrCommand,
407
+ args,
408
+ policy: await this.#resolveDefaultPolicy()
409
+ };
410
+ }
411
+ return {
412
+ command: inputOrCommand.command,
413
+ args: inputOrCommand.args ?? [],
414
+ policy: inputOrCommand.policy ?? await this.#resolveDefaultPolicy()
415
+ };
416
+ }
417
+ ensureCommandShape(command, args) {
418
+ if (!command.trim()) {
419
+ throw new Error("Sandbox command must not be empty.");
420
+ }
421
+ if (!Array.isArray(args)) {
422
+ throw new Error("Sandbox command arguments must be an array.");
423
+ }
424
+ }
425
+ async runUnitOfWork(command, args, effectivePolicy, operation) {
426
+ const now = (/* @__PURE__ */ new Date()).toISOString();
427
+ const runId = await this.startRun(command, args, effectivePolicy, now);
428
+ let commandError;
429
+ let commandResult;
430
+ try {
431
+ commandResult = await operation();
432
+ } catch (error) {
433
+ commandError = error;
434
+ }
435
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
436
+ const commandStatus = commandError === void 0 ? commandResult?.exitCode === 0 ? "succeeded" : "failed" : "failed";
437
+ let providerCommit;
438
+ let finalizeError;
439
+ try {
440
+ const commit = await this.snapshotWithFallback();
441
+ providerCommit = commit.state;
442
+ if (this.#onCommit) {
443
+ await this.persistCommit(commit);
444
+ }
445
+ } catch (error) {
446
+ finalizeError = error;
447
+ providerCommit = void 0;
448
+ }
449
+ const finalRunStatus = commandError === void 0 && finalizeError === void 0 ? commandStatus : "failed";
450
+ try {
451
+ if (runId) {
452
+ await this.finishRun({
453
+ runId,
454
+ status: finalRunStatus,
455
+ providerCommit,
456
+ finishedAt,
457
+ exitCode: commandResult?.exitCode,
458
+ stdout: commandResult?.stdout,
459
+ stderr: commandResult?.stderr
460
+ });
461
+ }
462
+ } catch (error) {
463
+ finalizeError = finalizeError ?? error;
464
+ }
465
+ if (commandError !== void 0) {
466
+ if (finalizeError !== void 0) {
467
+ throw new AggregateError(
468
+ [commandError, finalizeError],
469
+ "Sandbox command failed and unit-of-work durability finalization did not complete."
470
+ );
471
+ }
472
+ throw commandError;
473
+ }
474
+ if (finalizeError !== void 0) {
475
+ throw finalizeError;
476
+ }
477
+ if (!commandResult) {
478
+ throw new Error("Command result was missing after successful execution.");
479
+ }
480
+ return commandResult;
481
+ }
482
+ async startRun(command, args, effectivePolicy, at) {
483
+ if (!this.#runLifecycle?.onRunStart) {
484
+ return void 0;
485
+ }
486
+ return this.#runLifecycle.onRunStart({
487
+ command,
488
+ args,
489
+ effectivePolicy,
490
+ startedAt: at
491
+ });
492
+ }
493
+ async executeCommand(command, args, policy) {
494
+ await this.#driver.applyPolicy(policy);
495
+ return this.#driver.runCommand(command, [...args]);
496
+ }
497
+ async finishRun(input) {
498
+ if (!this.#runLifecycle?.onRunFinish) {
499
+ return;
500
+ }
501
+ await this.#runLifecycle.onRunFinish(input);
502
+ }
503
+ async snapshotWithFallback() {
504
+ const snapshot = await this.#driver.snapshot();
505
+ return makeSnapshotCommit(snapshot);
506
+ }
507
+ async persistCommit(commit) {
508
+ if (!this.#onCommit) {
509
+ return;
510
+ }
511
+ await this.#onCommit(commit);
512
+ }
513
+ };
514
+ var ManagedSession = class {
515
+ #driver;
516
+ #onCommit;
517
+ #resolveDefaultPolicy;
518
+ #onPolicyChange;
519
+ #stateValidator;
520
+ #leaseLifecycle;
521
+ #sessionPolicyOverride;
522
+ #isActive = true;
523
+ constructor(driver, resolveDefaultPolicy, onCommit, stateValidator, leaseLifecycle, policyLifecycle) {
524
+ this.#driver = driver;
525
+ this.#resolveDefaultPolicy = resolveDefaultPolicy;
526
+ this.#onCommit = onCommit;
527
+ this.#stateValidator = stateValidator;
528
+ this.#leaseLifecycle = leaseLifecycle;
529
+ this.#onPolicyChange = policyLifecycle?.onPolicyChange;
530
+ this.#sessionPolicyOverride = policyLifecycle?.initialSessionPolicy;
531
+ }
532
+ get id() {
533
+ return this.#driver.id;
534
+ }
535
+ async exec(inputOrCommand, args = []) {
536
+ await this.assertSessionActive();
537
+ const normalized = await this.normalizeSessionInput(inputOrCommand, args);
538
+ this.ensureCommandShape(normalized.command, normalized.args);
539
+ await this.#driver.applyPolicy(normalized.policy);
540
+ return this.#driver.runCommand(normalized.command, [...normalized.args]);
541
+ }
542
+ async commit() {
543
+ await this.assertSessionActive();
544
+ let snapshot;
545
+ try {
546
+ snapshot = await this.snapshotWithFallback();
547
+ if (this.#onCommit) {
548
+ await this.persistCommit(snapshot);
549
+ }
550
+ } finally {
551
+ this.#isActive = false;
552
+ }
553
+ }
554
+ async setPolicy(policy) {
555
+ await this.assertSessionActive();
556
+ this.#sessionPolicyOverride = policy;
557
+ await this.#driver.applyPolicy(policy);
558
+ if (this.#onPolicyChange !== void 0) {
559
+ await this.#onPolicyChange(policy);
560
+ }
561
+ }
562
+ async startProcess(inputOrCommand, args = []) {
563
+ await this.assertSessionActive();
564
+ const startProcess = this.#driver.startProcess;
565
+ if (!startProcess) {
566
+ throw new Error(`This sandbox provider does not support startProcess().`);
567
+ }
568
+ const normalized = typeof inputOrCommand === "string" ? {
569
+ command: inputOrCommand,
570
+ args,
571
+ policy: this.#sessionPolicyOverride,
572
+ onStdout: void 0,
573
+ onStderr: void 0
574
+ } : inputOrCommand;
575
+ if (!normalized.command.trim()) {
576
+ throw new Error("Sandbox process command must not be empty.");
577
+ }
578
+ if (!Array.isArray(normalized.args)) {
579
+ throw new Error("Sandbox process args must be an array.");
580
+ }
581
+ const policy = await this.resolveSessionPolicy(normalized.policy);
582
+ await this.#driver.applyPolicy(policy);
583
+ return startProcess.call(this.#driver, {
584
+ command: normalized.command,
585
+ args: [...normalized.args],
586
+ policy,
587
+ onStdout: normalized.onStdout,
588
+ onStderr: normalized.onStderr
589
+ });
590
+ }
591
+ async url(port) {
592
+ await this.assertSessionActive();
593
+ const url = this.#driver.url;
594
+ if (!url) {
595
+ throw new Error(`This sandbox provider does not support url(port).`);
596
+ }
597
+ return url.call(this.#driver, port);
598
+ }
599
+ async extendTimeout(durationMs) {
600
+ await this.assertSessionActive();
601
+ const extendTimeout = this.#driver.extendTimeout;
602
+ if (!extendTimeout) {
603
+ throw new Error(`This sandbox provider does not support extendTimeout().`);
604
+ }
605
+ await extendTimeout.call(this.#driver, durationMs);
606
+ if (this.#leaseLifecycle?.onLeaseRefresh) {
607
+ await this.#leaseLifecycle.onLeaseRefresh();
608
+ }
609
+ }
610
+ async normalizeSessionInput(inputOrCommand, args) {
611
+ if (typeof inputOrCommand === "string") {
612
+ return {
613
+ command: inputOrCommand,
614
+ args,
615
+ policy: await this.resolveSessionPolicy()
616
+ };
617
+ }
618
+ return {
619
+ command: inputOrCommand.command,
620
+ args: inputOrCommand.args ?? [],
621
+ policy: await this.resolveSessionPolicy(inputOrCommand.policy)
622
+ };
623
+ }
624
+ async resolveSessionPolicy(override) {
625
+ if (override !== void 0) {
626
+ return override;
627
+ }
628
+ if (this.#sessionPolicyOverride !== void 0) {
629
+ return this.#sessionPolicyOverride;
630
+ }
631
+ return this.#resolveDefaultPolicy();
632
+ }
633
+ ensureCommandShape(command, args) {
634
+ if (!command.trim()) {
635
+ throw new Error("Sandbox command must not be empty.");
636
+ }
637
+ if (!Array.isArray(args)) {
638
+ throw new Error("Sandbox command arguments must be an array.");
639
+ }
640
+ }
641
+ async assertSessionActive() {
642
+ if (!this.#isActive) {
643
+ throw new Error("This sandbox session has already been committed and is no longer active.");
644
+ }
645
+ if (this.#stateValidator) {
646
+ await this.#stateValidator.assertActive();
647
+ }
648
+ }
649
+ async snapshotWithFallback() {
650
+ const snapshot = await this.#driver.snapshot();
651
+ return makeSnapshotCommit(snapshot);
652
+ }
653
+ async persistCommit(commit) {
654
+ if (!this.#onCommit) {
655
+ return;
656
+ }
657
+ await this.#onCommit(commit);
658
+ }
659
+ };
660
+ var LazySandboxHandle = class {
661
+ #resolveSandbox;
662
+ #openSession;
663
+ #attachSession;
664
+ #getActiveLease;
665
+ constructor(resolveSandbox, openSession, attachSession, getActiveLease) {
666
+ this.#resolveSandbox = resolveSandbox;
667
+ this.#openSession = openSession;
668
+ this.#attachSession = attachSession;
669
+ this.#getActiveLease = getActiveLease;
670
+ }
671
+ async runCommand(inputOrCommand, args = []) {
672
+ const sandbox = await this.#resolveSandbox();
673
+ if (typeof inputOrCommand === "string") {
674
+ return sandbox.runCommand(inputOrCommand, args);
675
+ }
676
+ return sandbox.runCommand(inputOrCommand);
677
+ }
678
+ async openSession(input) {
679
+ return this.#openSession(input);
680
+ }
681
+ async attachSession() {
682
+ return this.#attachSession();
683
+ }
684
+ async getActiveLease() {
685
+ return this.#getActiveLease();
686
+ }
687
+ };
688
+
689
+ // src/core/workspace.ts
690
+ function setupStateFingerprint(command, args, policy) {
691
+ return encodeURIComponent(
692
+ JSON.stringify({
693
+ command,
694
+ args,
695
+ policy: policy ? {
696
+ id: describeWorkspacePolicyId(policy),
697
+ config: asPolicySnapshotConfig(policy)
698
+ } : null
699
+ })
700
+ );
701
+ }
702
+ var sharedSetupStateId = (adapterId, setup) => {
703
+ if (setup?.policy) {
704
+ assertWorkspacePolicyIsDurable(setup.policy);
705
+ }
706
+ const fingerprint = setup ? setupStateFingerprint(setup.command, [...setup.args ?? []], setup.policy) : "no-bootstrap";
707
+ return `${adapterId}:shared-bootstrap:${fingerprint}`;
708
+ };
709
+ var WorkspaceHandle = class {
710
+ #ctx;
711
+ #record;
712
+ #sandboxState;
713
+ #sandboxConfig;
714
+ #descriptor;
715
+ #lazySandbox;
716
+ constructor(ctx, record) {
717
+ this.#ctx = ctx;
718
+ this.#record = record;
719
+ this.#descriptor = this.resolveDescriptor(record);
720
+ this.#sandboxState = readWorkspaceSandboxState(record);
721
+ this.#sandboxConfig = readWorkspaceSandboxConfig(record);
722
+ }
723
+ get id() {
724
+ return this.#record.id;
725
+ }
726
+ get descriptor() {
727
+ return this.#descriptor;
728
+ }
729
+ get sandbox() {
730
+ if (!this.#lazySandbox) {
731
+ this.#lazySandbox = new LazySandboxHandle(
732
+ () => this.createOrResumeSandboxForCommand(),
733
+ (input) => this.openSession(input),
734
+ () => this.attachSession(),
735
+ () => this.getActiveLease()
736
+ );
737
+ }
738
+ return this.#lazySandbox;
739
+ }
740
+ async setPolicy(policy) {
741
+ await this.resolveLatestWorkspace();
742
+ const result = await this.#ctx.adapter.workspaces.updateWorkspace(
743
+ this.#record.id,
744
+ asWorkspacePolicyPatch(policy)
745
+ );
746
+ this.updateFromRecord(result);
747
+ this.#sandboxState = readWorkspaceSandboxState(result);
748
+ }
749
+ /**
750
+ * Returns a current lease only if a session is still attachable and unexpired.
751
+ * This is attachment-state derived from persisted metadata; it is not intended
752
+ * to reset expiry on read.
753
+ */
754
+ async getActiveLease() {
755
+ await this.resolveLatestWorkspace();
756
+ const sandbox = await this.resolveAttachableSession();
757
+ if (!sandbox) {
758
+ return null;
759
+ }
760
+ return readWorkspaceSandboxLease(this.#record);
761
+ }
762
+ async createOrResumeSandboxForCommand() {
763
+ await this.resolveLatestWorkspace();
764
+ if (await this.resolveAttachableSession()) {
765
+ throw new Error(
766
+ "Cannot run command while a sandbox session is active. Use attachSession() to reuse it or commit the session first."
767
+ );
768
+ }
769
+ const workspace = await this.resolveLatestWorkspace();
770
+ const sandbox = await this.resolveSandboxDriver(workspace);
771
+ return this.createManagedSandbox(sandbox);
772
+ }
773
+ async openSession(input) {
774
+ await this.resolveLatestWorkspace();
775
+ if (await this.resolveAttachableSession()) {
776
+ throw new Error("A sandbox session is already active for this workspace.");
777
+ }
778
+ const workspace = await this.resolveLatestWorkspace();
779
+ const sandbox = await this.resolveSandboxDriver(workspace, {
780
+ timeoutMs: normalizeSessionTimeoutMs(input?.timeoutMs)
781
+ });
782
+ const lease = await sandbox.getSessionLease();
783
+ await this.persistSandboxState(transitionToSession(sandbox.id, lease));
784
+ return this.makeSession(sandbox);
785
+ }
786
+ async attachSession() {
787
+ await this.resolveLatestWorkspace();
788
+ const sandbox = await this.resolveAttachableSession();
789
+ if (!sandbox) {
790
+ throw new Error("There is no active sandbox session to attach for this workspace.");
791
+ }
792
+ return this.makeSession(sandbox);
793
+ }
794
+ async persistSessionPolicy(policy) {
795
+ await this.resolveLatestWorkspace();
796
+ if (!workspaceStateIsSession(this.#sandboxState)) {
797
+ throw new Error("Cannot persist session policy without an active sandbox session.");
798
+ }
799
+ await this.persistSandboxState(
800
+ transitionToSession(this.#sandboxState.sandboxId, this.#sandboxState.lease, policy)
801
+ );
802
+ }
803
+ makeSession(sandbox) {
804
+ const sessionPolicy = workspaceStateIsSession(this.#sandboxState) ? this.#sandboxState.sessionPolicy : void 0;
805
+ return new ManagedSession(
806
+ sandbox,
807
+ async () => this.resolveDefaultPolicy(),
808
+ async (commit) => this.persistSandboxState(transitionAfterCommandCommit(commit, (/* @__PURE__ */ new Date()).toISOString())),
809
+ {
810
+ assertActive: async () => {
811
+ const activeSandbox = await this.resolveAttachableSession();
812
+ if (!activeSandbox || activeSandbox.id !== sandbox.id) {
813
+ throw new Error("This sandbox session is no longer active.");
814
+ }
815
+ }
816
+ },
817
+ {
818
+ onLeaseRefresh: async () => {
819
+ const lease = await sandbox.getSessionLease();
820
+ await this.refreshSessionLease(sandbox.id, lease);
821
+ }
822
+ },
823
+ {
824
+ initialSessionPolicy: sessionPolicy,
825
+ onPolicyChange: async (policy) => {
826
+ await this.persistSessionPolicy(policy);
827
+ }
828
+ }
829
+ );
830
+ }
831
+ async refreshSessionLease(sandboxId, lease) {
832
+ await this.resolveLatestWorkspace();
833
+ if (!workspaceStateIsSession(this.#sandboxState) || this.#sandboxState.sandboxId !== sandboxId) {
834
+ throw new Error("Cannot refresh lease for an inactive sandbox session.");
835
+ }
836
+ await this.persistSandboxState(
837
+ transitionToSession(sandboxId, lease, this.#sandboxState.sessionPolicy)
838
+ );
839
+ }
840
+ async resolveAttachableSession() {
841
+ if (!workspaceStateIsSession(this.#sandboxState)) {
842
+ return null;
843
+ }
844
+ if (isWorkspaceSessionStateExpired(this.#sandboxState)) {
845
+ await this.persistSandboxState(transitionToCold());
846
+ return null;
847
+ }
848
+ try {
849
+ return await this.resolveSandboxDriver(this.#record);
850
+ } catch (error) {
851
+ if (!this.#ctx.driverFactory.isSessionUnavailableError?.(error)) {
852
+ throw error;
853
+ }
854
+ await this.persistSandboxState(transitionToCold());
855
+ return null;
856
+ }
857
+ }
858
+ async resolveSandboxDriver(workspace, overrides) {
859
+ const policy = readWorkspacePolicy(workspace, allowAll());
860
+ const options = this.makeSandboxDriverOptions(policy, overrides?.timeoutMs);
861
+ const currentState = toDriverResumeState(this.#sandboxState);
862
+ if (currentState) {
863
+ return this.#ctx.driverFactory.resumeSandbox(workspace, currentState, options);
864
+ }
865
+ try {
866
+ const setupState = await this.#readSharedSetupState();
867
+ if (!setupState) {
868
+ return this.#bootstrapSetupState(workspace, options);
869
+ }
870
+ return await this.#ctx.driverFactory.resumeSandbox(workspace, setupState, options);
871
+ } catch (error) {
872
+ if (!this.#ctx.driverFactory.isSessionUnavailableError?.(error) && !this.#isRecoverableSetupStateError(error)) {
873
+ throw error;
874
+ }
875
+ await this.#clearSharedSetupState();
876
+ return this.#bootstrapSetupState(workspace, options);
877
+ }
878
+ }
879
+ async #bootstrapSetupState(workspace, options) {
880
+ const setup = this.#ctx.options.setup;
881
+ if (!setup) {
882
+ return this.#ctx.driverFactory.createSandbox(workspace, options);
883
+ }
884
+ const setupPolicy = setup.policy ?? options.policy;
885
+ const sandbox = await this.#ctx.driverFactory.createSandbox(workspace, {
886
+ ...options,
887
+ policy: setupPolicy
888
+ });
889
+ const result = await sandbox.runCommand(setup.command, [...setup.args ?? []]);
890
+ if (result.exitCode !== 0) {
891
+ throw new Error(
892
+ `Workspace setup failed with exit code ${result.exitCode}. ${result.stderr.trim()}`.trim()
893
+ );
894
+ }
895
+ const setupState = await sandbox.snapshot();
896
+ await this.#persistSharedSetupState(setupState);
897
+ return this.#ctx.driverFactory.resumeSandbox(workspace, setupState, options);
898
+ }
899
+ async #readSharedSetupState() {
900
+ if (!this.#ctx.options.setup) {
901
+ return null;
902
+ }
903
+ const setupState = await this.#ctx.adapter.setupStates.getSetupState(
904
+ this.#sharedSetupStateId()
905
+ );
906
+ return setupState ? setupState.state : null;
907
+ }
908
+ async #persistSharedSetupState(state) {
909
+ await this.#ctx.adapter.setupStates.putSetupState({
910
+ id: this.#sharedSetupStateId(),
911
+ state: {
912
+ kind: state.kind,
913
+ sessionId: state.sessionId,
914
+ state: state.state
915
+ }
916
+ });
917
+ }
918
+ async #clearSharedSetupState() {
919
+ await this.#ctx.adapter.setupStates.deleteSetupState(this.#sharedSetupStateId());
920
+ }
921
+ #sharedSetupStateId() {
922
+ return sharedSetupStateId(this.#ctx.adapter.id, this.#ctx.options.setup);
923
+ }
924
+ #isRecoverableSetupStateError(error) {
925
+ if (!(error instanceof Error)) {
926
+ return false;
927
+ }
928
+ return error.message.includes("Sandkit durable state corruption") && error.message.includes("sandkit_setup_states");
929
+ }
930
+ createManagedSandbox(sandbox) {
931
+ return new ManagedSandbox(
932
+ sandbox,
933
+ async () => this.resolveDefaultPolicy(),
934
+ async (commit) => this.persistSandboxState(transitionAfterCommandCommit(commit, (/* @__PURE__ */ new Date()).toISOString())),
935
+ this.createRunLifecycle(sandbox)
936
+ );
937
+ }
938
+ makeSandboxDriverOptions(policy, timeoutMs) {
939
+ const next = {
940
+ policy
941
+ };
942
+ if (this.#sandboxConfig.exposedPorts && this.#sandboxConfig.exposedPorts.length > 0) {
943
+ next.exposedPorts = this.#sandboxConfig.exposedPorts;
944
+ }
945
+ if (timeoutMs !== void 0) {
946
+ next.timeoutMs = timeoutMs;
947
+ }
948
+ return next;
949
+ }
950
+ createRunLifecycle(sandbox) {
951
+ return {
952
+ onRunStart: async (input) => {
953
+ const policySnapshot = await this.createPolicySnapshot(input.effectivePolicy);
954
+ const run = await this.#ctx.adapter.runs.createRun({
955
+ workspaceId: this.#record.id,
956
+ provider: sandbox.provider,
957
+ executionTargetId: sandbox.id,
958
+ command: input.command,
959
+ args: input.args,
960
+ status: "started",
961
+ startedAt: input.startedAt,
962
+ policySnapshotId: policySnapshot.id
963
+ });
964
+ return run.id;
965
+ },
966
+ onRunFinish: async (input) => {
967
+ await this.#ctx.adapter.runs.finishRun(input.runId, {
968
+ status: input.status,
969
+ finishedAt: input.finishedAt,
970
+ exitCode: input.exitCode ?? null,
971
+ stdout: input.stdout ?? null,
972
+ stderr: input.stderr ?? null,
973
+ providerCommit: input.providerCommit
974
+ });
975
+ }
976
+ };
977
+ }
978
+ async persistSandboxState(transition) {
979
+ const result = await persistSandboxTransition(
980
+ this.#ctx.adapter.workspaces,
981
+ this.#record.id,
982
+ transition
983
+ );
984
+ this.updateFromRecord(result.record);
985
+ this.#sandboxState = result.state;
986
+ }
987
+ async resolveLatestWorkspace() {
988
+ const latest = await this.#ctx.adapter.workspaces.getWorkspace(this.#record.id);
989
+ if (!latest) {
990
+ throw new Error(`Workspace with id "${this.#record.id}" no longer exists`);
991
+ }
992
+ this.updateFromRecord(latest);
993
+ this.#sandboxState = readWorkspaceSandboxState(latest);
994
+ this.#sandboxConfig = readWorkspaceSandboxConfig(latest);
995
+ if (isWorkspaceSessionStateExpired(this.#sandboxState)) {
996
+ const result = await persistSandboxTransition(
997
+ this.#ctx.adapter.workspaces,
998
+ this.#record.id,
999
+ transitionToCold()
1000
+ );
1001
+ this.updateFromRecord(result.record);
1002
+ this.#sandboxState = result.state;
1003
+ this.#sandboxConfig = readWorkspaceSandboxConfig(result.record);
1004
+ }
1005
+ return this.#record;
1006
+ }
1007
+ resolveDescriptor(record) {
1008
+ return {
1009
+ id: record.id,
1010
+ name: record.name,
1011
+ status: record.status,
1012
+ createdAt: record.createdAt,
1013
+ updatedAt: record.updatedAt
1014
+ };
1015
+ }
1016
+ updateFromRecord(record) {
1017
+ this.#record = record;
1018
+ this.#descriptor = this.resolveDescriptor(record);
1019
+ this.#sandboxConfig = readWorkspaceSandboxConfig(record);
1020
+ }
1021
+ async createPolicySnapshot(policy) {
1022
+ return this.#ctx.adapter.policySnapshots.createPolicySnapshot({
1023
+ workspaceId: this.#record.id,
1024
+ policyId: describeWorkspacePolicyId(policy),
1025
+ config: asPolicySnapshotConfig(policy)
1026
+ });
1027
+ }
1028
+ async resolveDefaultPolicy() {
1029
+ const workspace = await this.resolveLatestWorkspace();
1030
+ return readWorkspacePolicy(workspace, allowAll());
1031
+ }
1032
+ };
1033
+ function normalizeSessionTimeoutMs(timeoutMs) {
1034
+ if (timeoutMs === void 0) {
1035
+ return void 0;
1036
+ }
1037
+ if (!Number.isInteger(timeoutMs) || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1038
+ throw new Error("openSession timeoutMs must be a positive integer in milliseconds.");
1039
+ }
1040
+ return timeoutMs;
1041
+ }
1042
+ function workspaceStateIsSession(state) {
1043
+ return state.kind === "session";
1044
+ }
1045
+
1046
+ // src/core/sandkit.ts
1047
+ var Sandkit = class {
1048
+ #ctx;
1049
+ constructor(options) {
1050
+ this.#ctx = createSandkitContext(options);
1051
+ }
1052
+ get context() {
1053
+ return this.#ctx;
1054
+ }
1055
+ async createWorkspace(input = {}) {
1056
+ const { id, name, policy, metadata, status, sandboxId, lastResumedAt } = input;
1057
+ const sandboxConfig = normalizeWorkspaceSandboxCreateConfig(input.sandbox);
1058
+ const sanitizedMetadata = removeWorkspaceSandboxConfigMetadata(
1059
+ removeWorkspacePolicyMetadata(metadata)
1060
+ );
1061
+ const workspaceMetadata = policy ? {
1062
+ ...sanitizedMetadata,
1063
+ ...asWorkspacePolicyMetadata(policy),
1064
+ ...asWorkspaceSandboxConfigMetadata(sandboxConfig)
1065
+ } : {
1066
+ ...sanitizedMetadata,
1067
+ ...asWorkspaceSandboxConfigMetadata(sandboxConfig)
1068
+ };
1069
+ const workspace = await this.#ctx.adapter.workspaces.createWorkspace({
1070
+ id,
1071
+ name,
1072
+ metadata: workspaceMetadata,
1073
+ status,
1074
+ sandboxId,
1075
+ lastResumedAt
1076
+ });
1077
+ return new WorkspaceHandle(this.#ctx, workspace);
1078
+ }
1079
+ async getWorkspace(id) {
1080
+ const workspace = await this.#ctx.adapter.workspaces.getWorkspace(id);
1081
+ if (!workspace) {
1082
+ throw new Error(`Workspace not found: ${id}`);
1083
+ }
1084
+ return new WorkspaceHandle(this.#ctx, workspace);
1085
+ }
1086
+ };
1087
+ function createSandkit(options) {
1088
+ return new Sandkit(options);
1089
+ }
1090
+ export {
1091
+ Sandkit,
1092
+ aiGateway,
1093
+ allowAll,
1094
+ allowService,
1095
+ allowServices,
1096
+ codex,
1097
+ createSandkit,
1098
+ denyAll,
1099
+ gemini,
1100
+ github
1101
+ };
1102
+ //# sourceMappingURL=index.js.map