@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 +48 -4
- package/dist/db/projects.d.ts +1 -1
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/agent-runner.d.ts.map +1 -1
- package/dist/lib/runtime-state.d.ts +5 -0
- package/dist/lib/runtime-state.d.ts.map +1 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/mcp/index.js +45 -10
- package/dist/server/index.js +41 -7
- package/dist/server/serve.d.ts +1 -0
- package/dist/server/serve.d.ts.map +1 -1
- package/package.json +1 -2
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
});
|
package/dist/db/projects.d.ts
CHANGED
|
@@ -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,
|
|
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":"
|
|
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 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
5964
|
+
finalizeSessionExit(session.id, r.exit_code ?? 0);
|
|
5932
5965
|
}).catch(() => {
|
|
5933
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/server/serve.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"
|
|
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.
|
|
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",
|