@giselles-ai/sandkit 0.1.0 → 0.1.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.
Files changed (49) hide show
  1. package/README.md +51 -7
  2. package/dist/adapters/drizzle.d.ts +2 -2
  3. package/dist/adapters/memory.d.ts +2 -2
  4. package/dist/adapters/sqlite-bun.d.ts +2 -2
  5. package/dist/{chunk-FSDVHEEX.js → chunk-2M2AZUOC.js} +2 -2
  6. package/dist/chunk-5MXHFOJH.js +50 -0
  7. package/dist/chunk-5MXHFOJH.js.map +1 -0
  8. package/dist/{chunk-7DLK7LOM.js → chunk-6GHZO3Y5.js} +2 -2
  9. package/dist/{chunk-VISDS5T7.js → chunk-NKTNTBOY.js} +97 -4
  10. package/dist/chunk-NKTNTBOY.js.map +1 -0
  11. package/dist/chunk-T76VM5D2.js +23 -0
  12. package/dist/chunk-T76VM5D2.js.map +1 -0
  13. package/dist/{chunk-REGOUXVI.js → chunk-UEAKE56H.js} +2 -2
  14. package/dist/{chunk-HVYCAAZQ.js → chunk-XN6DGLRP.js} +1 -1
  15. package/dist/chunk-XN6DGLRP.js.map +1 -0
  16. package/dist/chunk-YGUNJGP7.js +23 -0
  17. package/dist/chunk-YGUNJGP7.js.map +1 -0
  18. package/dist/index.d.ts +12 -8
  19. package/dist/index.js +353 -127
  20. package/dist/index.js.map +1 -1
  21. package/dist/integrations/mock.d.ts +3 -3
  22. package/dist/integrations/mock.js +21 -4
  23. package/dist/integrations/mock.js.map +1 -1
  24. package/dist/integrations/vercel.d.ts +3 -3
  25. package/dist/integrations/vercel.js +59 -17
  26. package/dist/integrations/vercel.js.map +1 -1
  27. package/dist/policies/ai-gateway.d.ts +1 -1
  28. package/dist/policies/ai-gateway.js +2 -2
  29. package/dist/policies/bun.d.ts +9 -0
  30. package/dist/policies/bun.js +10 -0
  31. package/dist/policies/bun.js.map +1 -0
  32. package/dist/policies/codex.d.ts +1 -1
  33. package/dist/policies/codex.js +2 -2
  34. package/dist/policies/gemini.d.ts +1 -1
  35. package/dist/policies/gemini.js +2 -2
  36. package/dist/policies/npm.d.ts +12 -0
  37. package/dist/policies/npm.js +10 -0
  38. package/dist/policies/npm.js.map +1 -0
  39. package/dist/{types-Cy36bS1j.d.ts → types-DNpj280o.d.ts} +2 -2
  40. package/dist/{types-BCgprbo8.d.ts → types-Dpr_BkF9.d.ts} +3 -0
  41. package/dist/{types-BEKQnjeb.d.ts → types-nu3vpBCZ.d.ts} +35 -5
  42. package/package.json +11 -1
  43. package/dist/chunk-HVYCAAZQ.js.map +0 -1
  44. package/dist/chunk-VISDS5T7.js.map +0 -1
  45. package/dist/chunk-XM4HGRXW.js +0 -37
  46. package/dist/chunk-XM4HGRXW.js.map +0 -1
  47. /package/dist/{chunk-FSDVHEEX.js.map → chunk-2M2AZUOC.js.map} +0 -0
  48. /package/dist/{chunk-7DLK7LOM.js.map → chunk-6GHZO3Y5.js.map} +0 -0
  49. /package/dist/{chunk-REGOUXVI.js.map → chunk-UEAKE56H.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,25 +1,35 @@
1
+ import {
2
+ allowNpm,
3
+ npm
4
+ } from "./chunk-T76VM5D2.js";
1
5
  import {
2
6
  github
3
- } from "./chunk-XM4HGRXW.js";
7
+ } from "./chunk-5MXHFOJH.js";
8
+ import {
9
+ getSandboxDriverFactory
10
+ } from "./chunk-XN6DGLRP.js";
4
11
  import "./chunk-UDFWES6J.js";
5
12
  import "./chunk-BDPTYR6V.js";
6
13
  import "./chunk-RMMOQD5Y.js";
7
14
  import {
8
15
  createMemoryAdapter
9
16
  } from "./chunk-CSOBTLWV.js";
17
+ import {
18
+ createId
19
+ } from "./chunk-DLGUA3H7.js";
20
+ import {
21
+ allowBun,
22
+ bun
23
+ } from "./chunk-YGUNJGP7.js";
10
24
  import {
11
25
  codex
12
- } from "./chunk-FSDVHEEX.js";
26
+ } from "./chunk-2M2AZUOC.js";
13
27
  import {
14
28
  gemini
15
- } from "./chunk-7DLK7LOM.js";
29
+ } from "./chunk-6GHZO3Y5.js";
16
30
  import {
17
31
  aiGateway
18
- } from "./chunk-REGOUXVI.js";
19
- import {
20
- getSandboxDriverFactory
21
- } from "./chunk-HVYCAAZQ.js";
22
- import "./chunk-DLGUA3H7.js";
32
+ } from "./chunk-UEAKE56H.js";
23
33
  import {
24
34
  allowAll,
25
35
  allowService,
@@ -30,7 +40,7 @@ import {
30
40
  parseWorkspacePolicy,
31
41
  redactWorkspacePolicy,
32
42
  serializeWorkspacePolicy
33
- } from "./chunk-VISDS5T7.js";
43
+ } from "./chunk-NKTNTBOY.js";
34
44
 
35
45
  // src/core/context.ts
36
46
  function resolveDeprecatedNetworkOption(options) {
@@ -397,7 +407,12 @@ var ManagedSandbox = class {
397
407
  normalized.command,
398
408
  normalized.args,
399
409
  normalized.policy,
400
- () => this.executeCommand(normalized.command, normalized.args, normalized.policy)
410
+ () => this.executeCommand(
411
+ normalized.command,
412
+ normalized.args,
413
+ normalized.policy,
414
+ normalized.detached
415
+ )
401
416
  );
402
417
  }
403
418
  async normalizeRunCommandInput(inputOrCommand, args) {
@@ -405,13 +420,17 @@ var ManagedSandbox = class {
405
420
  return {
406
421
  command: inputOrCommand,
407
422
  args,
408
- policy: await this.#resolveDefaultPolicy()
423
+ policy: await this.#resolveDefaultPolicy(),
424
+ timeoutMs: void 0,
425
+ detached: false
409
426
  };
410
427
  }
411
428
  return {
412
429
  command: inputOrCommand.command,
413
430
  args: inputOrCommand.args ?? [],
414
- policy: inputOrCommand.policy ?? await this.#resolveDefaultPolicy()
431
+ policy: inputOrCommand.policy ?? await this.#resolveDefaultPolicy(),
432
+ timeoutMs: inputOrCommand.timeoutMs,
433
+ detached: "detached" in inputOrCommand ? inputOrCommand.detached : false
415
434
  };
416
435
  }
417
436
  ensureCommandShape(command, args) {
@@ -425,44 +444,35 @@ var ManagedSandbox = class {
425
444
  async runUnitOfWork(command, args, effectivePolicy, operation) {
426
445
  const now = (/* @__PURE__ */ new Date()).toISOString();
427
446
  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;
447
+ let commandHandle;
439
448
  try {
440
- const commit = await this.snapshotWithFallback();
441
- providerCommit = commit.state;
442
- if (this.#onCommit) {
443
- await this.persistCommit(commit);
449
+ commandHandle = await operation();
450
+ } catch (commandError) {
451
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
452
+ let providerCommit;
453
+ let finalizeError;
454
+ try {
455
+ const commit = await this.snapshotWithFallback();
456
+ providerCommit = commit.state;
457
+ if (this.#onCommit) {
458
+ await this.persistCommit(commit);
459
+ }
460
+ } catch (error) {
461
+ finalizeError = error;
462
+ providerCommit = void 0;
444
463
  }
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
- });
464
+ try {
465
+ if (runId) {
466
+ await this.finishRun({
467
+ runId,
468
+ status: "failed",
469
+ providerCommit,
470
+ finishedAt
471
+ });
472
+ }
473
+ } catch (error) {
474
+ finalizeError = finalizeError ?? error;
461
475
  }
462
- } catch (error) {
463
- finalizeError = finalizeError ?? error;
464
- }
465
- if (commandError !== void 0) {
466
476
  if (finalizeError !== void 0) {
467
477
  throw new AggregateError(
468
478
  [commandError, finalizeError],
@@ -471,13 +481,67 @@ var ManagedSandbox = class {
471
481
  }
472
482
  throw commandError;
473
483
  }
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;
484
+ const waitResult = (async () => {
485
+ let commandError;
486
+ let commandResult;
487
+ let providerCommit;
488
+ let finalizeError;
489
+ try {
490
+ commandResult = await commandHandle.wait();
491
+ } catch (error) {
492
+ commandError = error;
493
+ }
494
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
495
+ const commandStatus = commandError === void 0 ? commandResult?.exitCode === 0 ? "succeeded" : "failed" : "failed";
496
+ try {
497
+ const commit = await this.snapshotWithFallback();
498
+ providerCommit = commit.state;
499
+ if (this.#onCommit) {
500
+ await this.persistCommit(commit);
501
+ }
502
+ } catch (error) {
503
+ finalizeError = error;
504
+ providerCommit = void 0;
505
+ }
506
+ const finalRunStatus = commandError === void 0 && finalizeError === void 0 ? commandStatus : "failed";
507
+ try {
508
+ if (runId) {
509
+ await this.finishRun({
510
+ runId,
511
+ status: finalRunStatus,
512
+ providerCommit,
513
+ finishedAt,
514
+ exitCode: commandResult?.exitCode,
515
+ stdout: commandResult?.stdout,
516
+ stderr: commandResult?.stderr
517
+ });
518
+ }
519
+ } catch (error) {
520
+ finalizeError = finalizeError ?? error;
521
+ }
522
+ if (commandError !== void 0) {
523
+ if (finalizeError !== void 0) {
524
+ throw new AggregateError(
525
+ [commandError, finalizeError],
526
+ "Sandbox command failed and unit-of-work durability finalization did not complete."
527
+ );
528
+ }
529
+ throw commandError;
530
+ }
531
+ if (finalizeError !== void 0) {
532
+ throw finalizeError;
533
+ }
534
+ if (!commandResult) {
535
+ throw new Error("Command result was missing after successful execution.");
536
+ }
537
+ return commandResult;
538
+ })();
539
+ void waitResult.catch(() => {
540
+ });
541
+ return {
542
+ wait: async () => waitResult,
543
+ logs: commandHandle.logs
544
+ };
481
545
  }
482
546
  async startRun(command, args, effectivePolicy, at) {
483
547
  if (!this.#runLifecycle?.onRunStart) {
@@ -490,9 +554,9 @@ var ManagedSandbox = class {
490
554
  startedAt: at
491
555
  });
492
556
  }
493
- async executeCommand(command, args, policy) {
557
+ async executeCommand(command, args, policy, detached) {
494
558
  await this.#driver.applyPolicy(policy);
495
- return this.#driver.runCommand(command, [...args]);
559
+ return this.#driver.runCommand(command, [...args], { detached });
496
560
  }
497
561
  async finishRun(input) {
498
562
  if (!this.#runLifecycle?.onRunFinish) {
@@ -537,7 +601,9 @@ var ManagedSession = class {
537
601
  const normalized = await this.normalizeSessionInput(inputOrCommand, args);
538
602
  this.ensureCommandShape(normalized.command, normalized.args);
539
603
  await this.#driver.applyPolicy(normalized.policy);
540
- return this.#driver.runCommand(normalized.command, [...normalized.args]);
604
+ return (await this.#driver.runCommand(normalized.command, [...normalized.args], {
605
+ detached: false
606
+ })).wait();
541
607
  }
542
608
  async commit() {
543
609
  await this.assertSessionActive();
@@ -615,6 +681,9 @@ var ManagedSession = class {
615
681
  policy: await this.resolveSessionPolicy()
616
682
  };
617
683
  }
684
+ if ("detached" in inputOrCommand) {
685
+ throw new Error("session exec does not support detached execution.");
686
+ }
618
687
  return {
619
688
  command: inputOrCommand.command,
620
689
  args: inputOrCommand.args ?? [],
@@ -669,11 +738,15 @@ var LazySandboxHandle = class {
669
738
  this.#getActiveLease = getActiveLease;
670
739
  }
671
740
  async runCommand(inputOrCommand, args = []) {
672
- const sandbox = await this.#resolveSandbox();
741
+ const sandbox = typeof inputOrCommand === "string" ? await this.#resolveSandbox() : await this.#resolveSandbox(inputOrCommand);
673
742
  if (typeof inputOrCommand === "string") {
674
- return sandbox.runCommand(inputOrCommand, args);
743
+ return sandbox.runCommand(inputOrCommand, args).then((command2) => command2.wait());
744
+ }
745
+ const command = await sandbox.runCommand(inputOrCommand);
746
+ if ("detached" in inputOrCommand && inputOrCommand.detached === true) {
747
+ return command;
675
748
  }
676
- return sandbox.runCommand(inputOrCommand);
749
+ return command.wait();
677
750
  }
678
751
  async openSession(input) {
679
752
  return this.#openSession(input);
@@ -687,25 +760,169 @@ var LazySandboxHandle = class {
687
760
  };
688
761
 
689
762
  // src/core/workspace.ts
763
+ var inFlightDurableCommands = /* @__PURE__ */ new Set();
690
764
  function setupStateFingerprint(command, args, policy) {
691
765
  return encodeURIComponent(
692
766
  JSON.stringify({
693
767
  command,
694
768
  args,
695
- policy: policy ? {
769
+ policy: {
696
770
  id: describeWorkspacePolicyId(policy),
697
771
  config: asPolicySnapshotConfig(policy)
698
- } : null
772
+ }
699
773
  })
700
774
  );
701
775
  }
702
776
  var sharedSetupStateId = (adapterId, setup) => {
703
- if (setup?.policy) {
777
+ if (setup) {
704
778
  assertWorkspacePolicyIsDurable(setup.policy);
705
779
  }
706
780
  const fingerprint = setup ? setupStateFingerprint(setup.command, [...setup.args ?? []], setup.policy) : "no-bootstrap";
707
781
  return `${adapterId}:shared-bootstrap:${fingerprint}`;
708
782
  };
783
+ function sharedSetupRecordId(ctx) {
784
+ if (!ctx.options.setup) {
785
+ return null;
786
+ }
787
+ return sharedSetupStateId(ctx.adapter.id, ctx.options.setup);
788
+ }
789
+ function assertNoDurableCommandInFlight(workspaceId) {
790
+ if (inFlightDurableCommands.has(workspaceId)) {
791
+ throw new Error(
792
+ "Cannot run a durable command while another durable command is still in flight."
793
+ );
794
+ }
795
+ }
796
+ function beginDurableCommand(workspaceId) {
797
+ assertNoDurableCommandInFlight(workspaceId);
798
+ inFlightDurableCommands.add(workspaceId);
799
+ }
800
+ function endDurableCommand(workspaceId) {
801
+ inFlightDurableCommands.delete(workspaceId);
802
+ }
803
+ async function readSharedSetupState(ctx) {
804
+ if (!ctx.options.setup) {
805
+ return null;
806
+ }
807
+ const sharedSetupId = sharedSetupRecordId(ctx);
808
+ if (!sharedSetupId) {
809
+ return null;
810
+ }
811
+ const setupState = await ctx.adapter.setupStates.getSetupState(sharedSetupId);
812
+ return setupState ? setupState.state : null;
813
+ }
814
+ async function persistSharedSetupState(ctx, state) {
815
+ const sharedSetupId = sharedSetupRecordId(ctx);
816
+ if (!sharedSetupId) {
817
+ return;
818
+ }
819
+ await ctx.adapter.setupStates.putSetupState({
820
+ id: sharedSetupId,
821
+ state: {
822
+ kind: state.kind,
823
+ sessionId: state.sessionId,
824
+ state: state.state
825
+ }
826
+ });
827
+ }
828
+ async function clearSharedSetupState(ctx) {
829
+ const sharedSetupId = sharedSetupRecordId(ctx);
830
+ if (!sharedSetupId) {
831
+ return;
832
+ }
833
+ await ctx.adapter.setupStates.deleteSetupState(sharedSetupId);
834
+ }
835
+ function isRecoverableSetupStateError(error) {
836
+ if (!(error instanceof Error)) {
837
+ return false;
838
+ }
839
+ return error.message.includes("Sandkit durable state corruption") && error.message.includes("sandkit_setup_states");
840
+ }
841
+ function assertSharedSetupAndPolicy(ctx) {
842
+ if (!ctx.options.setup) {
843
+ throw new Error("Sandkit setup is not configured.");
844
+ }
845
+ const { setup } = ctx.options;
846
+ if (!setup.policy) {
847
+ throw new Error("Shared setup policy is required.");
848
+ }
849
+ return setup;
850
+ }
851
+ async function bootstrapSharedSetupState(ctx, workspace, options) {
852
+ const setup = assertSharedSetupAndPolicy(ctx);
853
+ const sandbox = await ctx.driverFactory.createSandbox(workspace, {
854
+ ...options,
855
+ policy: setup.policy
856
+ });
857
+ const result = await runCommandAndWait(sandbox, setup.command, setup.args);
858
+ if (result.exitCode !== 0) {
859
+ throw new Error(
860
+ `Workspace setup failed with exit code ${result.exitCode}. ${result.stderr.trim()}`.trim()
861
+ );
862
+ }
863
+ const state = await sandbox.snapshot();
864
+ await persistSharedSetupState(ctx, state);
865
+ return state;
866
+ }
867
+ async function runCommandAndWait(sandbox, command, args) {
868
+ const commandArgs = args ?? [];
869
+ return (await sandbox.runCommand(command, [...commandArgs])).wait();
870
+ }
871
+ async function resolveSharedSetupState(ctx, workspace, options) {
872
+ if (!ctx.options.setup) {
873
+ return null;
874
+ }
875
+ assertSharedSetupAndPolicy(ctx);
876
+ try {
877
+ const setupState = await readSharedSetupState(ctx);
878
+ if (!setupState) {
879
+ return bootstrapSharedSetupState(ctx, workspace, options);
880
+ }
881
+ return setupState;
882
+ } catch (error) {
883
+ if (!isRecoverableSetupStateError(error)) {
884
+ throw error;
885
+ }
886
+ await clearSharedSetupState(ctx);
887
+ return bootstrapSharedSetupState(ctx, workspace, options);
888
+ }
889
+ }
890
+ function createInternalSharedBootstrapWorkspaceRecord() {
891
+ const now = (/* @__PURE__ */ new Date()).toISOString();
892
+ return {
893
+ id: createId("sandkit-internal-bootstrap"),
894
+ status: "inactive",
895
+ createdAt: now,
896
+ updatedAt: now
897
+ };
898
+ }
899
+ async function bootstrapSharedSetup(ctx) {
900
+ if (!ctx.options.setup) {
901
+ return;
902
+ }
903
+ const setup = assertSharedSetupAndPolicy(ctx);
904
+ const sharedSetupId = sharedSetupRecordId(ctx);
905
+ if (!sharedSetupId) {
906
+ return;
907
+ }
908
+ const workspace = createInternalSharedBootstrapWorkspaceRecord();
909
+ const setupOptions = {
910
+ policy: setup.policy
911
+ };
912
+ let setupState;
913
+ try {
914
+ setupState = await readSharedSetupState(ctx);
915
+ } catch (error) {
916
+ if (!isRecoverableSetupStateError(error)) {
917
+ throw error;
918
+ }
919
+ await clearSharedSetupState(ctx);
920
+ setupState = null;
921
+ }
922
+ if (!setupState) {
923
+ await bootstrapSharedSetupState(ctx, workspace, setupOptions);
924
+ }
925
+ }
709
926
  var WorkspaceHandle = class {
710
927
  #ctx;
711
928
  #record;
@@ -729,7 +946,7 @@ var WorkspaceHandle = class {
729
946
  get sandbox() {
730
947
  if (!this.#lazySandbox) {
731
948
  this.#lazySandbox = new LazySandboxHandle(
732
- () => this.createOrResumeSandboxForCommand(),
949
+ (input) => this.createOrResumeSandboxForCommand(input),
733
950
  (input) => this.openSession(input),
734
951
  () => this.attachSession(),
735
952
  () => this.getActiveLease()
@@ -759,19 +976,29 @@ var WorkspaceHandle = class {
759
976
  }
760
977
  return readWorkspaceSandboxLease(this.#record);
761
978
  }
762
- async createOrResumeSandboxForCommand() {
979
+ async createOrResumeSandboxForCommand(input) {
763
980
  await this.resolveLatestWorkspace();
764
981
  if (await this.resolveAttachableSession()) {
765
982
  throw new Error(
766
983
  "Cannot run command while a sandbox session is active. Use attachSession() to reuse it or commit the session first."
767
984
  );
768
985
  }
986
+ assertNoDurableCommandInFlight(this.#record.id);
987
+ beginDurableCommand(this.#record.id);
769
988
  const workspace = await this.resolveLatestWorkspace();
770
- const sandbox = await this.resolveSandboxDriver(workspace);
771
- return this.createManagedSandbox(sandbox);
989
+ try {
990
+ const sandbox = await this.resolveSandboxDriver(workspace, {
991
+ timeoutMs: normalizeRunCommandTimeoutMs(input?.timeoutMs)
992
+ });
993
+ return this.createManagedSandbox(sandbox);
994
+ } catch (error) {
995
+ endDurableCommand(this.#record.id);
996
+ throw error;
997
+ }
772
998
  }
773
999
  async openSession(input) {
774
1000
  await this.resolveLatestWorkspace();
1001
+ assertNoDurableCommandInFlight(this.#record.id);
775
1002
  if (await this.resolveAttachableSession()) {
776
1003
  throw new Error("A sandbox session is already active for this workspace.");
777
1004
  }
@@ -862,78 +1089,61 @@ var WorkspaceHandle = class {
862
1089
  if (currentState) {
863
1090
  return this.#ctx.driverFactory.resumeSandbox(workspace, currentState, options);
864
1091
  }
1092
+ const setupState = await resolveSharedSetupState(this.#ctx, workspace, options);
1093
+ if (!setupState) {
1094
+ return this.#ctx.driverFactory.createSandbox(workspace, options);
1095
+ }
865
1096
  try {
866
- const setupState = await this.#readSharedSetupState();
867
- if (!setupState) {
868
- return this.#bootstrapSetupState(workspace, options);
869
- }
870
1097
  return await this.#ctx.driverFactory.resumeSandbox(workspace, setupState, options);
871
1098
  } catch (error) {
872
- if (!this.#ctx.driverFactory.isSessionUnavailableError?.(error) && !this.#isRecoverableSetupStateError(error)) {
1099
+ if (!this.#ctx.driverFactory.isSessionUnavailableError?.(error) && !isRecoverableSetupStateError(error)) {
873
1100
  throw error;
874
1101
  }
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;
1102
+ await clearSharedSetupState(this.#ctx);
1103
+ const rerunSetupState = await bootstrapSharedSetupState(this.#ctx, workspace, options);
1104
+ return this.#ctx.driverFactory.resumeSandbox(workspace, rerunSetupState, options);
927
1105
  }
928
- return error.message.includes("Sandkit durable state corruption") && error.message.includes("sandkit_setup_states");
929
1106
  }
930
1107
  createManagedSandbox(sandbox) {
931
- return new ManagedSandbox(
1108
+ const managed = new ManagedSandbox(
932
1109
  sandbox,
933
1110
  async () => this.resolveDefaultPolicy(),
934
1111
  async (commit) => this.persistSandboxState(transitionAfterCommandCommit(commit, (/* @__PURE__ */ new Date()).toISOString())),
935
1112
  this.createRunLifecycle(sandbox)
936
1113
  );
1114
+ const workspaceId = this.#record.id;
1115
+ const originalRunCommand = managed.runCommand.bind(managed);
1116
+ const wrappedRunCommand = async (inputOrCommand, args = []) => {
1117
+ let releaseCalled = false;
1118
+ const release = async () => {
1119
+ if (releaseCalled) {
1120
+ return;
1121
+ }
1122
+ releaseCalled = true;
1123
+ endDurableCommand(workspaceId);
1124
+ };
1125
+ try {
1126
+ const command = typeof inputOrCommand === "string" ? await originalRunCommand(inputOrCommand, args) : await originalRunCommand(inputOrCommand);
1127
+ const completion = (async () => {
1128
+ try {
1129
+ return await command.wait();
1130
+ } finally {
1131
+ await release();
1132
+ }
1133
+ })();
1134
+ void completion.catch(() => {
1135
+ });
1136
+ return {
1137
+ wait: () => completion,
1138
+ logs: command.logs
1139
+ };
1140
+ } catch (error) {
1141
+ await release();
1142
+ throw error;
1143
+ }
1144
+ };
1145
+ managed.runCommand = wrappedRunCommand;
1146
+ return managed;
937
1147
  }
938
1148
  makeSandboxDriverOptions(policy, timeoutMs) {
939
1149
  const next = {
@@ -1039,6 +1249,15 @@ function normalizeSessionTimeoutMs(timeoutMs) {
1039
1249
  }
1040
1250
  return timeoutMs;
1041
1251
  }
1252
+ function normalizeRunCommandTimeoutMs(timeoutMs) {
1253
+ if (timeoutMs === void 0) {
1254
+ return void 0;
1255
+ }
1256
+ if (!Number.isInteger(timeoutMs) || !Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1257
+ throw new Error("runCommand timeoutMs must be a positive integer in milliseconds.");
1258
+ }
1259
+ return timeoutMs;
1260
+ }
1042
1261
  function workspaceStateIsSession(state) {
1043
1262
  return state.kind === "session";
1044
1263
  }
@@ -1052,6 +1271,9 @@ var Sandkit = class {
1052
1271
  get context() {
1053
1272
  return this.#ctx;
1054
1273
  }
1274
+ async bootstrap() {
1275
+ await bootstrapSharedSetup(this.#ctx);
1276
+ }
1055
1277
  async createWorkspace(input = {}) {
1056
1278
  const { id, name, policy, metadata, status, sandboxId, lastResumedAt } = input;
1057
1279
  const sandboxConfig = normalizeWorkspaceSandboxCreateConfig(input.sandbox);
@@ -1091,12 +1313,16 @@ export {
1091
1313
  Sandkit,
1092
1314
  aiGateway,
1093
1315
  allowAll,
1316
+ allowBun,
1317
+ allowNpm,
1094
1318
  allowService,
1095
1319
  allowServices,
1320
+ bun,
1096
1321
  codex,
1097
1322
  createSandkit,
1098
1323
  denyAll,
1099
1324
  gemini,
1100
- github
1325
+ github,
1326
+ npm
1101
1327
  };
1102
1328
  //# sourceMappingURL=index.js.map