@hasna/sandboxes 0.1.6 → 0.1.8

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
@@ -2244,6 +2244,37 @@ CREATE TABLE IF NOT EXISTS templates (
2244
2244
  CREATE INDEX IF NOT EXISTS idx_templates_name ON templates(name);
2245
2245
 
2246
2246
  INSERT OR IGNORE INTO _migrations (id) VALUES (2);
2247
+ `,
2248
+ `
2249
+ CREATE TABLE IF NOT EXISTS sandbox_sessions_new (
2250
+ id TEXT PRIMARY KEY,
2251
+ sandbox_id TEXT NOT NULL REFERENCES sandboxes(id) ON DELETE CASCADE,
2252
+ agent_name TEXT,
2253
+ agent_type TEXT,
2254
+ command TEXT,
2255
+ status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed', 'killed')),
2256
+ exit_code INTEGER,
2257
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
2258
+ ended_at TEXT
2259
+ );
2260
+ INSERT INTO sandbox_sessions_new SELECT * FROM sandbox_sessions;
2261
+ DROP TABLE sandbox_sessions;
2262
+ ALTER TABLE sandbox_sessions_new RENAME TO sandbox_sessions;
2263
+ CREATE INDEX IF NOT EXISTS idx_sessions_sandbox ON sandbox_sessions(sandbox_id);
2264
+ CREATE INDEX IF NOT EXISTS idx_sessions_status ON sandbox_sessions(status);
2265
+ INSERT OR IGNORE INTO _migrations (id) VALUES (3);
2266
+ `,
2267
+ `
2268
+ CREATE TABLE IF NOT EXISTS snapshots (
2269
+ id TEXT PRIMARY KEY,
2270
+ sandbox_id TEXT NOT NULL,
2271
+ provider_sandbox_id TEXT NOT NULL,
2272
+ provider TEXT NOT NULL,
2273
+ name TEXT,
2274
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2275
+ );
2276
+ CREATE INDEX IF NOT EXISTS idx_snapshots_sandbox ON snapshots(sandbox_id);
2277
+ INSERT OR IGNORE INTO _migrations (id) VALUES (4);
2247
2278
  `
2248
2279
  ];
2249
2280
  });
@@ -2733,6 +2764,15 @@ class E2BProvider {
2733
2764
  throw new ProviderError("e2b", `Failed to resume sandbox: ${err.message}`);
2734
2765
  }
2735
2766
  }
2767
+ async getPublicUrl(sandboxId, port, _protocol) {
2768
+ const sandbox = await this.getInstance(sandboxId);
2769
+ try {
2770
+ const host = sandbox.getHost(port);
2771
+ return `https://${host}`;
2772
+ } catch (err) {
2773
+ throw new ProviderError("e2b", `Failed to get public URL for port ${port}: ${err.message}`);
2774
+ }
2775
+ }
2736
2776
  async keepAlive(sandboxId, durationMs) {
2737
2777
  const sandbox = await this.getInstance(sandboxId);
2738
2778
  try {
@@ -2893,6 +2933,9 @@ class DaytonaProvider {
2893
2933
  throw new ProviderError("daytona", `Failed to delete sandbox: ${err.message}`);
2894
2934
  }
2895
2935
  }
2936
+ async getPublicUrl(_sandboxId, _port, _protocol) {
2937
+ throw new ProviderError("daytona", "Port forwarding not supported by Daytona provider");
2938
+ }
2896
2939
  async pause(_sandboxId) {
2897
2940
  throw new ProviderError("daytona", "Pause/resume not supported by Daytona provider");
2898
2941
  }
@@ -3121,6 +3164,9 @@ class ModalProvider {
3121
3164
  async delete(sandboxId) {
3122
3165
  await this.stop(sandboxId);
3123
3166
  }
3167
+ async getPublicUrl(_sandboxId, _port, _protocol) {
3168
+ throw new ProviderError("modal", "Port forwarding not supported by Modal provider");
3169
+ }
3124
3170
  async pause(_sandboxId) {
3125
3171
  throw new ProviderError("modal", "Pause/resume not supported by Modal provider");
3126
3172
  }
@@ -3272,18 +3318,152 @@ var init_stream = __esm(() => {
3272
3318
  listeners = new Map;
3273
3319
  });
3274
3320
 
3321
+ // src/lib/agents/claude.ts
3322
+ class ClaudeDriver {
3323
+ name = "claude";
3324
+ requiredEnvVars = ["ANTHROPIC_API_KEY"];
3325
+ async install(provider, providerSandboxId) {
3326
+ const check = await provider.exec(providerSandboxId, "which claude 2>/dev/null || echo MISSING");
3327
+ if (check.stdout.trim() !== "MISSING")
3328
+ return;
3329
+ await provider.exec(providerSandboxId, "npm install -g @anthropic-ai/claude-code 2>&1 || sudo npm install -g @anthropic-ai/claude-code 2>&1");
3330
+ }
3331
+ async configure(provider, providerSandboxId, _envVars) {
3332
+ const config = JSON.stringify({
3333
+ hasCompletedOnboarding: true,
3334
+ hasTrustDialogAccepted: true,
3335
+ hasAcknowledgedCostThreshold: true
3336
+ });
3337
+ await provider.exec(providerSandboxId, `mkdir -p ~/.claude && echo '${config}' > ~/.claude.json`);
3338
+ }
3339
+ buildCommand(prompt) {
3340
+ return `claude --dangerously-skip-permissions -p ${JSON.stringify(prompt)}`;
3341
+ }
3342
+ }
3343
+
3344
+ // src/lib/agents/codex.ts
3345
+ class CodexDriver {
3346
+ name = "codex";
3347
+ requiredEnvVars = ["OPENAI_API_KEY"];
3348
+ async install(provider, providerSandboxId) {
3349
+ const check = await provider.exec(providerSandboxId, "which codex 2>/dev/null || echo MISSING");
3350
+ if (check.stdout.trim() !== "MISSING")
3351
+ return;
3352
+ await provider.exec(providerSandboxId, "npm install -g @openai/codex 2>&1 || sudo npm install -g @openai/codex 2>&1");
3353
+ }
3354
+ async configure(provider, providerSandboxId, _envVars) {
3355
+ const config = `[core]
3356
+ approvalMode = "full-auto"
3357
+ quiet = true
3358
+ `;
3359
+ await provider.exec(providerSandboxId, `mkdir -p ~/.codex && printf '${config.replace(/'/g, "'\\''")}' > ~/.codex/config.toml`);
3360
+ }
3361
+ buildCommand(prompt) {
3362
+ return `codex --approval-mode full-auto -q ${JSON.stringify(prompt)}`;
3363
+ }
3364
+ }
3365
+
3366
+ // src/lib/agents/gemini.ts
3367
+ class GeminiDriver {
3368
+ name = "gemini";
3369
+ requiredEnvVars = ["GEMINI_API_KEY"];
3370
+ async install(provider, providerSandboxId) {
3371
+ const check = await provider.exec(providerSandboxId, "which gemini 2>/dev/null || echo MISSING");
3372
+ if (check.stdout.trim() !== "MISSING")
3373
+ return;
3374
+ await provider.exec(providerSandboxId, "npm install -g @google/gemini-cli 2>&1 || sudo npm install -g @google/gemini-cli 2>&1");
3375
+ }
3376
+ async configure(provider, providerSandboxId, _envVars) {
3377
+ const settings = JSON.stringify({ theme: "Default", selectedAuthType: "gemini-api-key" });
3378
+ await provider.exec(providerSandboxId, `mkdir -p ~/.gemini && echo '${settings}' > ~/.gemini/settings.json`);
3379
+ }
3380
+ buildCommand(prompt) {
3381
+ return `gemini -p ${JSON.stringify(prompt)}`;
3382
+ }
3383
+ }
3384
+
3385
+ // src/lib/agents/opencode.ts
3386
+ class OpenCodeDriver {
3387
+ name = "opencode";
3388
+ requiredEnvVars = [];
3389
+ async install(provider, providerSandboxId) {
3390
+ const check = await provider.exec(providerSandboxId, "which opencode 2>/dev/null || echo MISSING");
3391
+ if (check.stdout.trim() !== "MISSING")
3392
+ return;
3393
+ await provider.exec(providerSandboxId, "npm install -g opencode-ai 2>&1 || sudo npm install -g opencode-ai 2>&1");
3394
+ }
3395
+ async configure(_provider, _providerSandboxId, _envVars) {}
3396
+ buildCommand(prompt) {
3397
+ return `opencode run ${JSON.stringify(prompt)}`;
3398
+ }
3399
+ }
3400
+
3401
+ // src/lib/agents/pi.ts
3402
+ class PiDriver {
3403
+ name = "pi";
3404
+ requiredEnvVars = ["PI_API_KEY"];
3405
+ async install(provider, providerSandboxId) {
3406
+ const check = await provider.exec(providerSandboxId, "which pi 2>/dev/null || echo MISSING");
3407
+ if (check.stdout.trim() !== "MISSING")
3408
+ return;
3409
+ await provider.exec(providerSandboxId, "npm install -g @pi-ai/cli 2>&1 || sudo npm install -g @pi-ai/cli 2>&1");
3410
+ }
3411
+ async configure(_provider, _providerSandboxId, _envVars) {}
3412
+ buildCommand(prompt) {
3413
+ return `pi ask ${JSON.stringify(prompt)}`;
3414
+ }
3415
+ }
3416
+
3417
+ // src/lib/agents/index.ts
3418
+ function getAgentDriver(name) {
3419
+ return DRIVER_MAP.get(name);
3420
+ }
3421
+ var DRIVERS, DRIVER_MAP;
3422
+ var init_agents = __esm(() => {
3423
+ DRIVERS = [
3424
+ new ClaudeDriver,
3425
+ new CodexDriver,
3426
+ new GeminiDriver,
3427
+ new OpenCodeDriver,
3428
+ new PiDriver
3429
+ ];
3430
+ DRIVER_MAP = new Map(DRIVERS.map((d) => [d.name, d]));
3431
+ });
3432
+
3275
3433
  // src/lib/agent-runner.ts
3276
3434
  var exports_agent_runner = {};
3277
3435
  __export(exports_agent_runner, {
3278
3436
  stopAgent: () => stopAgent,
3279
3437
  runAgent: () => runAgent
3280
3438
  });
3439
+ async function fireWebhook(url, payload) {
3440
+ try {
3441
+ await fetch(url, {
3442
+ method: "POST",
3443
+ headers: { "Content-Type": "application/json" },
3444
+ body: JSON.stringify(payload)
3445
+ });
3446
+ } catch {}
3447
+ }
3281
3448
  async function runAgent(sandboxId, opts) {
3282
3449
  const sandbox = getSandbox(sandboxId);
3283
3450
  if (!sandbox.provider_sandbox_id) {
3284
3451
  throw new Error("Sandbox has no provider instance");
3285
3452
  }
3286
- const cmd = opts.command || AGENT_COMMANDS[opts.agentType]?.(opts.prompt) || opts.prompt;
3453
+ const provider = await getProvider(sandbox.provider);
3454
+ const mergedEnv = { ...sandbox.env_vars, ...opts.callEnvVars };
3455
+ const env = Object.keys(mergedEnv).length > 0 ? mergedEnv : undefined;
3456
+ let cmd;
3457
+ const driver = opts.agentType !== "custom" ? getAgentDriver(opts.agentType) : undefined;
3458
+ if (opts.command) {
3459
+ cmd = opts.command;
3460
+ } else if (driver) {
3461
+ await driver.install(provider, sandbox.provider_sandbox_id);
3462
+ await driver.configure(provider, sandbox.provider_sandbox_id, sandbox.env_vars ?? {});
3463
+ cmd = driver.buildCommand(opts.prompt);
3464
+ } else {
3465
+ cmd = opts.prompt;
3466
+ }
3287
3467
  const session = createSession({
3288
3468
  sandbox_id: sandbox.id,
3289
3469
  agent_name: opts.agentName,
@@ -3291,9 +3471,19 @@ async function runAgent(sandboxId, opts) {
3291
3471
  command: cmd
3292
3472
  });
3293
3473
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} started: ${opts.prompt.slice(0, 100)}`);
3474
+ const startedAt = Date.now();
3475
+ const webhookEvents = opts.webhookEvents ?? ["start", "complete", "error"];
3476
+ if (opts.webhookUrl && webhookEvents.includes("start")) {
3477
+ fireWebhook(opts.webhookUrl, {
3478
+ event: "start",
3479
+ session_id: session.id,
3480
+ sandbox_id: sandbox.id,
3481
+ agent_type: opts.agentType,
3482
+ status: "running",
3483
+ timestamp: new Date().toISOString()
3484
+ });
3485
+ }
3294
3486
  const collector = createStreamCollector(sandbox.id, session.id);
3295
- const provider = await getProvider(sandbox.provider);
3296
- const env = Object.keys(sandbox.env_vars ?? {}).length > 0 ? sandbox.env_vars : undefined;
3297
3487
  provider.exec(sandbox.provider_sandbox_id, cmd, {
3298
3488
  onStdout: (data) => {
3299
3489
  collector.onStdout(data);
@@ -3309,9 +3499,32 @@ async function runAgent(sandboxId, opts) {
3309
3499
  const status = exitResult.exit_code === 0 ? "completed" : "failed";
3310
3500
  endSession(session.id, exitResult.exit_code ?? 0, status);
3311
3501
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} finished with exit code ${exitResult.exit_code}`);
3502
+ if (opts.webhookUrl && webhookEvents.includes("complete")) {
3503
+ fireWebhook(opts.webhookUrl, {
3504
+ event: "complete",
3505
+ session_id: session.id,
3506
+ sandbox_id: sandbox.id,
3507
+ agent_type: opts.agentType,
3508
+ status,
3509
+ exit_code: exitResult.exit_code,
3510
+ duration_ms: Date.now() - startedAt,
3511
+ timestamp: new Date().toISOString()
3512
+ });
3513
+ }
3312
3514
  }).catch((err) => {
3313
3515
  endSession(session.id, 1, "failed");
3314
3516
  emitLifecycleEvent(sandbox.id, `Agent ${opts.agentType} failed: ${err.message}`);
3517
+ if (opts.webhookUrl && webhookEvents.includes("error")) {
3518
+ fireWebhook(opts.webhookUrl, {
3519
+ event: "error",
3520
+ session_id: session.id,
3521
+ sandbox_id: sandbox.id,
3522
+ agent_type: opts.agentType,
3523
+ status: "failed",
3524
+ duration_ms: Date.now() - startedAt,
3525
+ timestamp: new Date().toISOString()
3526
+ });
3527
+ }
3315
3528
  });
3316
3529
  return session;
3317
3530
  }
@@ -3321,22 +3534,16 @@ async function stopAgent(sandboxId) {
3321
3534
  return;
3322
3535
  const provider = await getProvider(sandbox.provider);
3323
3536
  try {
3324
- await provider.exec(sandbox.provider_sandbox_id, "pkill -f 'claude\\|codex\\|gemini' || true");
3537
+ await provider.exec(sandbox.provider_sandbox_id, "pkill -f 'claude\\|codex\\|gemini\\|opencode\\|pi' || true");
3325
3538
  } catch {}
3326
3539
  emitLifecycleEvent(sandbox.id, "Agent stopped by user");
3327
3540
  }
3328
- var CLAUDE_ONBOARDING_SETUP, AGENT_COMMANDS;
3329
3541
  var init_agent_runner = __esm(() => {
3330
3542
  init_sandboxes();
3331
3543
  init_sessions();
3332
3544
  init_providers();
3333
3545
  init_stream();
3334
- CLAUDE_ONBOARDING_SETUP = `mkdir -p ~/.claude && ` + `echo '{"hasCompletedOnboarding":true,"hasTrustDialogAccepted":true,"hasAcknowledgedCostThreshold":true}' > ~/.claude.json`;
3335
- AGENT_COMMANDS = {
3336
- claude: (prompt) => `${CLAUDE_ONBOARDING_SETUP} && claude --dangerously-skip-permissions -p ${JSON.stringify(prompt)}`,
3337
- codex: (prompt) => `codex -q ${JSON.stringify(prompt)}`,
3338
- gemini: (prompt) => `gemini -p ${JSON.stringify(prompt)}`
3339
- };
3546
+ init_agents();
3340
3547
  });
3341
3548
 
3342
3549
  // node_modules/commander/esm.mjs
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAwKtC,wBAAgB,WAAW,IAAI,QAAQ,CAYtC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAcf"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2MtC,wBAAgB,WAAW,IAAI,QAAQ,CAYtC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAcf"}
@@ -0,0 +1,29 @@
1
+ export interface Snapshot {
2
+ id: string;
3
+ sandbox_id: string;
4
+ provider_sandbox_id: string;
5
+ provider: string;
6
+ name: string | null;
7
+ created_at: string;
8
+ }
9
+ export interface SnapshotRow {
10
+ id: string;
11
+ sandbox_id: string;
12
+ provider_sandbox_id: string;
13
+ provider: string;
14
+ name: string | null;
15
+ created_at: string;
16
+ }
17
+ export declare class SnapshotNotFoundError extends Error {
18
+ constructor(id: string);
19
+ }
20
+ export declare function createSnapshot(input: {
21
+ sandbox_id: string;
22
+ provider_sandbox_id: string;
23
+ provider: string;
24
+ name?: string;
25
+ }): Snapshot;
26
+ export declare function getSnapshot(id: string): Snapshot;
27
+ export declare function listSnapshots(sandboxId?: string): Snapshot[];
28
+ export declare function deleteSnapshot(id: string): void;
29
+ //# sourceMappingURL=snapshots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../src/db/snapshots.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,EAAE,EAAE,MAAM;CAIvB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,QAAQ,CASX;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAOhD;AAED,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAM5D;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAK/C"}
package/dist/index.d.ts CHANGED
@@ -7,8 +7,13 @@ export { registerAgent, getAgent, getAgentByName, listAgents, deleteAgent } from
7
7
  export { createProject, getProject, getProjectByPath, listProjects, ensureProject, deleteProject } from "./db/projects.js";
8
8
  export { createWebhook, getWebhook, listWebhooks, deleteWebhook } from "./db/webhooks.js";
9
9
  export { createTemplate, getTemplate, getTemplateByName, listTemplates, deleteTemplate } from "./db/templates.js";
10
+ export { createSnapshot, getSnapshot, listSnapshots, deleteSnapshot } from "./db/snapshots.js";
11
+ export type { Snapshot, SnapshotRow } from "./db/snapshots.js";
12
+ export { SnapshotNotFoundError } from "./db/snapshots.js";
10
13
  export { loadConfig, saveConfig, getDefaultProvider, getDefaultTimeout, getDefaultImage, getProviderApiKey, setConfigValue, getConfigValue } from "./lib/config.js";
11
14
  export { getProvider } from "./providers/index.js";
12
15
  export type { SandboxProvider, ProviderSandbox, CreateSandboxOpts, ExecOptions } from "./providers/types.js";
13
16
  export { createStreamCollector, addStreamListener, emitLifecycleEvent } from "./lib/stream.js";
17
+ export { getAgentDriver, listAgentDrivers } from "./lib/agents/index.js";
18
+ export type { AgentDriver } from "./lib/agents/types.js";
14
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3H,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGlH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpK,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAG7G,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3H,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC/F,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpK,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAG7G,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG/F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ var init_types = __esm(() => {
45
45
  "failed",
46
46
  "killed"
47
47
  ];
48
- AGENT_TYPES = ["claude", "codex", "gemini", "custom"];
48
+ AGENT_TYPES = ["claude", "codex", "gemini", "opencode", "pi", "custom"];
49
49
  EVENT_TYPES = [
50
50
  "stdout",
51
51
  "stderr",
@@ -244,6 +244,15 @@ class E2BProvider {
244
244
  throw new ProviderError("e2b", `Failed to resume sandbox: ${err.message}`);
245
245
  }
246
246
  }
247
+ async getPublicUrl(sandboxId, port, _protocol) {
248
+ const sandbox = await this.getInstance(sandboxId);
249
+ try {
250
+ const host = sandbox.getHost(port);
251
+ return `https://${host}`;
252
+ } catch (err) {
253
+ throw new ProviderError("e2b", `Failed to get public URL for port ${port}: ${err.message}`);
254
+ }
255
+ }
247
256
  async keepAlive(sandboxId, durationMs) {
248
257
  const sandbox = await this.getInstance(sandboxId);
249
258
  try {
@@ -404,6 +413,9 @@ class DaytonaProvider {
404
413
  throw new ProviderError("daytona", `Failed to delete sandbox: ${err.message}`);
405
414
  }
406
415
  }
416
+ async getPublicUrl(_sandboxId, _port, _protocol) {
417
+ throw new ProviderError("daytona", "Port forwarding not supported by Daytona provider");
418
+ }
407
419
  async pause(_sandboxId) {
408
420
  throw new ProviderError("daytona", "Pause/resume not supported by Daytona provider");
409
421
  }
@@ -632,6 +644,9 @@ class ModalProvider {
632
644
  async delete(sandboxId) {
633
645
  await this.stop(sandboxId);
634
646
  }
647
+ async getPublicUrl(_sandboxId, _port, _protocol) {
648
+ throw new ProviderError("modal", "Port forwarding not supported by Modal provider");
649
+ }
635
650
  async pause(_sandboxId) {
636
651
  throw new ProviderError("modal", "Pause/resume not supported by Modal provider");
637
652
  }
@@ -829,6 +844,37 @@ CREATE TABLE IF NOT EXISTS templates (
829
844
  CREATE INDEX IF NOT EXISTS idx_templates_name ON templates(name);
830
845
 
831
846
  INSERT OR IGNORE INTO _migrations (id) VALUES (2);
847
+ `,
848
+ `
849
+ CREATE TABLE IF NOT EXISTS sandbox_sessions_new (
850
+ id TEXT PRIMARY KEY,
851
+ sandbox_id TEXT NOT NULL REFERENCES sandboxes(id) ON DELETE CASCADE,
852
+ agent_name TEXT,
853
+ agent_type TEXT,
854
+ command TEXT,
855
+ status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed', 'killed')),
856
+ exit_code INTEGER,
857
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
858
+ ended_at TEXT
859
+ );
860
+ INSERT INTO sandbox_sessions_new SELECT * FROM sandbox_sessions;
861
+ DROP TABLE sandbox_sessions;
862
+ ALTER TABLE sandbox_sessions_new RENAME TO sandbox_sessions;
863
+ CREATE INDEX IF NOT EXISTS idx_sessions_sandbox ON sandbox_sessions(sandbox_id);
864
+ CREATE INDEX IF NOT EXISTS idx_sessions_status ON sandbox_sessions(status);
865
+ INSERT OR IGNORE INTO _migrations (id) VALUES (3);
866
+ `,
867
+ `
868
+ CREATE TABLE IF NOT EXISTS snapshots (
869
+ id TEXT PRIMARY KEY,
870
+ sandbox_id TEXT NOT NULL,
871
+ provider_sandbox_id TEXT NOT NULL,
872
+ provider TEXT NOT NULL,
873
+ name TEXT,
874
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
875
+ );
876
+ CREATE INDEX IF NOT EXISTS idx_snapshots_sandbox ON snapshots(sandbox_id);
877
+ INSERT OR IGNORE INTO _migrations (id) VALUES (4);
832
878
  `
833
879
  ];
834
880
  var db = null;
@@ -1324,6 +1370,45 @@ function deleteTemplate(id) {
1324
1370
  throw new TemplateNotFoundError(id);
1325
1371
  db2.query("DELETE FROM templates WHERE id = ?").run(resolvedId);
1326
1372
  }
1373
+ // src/db/snapshots.ts
1374
+ class SnapshotNotFoundError extends Error {
1375
+ constructor(id) {
1376
+ super(`Snapshot not found: ${id}`);
1377
+ this.name = "SnapshotNotFoundError";
1378
+ }
1379
+ }
1380
+ function createSnapshot(input) {
1381
+ const db2 = getDatabase();
1382
+ const id = uuid();
1383
+ const timestamp = now();
1384
+ db2.query(`INSERT INTO snapshots (id, sandbox_id, provider_sandbox_id, provider, name, created_at)
1385
+ VALUES (?, ?, ?, ?, ?, ?)`).run(id, input.sandbox_id, input.provider_sandbox_id, input.provider, input.name ?? null, timestamp);
1386
+ return getSnapshot(id);
1387
+ }
1388
+ function getSnapshot(id) {
1389
+ const db2 = getDatabase();
1390
+ const resolvedId = resolvePartialId("snapshots", id);
1391
+ if (!resolvedId)
1392
+ throw new SnapshotNotFoundError(id);
1393
+ const row = db2.query("SELECT * FROM snapshots WHERE id = ?").get(resolvedId);
1394
+ if (!row)
1395
+ throw new SnapshotNotFoundError(id);
1396
+ return row;
1397
+ }
1398
+ function listSnapshots(sandboxId) {
1399
+ const db2 = getDatabase();
1400
+ if (sandboxId) {
1401
+ return db2.query("SELECT * FROM snapshots WHERE sandbox_id = ? ORDER BY created_at DESC").all(sandboxId);
1402
+ }
1403
+ return db2.query("SELECT * FROM snapshots ORDER BY created_at DESC").all();
1404
+ }
1405
+ function deleteSnapshot(id) {
1406
+ const db2 = getDatabase();
1407
+ const resolvedId = resolvePartialId("snapshots", id);
1408
+ if (!resolvedId)
1409
+ throw new SnapshotNotFoundError(id);
1410
+ db2.query("DELETE FROM snapshots WHERE id = ?").run(resolvedId);
1411
+ }
1327
1412
  // src/lib/config.ts
1328
1413
  import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
1329
1414
  import { dirname as dirname2, join as join2 } from "path";
@@ -1518,6 +1603,117 @@ function emitLifecycleEvent(sandboxId, message) {
1518
1603
  });
1519
1604
  notifyListeners(sandboxId, "lifecycle", message);
1520
1605
  }
1606
+ // src/lib/agents/claude.ts
1607
+ class ClaudeDriver {
1608
+ name = "claude";
1609
+ requiredEnvVars = ["ANTHROPIC_API_KEY"];
1610
+ async install(provider, providerSandboxId) {
1611
+ const check = await provider.exec(providerSandboxId, "which claude 2>/dev/null || echo MISSING");
1612
+ if (check.stdout.trim() !== "MISSING")
1613
+ return;
1614
+ await provider.exec(providerSandboxId, "npm install -g @anthropic-ai/claude-code 2>&1 || sudo npm install -g @anthropic-ai/claude-code 2>&1");
1615
+ }
1616
+ async configure(provider, providerSandboxId, _envVars) {
1617
+ const config = JSON.stringify({
1618
+ hasCompletedOnboarding: true,
1619
+ hasTrustDialogAccepted: true,
1620
+ hasAcknowledgedCostThreshold: true
1621
+ });
1622
+ await provider.exec(providerSandboxId, `mkdir -p ~/.claude && echo '${config}' > ~/.claude.json`);
1623
+ }
1624
+ buildCommand(prompt) {
1625
+ return `claude --dangerously-skip-permissions -p ${JSON.stringify(prompt)}`;
1626
+ }
1627
+ }
1628
+
1629
+ // src/lib/agents/codex.ts
1630
+ class CodexDriver {
1631
+ name = "codex";
1632
+ requiredEnvVars = ["OPENAI_API_KEY"];
1633
+ async install(provider, providerSandboxId) {
1634
+ const check = await provider.exec(providerSandboxId, "which codex 2>/dev/null || echo MISSING");
1635
+ if (check.stdout.trim() !== "MISSING")
1636
+ return;
1637
+ await provider.exec(providerSandboxId, "npm install -g @openai/codex 2>&1 || sudo npm install -g @openai/codex 2>&1");
1638
+ }
1639
+ async configure(provider, providerSandboxId, _envVars) {
1640
+ const config = `[core]
1641
+ approvalMode = "full-auto"
1642
+ quiet = true
1643
+ `;
1644
+ await provider.exec(providerSandboxId, `mkdir -p ~/.codex && printf '${config.replace(/'/g, "'\\''")}' > ~/.codex/config.toml`);
1645
+ }
1646
+ buildCommand(prompt) {
1647
+ return `codex --approval-mode full-auto -q ${JSON.stringify(prompt)}`;
1648
+ }
1649
+ }
1650
+
1651
+ // src/lib/agents/gemini.ts
1652
+ class GeminiDriver {
1653
+ name = "gemini";
1654
+ requiredEnvVars = ["GEMINI_API_KEY"];
1655
+ async install(provider, providerSandboxId) {
1656
+ const check = await provider.exec(providerSandboxId, "which gemini 2>/dev/null || echo MISSING");
1657
+ if (check.stdout.trim() !== "MISSING")
1658
+ return;
1659
+ await provider.exec(providerSandboxId, "npm install -g @google/gemini-cli 2>&1 || sudo npm install -g @google/gemini-cli 2>&1");
1660
+ }
1661
+ async configure(provider, providerSandboxId, _envVars) {
1662
+ const settings = JSON.stringify({ theme: "Default", selectedAuthType: "gemini-api-key" });
1663
+ await provider.exec(providerSandboxId, `mkdir -p ~/.gemini && echo '${settings}' > ~/.gemini/settings.json`);
1664
+ }
1665
+ buildCommand(prompt) {
1666
+ return `gemini -p ${JSON.stringify(prompt)}`;
1667
+ }
1668
+ }
1669
+
1670
+ // src/lib/agents/opencode.ts
1671
+ class OpenCodeDriver {
1672
+ name = "opencode";
1673
+ requiredEnvVars = [];
1674
+ async install(provider, providerSandboxId) {
1675
+ const check = await provider.exec(providerSandboxId, "which opencode 2>/dev/null || echo MISSING");
1676
+ if (check.stdout.trim() !== "MISSING")
1677
+ return;
1678
+ await provider.exec(providerSandboxId, "npm install -g opencode-ai 2>&1 || sudo npm install -g opencode-ai 2>&1");
1679
+ }
1680
+ async configure(_provider, _providerSandboxId, _envVars) {}
1681
+ buildCommand(prompt) {
1682
+ return `opencode run ${JSON.stringify(prompt)}`;
1683
+ }
1684
+ }
1685
+
1686
+ // src/lib/agents/pi.ts
1687
+ class PiDriver {
1688
+ name = "pi";
1689
+ requiredEnvVars = ["PI_API_KEY"];
1690
+ async install(provider, providerSandboxId) {
1691
+ const check = await provider.exec(providerSandboxId, "which pi 2>/dev/null || echo MISSING");
1692
+ if (check.stdout.trim() !== "MISSING")
1693
+ return;
1694
+ await provider.exec(providerSandboxId, "npm install -g @pi-ai/cli 2>&1 || sudo npm install -g @pi-ai/cli 2>&1");
1695
+ }
1696
+ async configure(_provider, _providerSandboxId, _envVars) {}
1697
+ buildCommand(prompt) {
1698
+ return `pi ask ${JSON.stringify(prompt)}`;
1699
+ }
1700
+ }
1701
+
1702
+ // src/lib/agents/index.ts
1703
+ var DRIVERS = [
1704
+ new ClaudeDriver,
1705
+ new CodexDriver,
1706
+ new GeminiDriver,
1707
+ new OpenCodeDriver,
1708
+ new PiDriver
1709
+ ];
1710
+ var DRIVER_MAP = new Map(DRIVERS.map((d) => [d.name, d]));
1711
+ function getAgentDriver(name) {
1712
+ return DRIVER_MAP.get(name);
1713
+ }
1714
+ function listAgentDrivers() {
1715
+ return DRIVERS;
1716
+ }
1521
1717
  export {
1522
1718
  uuid,
1523
1719
  updateSession,
@@ -1532,14 +1728,17 @@ export {
1532
1728
  loadConfig,
1533
1729
  listWebhooks,
1534
1730
  listTemplates,
1731
+ listSnapshots,
1535
1732
  listSessions,
1536
1733
  listSandboxes,
1537
1734
  listProjects,
1538
1735
  listEvents,
1539
1736
  listAgents,
1737
+ listAgentDrivers,
1540
1738
  getWebhook,
1541
1739
  getTemplateByName,
1542
1740
  getTemplate,
1741
+ getSnapshot,
1543
1742
  getSession,
1544
1743
  getSandbox,
1545
1744
  getProviderApiKey,
@@ -1551,6 +1750,7 @@ export {
1551
1750
  getDefaultImage,
1552
1751
  getDatabase,
1553
1752
  getConfigValue,
1753
+ getAgentDriver,
1554
1754
  getAgentByName,
1555
1755
  getAgent,
1556
1756
  ensureProject,
@@ -1558,12 +1758,14 @@ export {
1558
1758
  emitLifecycleEvent,
1559
1759
  deleteWebhook,
1560
1760
  deleteTemplate,
1761
+ deleteSnapshot,
1561
1762
  deleteSandbox,
1562
1763
  deleteProject,
1563
1764
  deleteAgent,
1564
1765
  createWebhook,
1565
1766
  createTemplate,
1566
1767
  createStreamCollector,
1768
+ createSnapshot,
1567
1769
  createSession,
1568
1770
  createSandbox,
1569
1771
  createProject,
@@ -1572,6 +1774,7 @@ export {
1572
1774
  addEvent,
1573
1775
  WebhookNotFoundError,
1574
1776
  TemplateNotFoundError,
1777
+ SnapshotNotFoundError,
1575
1778
  SessionNotFoundError,
1576
1779
  SandboxNotFoundError,
1577
1780
  SESSION_STATUSES,
@@ -6,6 +6,9 @@ export interface RunAgentOpts {
6
6
  command?: string;
7
7
  onStdout?: (data: string) => void;
8
8
  onStderr?: (data: string) => void;
9
+ callEnvVars?: Record<string, string>;
10
+ webhookUrl?: string;
11
+ webhookEvents?: ('start' | 'complete' | 'error')[];
9
12
  }
10
13
  export declare function runAgent(sandboxId: string, opts: RunAgentOpts): Promise<SandboxSession>;
11
14
  export declare function stopAgent(sandboxId: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../src/lib/agent-runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AAc/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;CACnC;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAkDzB;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAehE"}
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"}