@hasna/sandboxes 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -3364,6 +3364,25 @@ var init_stream = __esm(() => {
3364
3364
  listeners = new Map;
3365
3365
  });
3366
3366
 
3367
+ // src/lib/runtime-state.ts
3368
+ function getErrorMessage(error) {
3369
+ return error instanceof Error ? error.message : String(error);
3370
+ }
3371
+ function finalizeSessionExit(sessionId, exitCode) {
3372
+ endSession(sessionId, exitCode, exitCode === 0 ? "completed" : "failed");
3373
+ }
3374
+ function finalizeSessionFailure(sessionId, _error, exitCode = 1) {
3375
+ endSession(sessionId, exitCode, "failed");
3376
+ }
3377
+ function finalizeSandboxProvisionFailure(sandboxId, error) {
3378
+ updateSandbox(sandboxId, { status: "error" });
3379
+ return getErrorMessage(error);
3380
+ }
3381
+ var init_runtime_state = __esm(() => {
3382
+ init_sessions();
3383
+ init_sandboxes();
3384
+ });
3385
+
3367
3386
  // src/lib/agents/claude.ts
3368
3387
  class ClaudeDriver {
3369
3388
  name = "claude";
@@ -3543,7 +3562,7 @@ async function runAgent(sandboxId, opts) {
3543
3562
  }).then((result) => {
3544
3563
  const exitResult = result;
3545
3564
  const status = exitResult.exit_code === 0 ? "completed" : "failed";
3546
- endSession(session.id, exitResult.exit_code ?? 0, status);
3565
+ finalizeSessionExit(session.id, exitResult.exit_code ?? 0);
3547
3566
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} finished with exit code ${exitResult.exit_code}`);
3548
3567
  if (opts.webhookUrl && webhookEvents.includes("complete")) {
3549
3568
  fireWebhook(opts.webhookUrl, {
@@ -3558,7 +3577,7 @@ async function runAgent(sandboxId, opts) {
3558
3577
  });
3559
3578
  }
3560
3579
  }).catch((err) => {
3561
- endSession(session.id, 1, "failed");
3580
+ finalizeSessionFailure(session.id, err);
3562
3581
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} failed: ${err.message}`);
3563
3582
  if (opts.webhookUrl && webhookEvents.includes("error")) {
3564
3583
  fireWebhook(opts.webhookUrl, {
@@ -3590,6 +3609,7 @@ var init_agent_runner = __esm(() => {
3590
3609
  init_providers();
3591
3610
  init_stream();
3592
3611
  init_agents();
3612
+ init_runtime_state();
3593
3613
  });
3594
3614
 
3595
3615
  // node_modules/commander/esm.mjs
@@ -3669,6 +3689,20 @@ function listAgents() {
3669
3689
  init_providers();
3670
3690
  init_config();
3671
3691
  init_stream();
3692
+ init_runtime_state();
3693
+
3694
+ // src/lib/version.ts
3695
+ import { readFileSync as readFileSync2 } from "fs";
3696
+ var cachedVersion;
3697
+ function getPackageVersion() {
3698
+ if (cachedVersion)
3699
+ return cachedVersion;
3700
+ const packageJson = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
3701
+ cachedVersion = packageJson.version ?? "0.0.0";
3702
+ return cachedVersion;
3703
+ }
3704
+
3705
+ // src/cli/index.tsx
3672
3706
  function printTable(headers, rows) {
3673
3707
  const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length)));
3674
3708
  const headerLine = headers.map((h, i) => chalk.bold(h.padEnd(widths[i]))).join(" ");
@@ -3720,11 +3754,12 @@ function parseEnvVars(envArgs) {
3720
3754
  }
3721
3755
  return vars;
3722
3756
  }
3723
- var program2 = new Command().name("sandboxes").description("Universal cloud sandbox manager for AI coding agents").version("0.1.0");
3757
+ var program2 = new Command().name("sandboxes").description("Universal cloud sandbox manager for AI coding agents").version(getPackageVersion());
3724
3758
  program2.command("create").description("Create a new sandbox").option("-p, --provider <provider>", "Provider (e2b, daytona, modal)").option("-i, --image <image>", "Container image").option("-t, --timeout <seconds>", "Timeout in seconds").option("-n, --name <name>", "Sandbox name").option("-e, --env <KEY=VAL...>", "Environment variables", (val, acc) => {
3725
3759
  acc.push(val);
3726
3760
  return acc;
3727
3761
  }, []).action(async (opts) => {
3762
+ let sandboxId;
3728
3763
  try {
3729
3764
  const provider = opts.provider || getDefaultProvider();
3730
3765
  const timeout = opts.timeout ? parseInt(opts.timeout, 10) : getDefaultTimeout();
@@ -3737,6 +3772,7 @@ program2.command("create").description("Create a new sandbox").option("-p, --pro
3737
3772
  timeout,
3738
3773
  env_vars: envVars
3739
3774
  });
3775
+ sandboxId = sandbox.id;
3740
3776
  console.log(chalk.dim("Creating sandbox..."));
3741
3777
  const p = await getProvider(provider);
3742
3778
  const result = await p.create({
@@ -3756,6 +3792,9 @@ program2.command("create").description("Create a new sandbox").option("-p, --pro
3756
3792
  console.log(` ${chalk.bold("Name:")} ${updated.name}`);
3757
3793
  }
3758
3794
  } catch (err) {
3795
+ if (sandboxId) {
3796
+ finalizeSandboxProvisionFailure(sandboxId, err);
3797
+ }
3759
3798
  handleError(err);
3760
3799
  }
3761
3800
  });
@@ -3812,6 +3851,7 @@ program2.command("show <id>").description("Show sandbox details").action((id) =>
3812
3851
  }
3813
3852
  });
3814
3853
  program2.command("exec <id> <command...>").description("Execute a command in a sandbox").action(async (id, commandParts) => {
3854
+ let sessionId;
3815
3855
  try {
3816
3856
  const sandbox = getSandbox(id);
3817
3857
  if (!sandbox.provider_sandbox_id) {
@@ -3823,6 +3863,7 @@ program2.command("exec <id> <command...>").description("Execute a command in a s
3823
3863
  sandbox_id: sandbox.id,
3824
3864
  command: cmd
3825
3865
  });
3866
+ sessionId = session.id;
3826
3867
  const collector = createStreamCollector(sandbox.id, session.id);
3827
3868
  const p = await getProvider(sandbox.provider);
3828
3869
  const result = await p.exec(sandbox.provider_sandbox_id, cmd, {
@@ -3836,9 +3877,12 @@ program2.command("exec <id> <command...>").description("Execute a command in a s
3836
3877
  }
3837
3878
  });
3838
3879
  const execResult = "exit_code" in result ? result : await result.wait();
3839
- endSession(session.id, execResult.exit_code, execResult.exit_code === 0 ? "completed" : "failed");
3880
+ finalizeSessionExit(session.id, execResult.exit_code);
3840
3881
  process.exit(execResult.exit_code);
3841
3882
  } catch (err) {
3883
+ if (sessionId) {
3884
+ finalizeSessionFailure(sessionId, err);
3885
+ }
3842
3886
  handleError(err);
3843
3887
  }
3844
3888
  });
@@ -4,6 +4,6 @@ export declare function createProject(input: CreateProjectInput): Project;
4
4
  export declare function getProject(id: string): Project;
5
5
  export declare function getProjectByPath(path: string): Project | null;
6
6
  export declare function listProjects(): Project[];
7
- export declare function ensureProject(name: string, path: string): Project;
7
+ export declare function ensureProject(name: string, path: string, description?: string): Project;
8
8
  export declare function deleteProject(id: string): void;
9
9
  //# sourceMappingURL=projects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAKlE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASlD;AAID,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAYhE;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAY9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAS7D;AAED,wBAAgB,YAAY,IAAI,OAAO,EAAE,CAQxC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKjE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAO9C"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAKlE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CASlD;AAID,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAYhE;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAY9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAS7D;AAED,wBAAgB,YAAY,IAAI,OAAO,EAAE,CAQxC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAKvF;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAO9C"}
package/dist/index.js CHANGED
@@ -1308,11 +1308,11 @@ function listProjects() {
1308
1308
  const rows = db2.query("SELECT * FROM projects ORDER BY created_at DESC").all();
1309
1309
  return rows.map(rowToProject);
1310
1310
  }
1311
- function ensureProject(name, path) {
1311
+ function ensureProject(name, path, description) {
1312
1312
  const existing = getProjectByPath(path);
1313
1313
  if (existing)
1314
1314
  return existing;
1315
- return createProject({ name, path });
1315
+ return createProject({ name, path, description });
1316
1316
  }
1317
1317
  function deleteProject(id) {
1318
1318
  const db2 = getDatabase();
@@ -1 +1 @@
1
- {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../src/lib/agent-runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC;CACpD;AAcD,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAyGzB;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhE"}
1
+ {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../src/lib/agent-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC;CACpD;AAcD,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAyGzB;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhE"}
@@ -0,0 +1,5 @@
1
+ export declare function getErrorMessage(error: unknown): string;
2
+ export declare function finalizeSessionExit(sessionId: string, exitCode: number): void;
3
+ export declare function finalizeSessionFailure(sessionId: string, _error?: unknown, exitCode?: number): void;
4
+ export declare function finalizeSandboxProvisionFailure(sandboxId: string, error?: unknown): string;
5
+ //# sourceMappingURL=runtime-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-state.d.ts","sourceRoot":"","sources":["../../src/lib/runtime-state.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE7E;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,SAAI,GAAG,IAAI,CAG9F;AAED,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAI1F"}
@@ -0,0 +1,2 @@
1
+ export declare function getPackageVersion(): string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"AAIA,wBAAgB,iBAAiB,IAAI,MAAM,CAS1C"}
package/dist/mcp/index.js CHANGED
@@ -5203,11 +5203,11 @@ function listProjects() {
5203
5203
  const rows = db2.query("SELECT * FROM projects ORDER BY created_at DESC").all();
5204
5204
  return rows.map(rowToProject);
5205
5205
  }
5206
- function ensureProject(name, path) {
5206
+ function ensureProject(name, path, description) {
5207
5207
  const existing = getProjectByPath(path);
5208
5208
  if (existing)
5209
5209
  return existing;
5210
- return createProject({ name, path });
5210
+ return createProject({ name, path, description });
5211
5211
  }
5212
5212
 
5213
5213
  // src/db/templates.ts
@@ -5538,6 +5538,21 @@ function getAgentDriver(name) {
5538
5538
  return DRIVER_MAP.get(name);
5539
5539
  }
5540
5540
 
5541
+ // src/lib/runtime-state.ts
5542
+ function getErrorMessage(error) {
5543
+ return error instanceof Error ? error.message : String(error);
5544
+ }
5545
+ function finalizeSessionExit(sessionId, exitCode) {
5546
+ endSession(sessionId, exitCode, exitCode === 0 ? "completed" : "failed");
5547
+ }
5548
+ function finalizeSessionFailure(sessionId, _error, exitCode = 1) {
5549
+ endSession(sessionId, exitCode, "failed");
5550
+ }
5551
+ function finalizeSandboxProvisionFailure(sandboxId, error) {
5552
+ updateSandbox(sandboxId, { status: "error" });
5553
+ return getErrorMessage(error);
5554
+ }
5555
+
5541
5556
  // src/lib/agent-runner.ts
5542
5557
  async function fireWebhook(url, payload) {
5543
5558
  try {
@@ -5600,7 +5615,7 @@ async function runAgent(sandboxId, opts) {
5600
5615
  }).then((result) => {
5601
5616
  const exitResult = result;
5602
5617
  const status = exitResult.exit_code === 0 ? "completed" : "failed";
5603
- endSession(session.id, exitResult.exit_code ?? 0, status);
5618
+ finalizeSessionExit(session.id, exitResult.exit_code ?? 0);
5604
5619
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} finished with exit code ${exitResult.exit_code}`);
5605
5620
  if (opts.webhookUrl && webhookEvents.includes("complete")) {
5606
5621
  fireWebhook(opts.webhookUrl, {
@@ -5615,7 +5630,7 @@ async function runAgent(sandboxId, opts) {
5615
5630
  });
5616
5631
  }
5617
5632
  }).catch((err) => {
5618
- endSession(session.id, 1, "failed");
5633
+ finalizeSessionFailure(session.id, err);
5619
5634
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} failed: ${err.message}`);
5620
5635
  if (opts.webhookUrl && webhookEvents.includes("error")) {
5621
5636
  fireWebhook(opts.webhookUrl, {
@@ -5684,6 +5699,17 @@ function getBuiltinImageSetupScript(image) {
5684
5699
  return BUILTIN_IMAGES[image]?.setup_script;
5685
5700
  }
5686
5701
 
5702
+ // src/lib/version.ts
5703
+ import { readFileSync as readFileSync2 } from "fs";
5704
+ var cachedVersion;
5705
+ function getPackageVersion() {
5706
+ if (cachedVersion)
5707
+ return cachedVersion;
5708
+ const packageJson = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
5709
+ cachedVersion = packageJson.version ?? "0.0.0";
5710
+ return cachedVersion;
5711
+ }
5712
+
5687
5713
  // src/mcp/index.ts
5688
5714
  var E2B_COST_PER_SECOND = 0.000014;
5689
5715
  var DAYTONA_COST_PER_SECOND = 0.00001;
@@ -5744,7 +5770,7 @@ var TOOL_CATALOG = [
5744
5770
  ];
5745
5771
  var server = new McpServer({
5746
5772
  name: "sandboxes",
5747
- version: "0.1.0"
5773
+ version: getPackageVersion()
5748
5774
  });
5749
5775
  server.tool("create_sandbox", "Create a new sandbox", {
5750
5776
  provider: exports_external.string().optional().describe("Provider name (e2b, daytona, modal)"),
@@ -5760,6 +5786,7 @@ server.tool("create_sandbox", "Create a new sandbox", {
5760
5786
  budget_limit_usd: exports_external.number().optional().describe("Auto-terminate sandbox if compute cost exceeds this USD amount"),
5761
5787
  on_budget_exceeded: exports_external.enum(["terminate", "pause", "notify"]).optional().describe("Action when budget limit is reached (default: terminate)")
5762
5788
  }, async (params) => {
5789
+ let sandboxId;
5763
5790
  try {
5764
5791
  const providerName = params.provider ?? getDefaultProvider();
5765
5792
  const timeout = params.timeout ?? getDefaultTimeout();
@@ -5787,6 +5814,7 @@ server.tool("create_sandbox", "Create a new sandbox", {
5787
5814
  budget_limit_usd: params.budget_limit_usd,
5788
5815
  on_budget_exceeded: params.on_budget_exceeded
5789
5816
  });
5817
+ sandboxId = sandbox.id;
5790
5818
  const provider = await getProvider(providerName);
5791
5819
  if (params.snapshot_id) {
5792
5820
  const snapshot = getSnapshot(params.snapshot_id);
@@ -5823,6 +5851,9 @@ server.tool("create_sandbox", "Create a new sandbox", {
5823
5851
  }
5824
5852
  return ok(updated);
5825
5853
  } catch (e) {
5854
+ if (sandboxId) {
5855
+ finalizeSandboxProvisionFailure(sandboxId, e);
5856
+ }
5826
5857
  return err(e);
5827
5858
  }
5828
5859
  });
@@ -5907,6 +5938,7 @@ server.tool("exec_command", "Execute a command in a sandbox", {
5907
5938
  stdin: exports_external.string().optional().describe("String to pipe as stdin to the command"),
5908
5939
  tty: exports_external.boolean().optional().describe("Allocate a TTY for the session (best-effort)")
5909
5940
  }, async (params) => {
5941
+ let sessionId;
5910
5942
  try {
5911
5943
  const sandbox = getSandbox(params.sandbox_id);
5912
5944
  if (!sandbox.provider_sandbox_id)
@@ -5915,6 +5947,7 @@ server.tool("exec_command", "Execute a command in a sandbox", {
5915
5947
  sandbox_id: sandbox.id,
5916
5948
  command: params.command
5917
5949
  });
5950
+ sessionId = session.id;
5918
5951
  const collector = createStreamCollector(sandbox.id, session.id);
5919
5952
  const provider = await getProvider(sandbox.provider);
5920
5953
  const callEnv = { ...sandbox.env_vars, ...params.env_vars };
@@ -5928,9 +5961,9 @@ server.tool("exec_command", "Execute a command in a sandbox", {
5928
5961
  tty: params.tty
5929
5962
  }).then((res) => {
5930
5963
  const r = res;
5931
- endSession(session.id, r.exit_code ?? 0);
5964
+ finalizeSessionExit(session.id, r.exit_code ?? 0);
5932
5965
  }).catch(() => {
5933
- endSession(session.id, 1);
5966
+ finalizeSessionFailure(session.id);
5934
5967
  });
5935
5968
  return ok({ session_id: session.id, background: true });
5936
5969
  }
@@ -5942,7 +5975,7 @@ server.tool("exec_command", "Execute a command in a sandbox", {
5942
5975
  tty: params.tty
5943
5976
  });
5944
5977
  const execResult = result;
5945
- endSession(session.id, execResult.exit_code);
5978
+ finalizeSessionExit(session.id, execResult.exit_code);
5946
5979
  return ok({
5947
5980
  session_id: session.id,
5948
5981
  exit_code: execResult.exit_code,
@@ -5950,6 +5983,9 @@ server.tool("exec_command", "Execute a command in a sandbox", {
5950
5983
  stderr: execResult.stderr
5951
5984
  });
5952
5985
  } catch (e) {
5986
+ if (sessionId) {
5987
+ finalizeSessionFailure(sessionId, e);
5988
+ }
5953
5989
  return err(e);
5954
5990
  }
5955
5991
  });
@@ -6049,8 +6085,7 @@ server.tool("register_project", "Register a project", {
6049
6085
  description: exports_external.string().optional().describe("Project description")
6050
6086
  }, async (params) => {
6051
6087
  try {
6052
- const project = ensureProject(params.name, params.path);
6053
- return ok(project);
6088
+ return ok(ensureProject(params.name, params.path, params.description));
6054
6089
  } catch (e) {
6055
6090
  return err(e);
6056
6091
  }
@@ -1245,11 +1245,11 @@ function listProjects() {
1245
1245
  const rows = db2.query("SELECT * FROM projects ORDER BY created_at DESC").all();
1246
1246
  return rows.map(rowToProject);
1247
1247
  }
1248
- function ensureProject(name, path) {
1248
+ function ensureProject(name, path, description) {
1249
1249
  const existing = getProjectByPath(path);
1250
1250
  if (existing)
1251
1251
  return existing;
1252
- return createProject({ name, path });
1252
+ return createProject({ name, path, description });
1253
1253
  }
1254
1254
 
1255
1255
  // src/db/webhooks.ts
@@ -1443,6 +1443,32 @@ function emitLifecycleEvent(sandboxId, message) {
1443
1443
  notifyListeners(sandboxId, "lifecycle", message);
1444
1444
  }
1445
1445
 
1446
+ // src/lib/runtime-state.ts
1447
+ function getErrorMessage(error) {
1448
+ return error instanceof Error ? error.message : String(error);
1449
+ }
1450
+ function finalizeSessionExit(sessionId, exitCode) {
1451
+ endSession(sessionId, exitCode, exitCode === 0 ? "completed" : "failed");
1452
+ }
1453
+ function finalizeSessionFailure(sessionId, _error, exitCode = 1) {
1454
+ endSession(sessionId, exitCode, "failed");
1455
+ }
1456
+ function finalizeSandboxProvisionFailure(sandboxId, error) {
1457
+ updateSandbox(sandboxId, { status: "error" });
1458
+ return getErrorMessage(error);
1459
+ }
1460
+
1461
+ // src/lib/version.ts
1462
+ import { readFileSync as readFileSync2 } from "fs";
1463
+ var cachedVersion;
1464
+ function getPackageVersion() {
1465
+ if (cachedVersion)
1466
+ return cachedVersion;
1467
+ const packageJson = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
1468
+ cachedVersion = packageJson.version ?? "0.0.0";
1469
+ return cachedVersion;
1470
+ }
1471
+
1446
1472
  // src/server/serve.ts
1447
1473
  function json(data, status = 200) {
1448
1474
  return new Response(JSON.stringify(data), {
@@ -1488,7 +1514,7 @@ async function handleRequest(req) {
1488
1514
  return json({ ok: true });
1489
1515
  }
1490
1516
  if (pathname === "/api/health" && method === "GET") {
1491
- return json({ status: "ok", version: "0.1.0" });
1517
+ return json({ status: "ok", version: getPackageVersion() });
1492
1518
  }
1493
1519
  if (pathname === "/api/sandboxes" && method === "GET") {
1494
1520
  const status = url.searchParams.get("status") || undefined;
@@ -1500,11 +1526,13 @@ async function handleRequest(req) {
1500
1526
  return json(result);
1501
1527
  }
1502
1528
  if (pathname === "/api/sandboxes" && method === "POST") {
1529
+ let sandboxId;
1503
1530
  try {
1504
1531
  const input = await body(req);
1505
1532
  const providerName = input.provider || getDefaultProvider();
1506
1533
  const timeout = input.timeout || getDefaultTimeout();
1507
1534
  const sandbox = createSandbox({ ...input, provider: providerName, timeout });
1535
+ sandboxId = sandbox.id;
1508
1536
  const provider = await getProvider(providerName);
1509
1537
  const providerSandbox = await provider.create({
1510
1538
  image: input.image,
@@ -1518,7 +1546,8 @@ async function handleRequest(req) {
1518
1546
  emitLifecycleEvent(sandbox.id, `Sandbox created with provider ${providerName}`);
1519
1547
  return json(updated, 201);
1520
1548
  } catch (err) {
1521
- return error(err.message, 500);
1549
+ const message = sandboxId ? finalizeSandboxProvisionFailure(sandboxId, err) : getErrorMessage(err);
1550
+ return error(message, 500);
1522
1551
  }
1523
1552
  }
1524
1553
  let params = matchRoute(pathname, method, "/api/sandboxes/:id", "GET");
@@ -1561,6 +1590,7 @@ async function handleRequest(req) {
1561
1590
  }
1562
1591
  params = matchRoute(pathname, method, "/api/sandboxes/:id/exec", "POST");
1563
1592
  if (params) {
1593
+ let sessionId;
1564
1594
  try {
1565
1595
  const sandbox = getSandbox(params["id"]);
1566
1596
  if (!sandbox.provider_sandbox_id) {
@@ -1568,6 +1598,7 @@ async function handleRequest(req) {
1568
1598
  }
1569
1599
  const { command } = await body(req);
1570
1600
  const session = createSession({ sandbox_id: sandbox.id, command });
1601
+ sessionId = session.id;
1571
1602
  const collector = createStreamCollector(sandbox.id, session.id);
1572
1603
  const provider = await getProvider(sandbox.provider);
1573
1604
  const result = await provider.exec(sandbox.provider_sandbox_id, command, {
@@ -1575,12 +1606,15 @@ async function handleRequest(req) {
1575
1606
  onStderr: collector.onStderr
1576
1607
  });
1577
1608
  if ("exit_code" in result) {
1578
- endSession(session.id, result.exit_code);
1609
+ finalizeSessionExit(session.id, result.exit_code);
1579
1610
  return json({ session_id: session.id, ...result });
1580
1611
  }
1581
1612
  return json({ session_id: session.id, status: "running" });
1582
1613
  } catch (err) {
1583
- return error(err.message, 500);
1614
+ if (sessionId) {
1615
+ finalizeSessionFailure(sessionId, err);
1616
+ }
1617
+ return error(getErrorMessage(err), 500);
1584
1618
  }
1585
1619
  }
1586
1620
  params = matchRoute(pathname, method, "/api/sandboxes/:id/keep-alive", "POST");
@@ -1645,7 +1679,7 @@ async function handleRequest(req) {
1645
1679
  if (pathname === "/api/projects" && method === "POST") {
1646
1680
  try {
1647
1681
  const input = await body(req);
1648
- return json(ensureProject(input.name, input.path), 201);
1682
+ return json(ensureProject(input.name, input.path, input.description), 201);
1649
1683
  } catch (err) {
1650
1684
  return error(err.message, 500);
1651
1685
  }
@@ -1,2 +1,3 @@
1
+ export declare function handleRequest(req: Request): Promise<Response>;
1
2
  export declare function startServer(port: number): void;
2
3
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAyVA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9C"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAuEA,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA+RnE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/sandboxes",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "author": "Andrei Hasna <andrei@hasna.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,6 @@
10
10
  "dependencies": {
11
11
  "@daytonaio/sdk": "^0.18.0",
12
12
  "@e2b/code-interpreter": "^1.5.0",
13
- "@hasna/sandboxes": "^0.1.8",
14
13
  "@modelcontextprotocol/sdk": "^1.12.1",
15
14
  "chalk": "^5.4.1",
16
15
  "commander": "^13.1.0",