@computesdk/workbench 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,38 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
2
11
 
3
12
  // ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/esm_shims.js
4
13
  import path from "path";
5
14
  import { fileURLToPath } from "url";
6
- var getFilename = () => fileURLToPath(import.meta.url);
7
- var getDirname = () => path.dirname(getFilename());
8
- var __dirname = /* @__PURE__ */ getDirname();
9
-
10
- // src/bin/workbench.ts
11
- import { config } from "dotenv";
15
+ var getFilename, getDirname, __dirname;
16
+ var init_esm_shims = __esm({
17
+ "../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/esm_shims.js"() {
18
+ "use strict";
19
+ getFilename = () => fileURLToPath(import.meta.url);
20
+ getDirname = () => path.dirname(getFilename());
21
+ __dirname = /* @__PURE__ */ getDirname();
22
+ }
23
+ });
12
24
 
13
25
  // src/cli/state.ts
26
+ var state_exports = {};
27
+ __export(state_exports, {
28
+ clearSandbox: () => clearSandbox,
29
+ createState: () => createState,
30
+ formatUptime: () => formatUptime,
31
+ getCurrentSandbox: () => getCurrentSandbox,
32
+ getUptimeSeconds: () => getUptimeSeconds,
33
+ hasSandbox: () => hasSandbox,
34
+ setSandbox: () => setSandbox
35
+ });
14
36
  function createState() {
15
37
  return {
16
38
  currentProvider: null,
@@ -19,7 +41,8 @@ function createState() {
19
41
  availableProviders: [],
20
42
  useDirectMode: false,
21
43
  // Default to gateway mode
22
- verbose: false
44
+ verbose: false,
45
+ compute: null
23
46
  };
24
47
  }
25
48
  function getCurrentSandbox(state) {
@@ -73,36 +96,28 @@ function formatUptime(state) {
73
96
  const remainingMinutes = minutes % 60;
74
97
  return `${hours}h ${remainingMinutes}m`;
75
98
  }
76
-
77
- // src/cli/repl.ts
78
- import * as repl from "repl";
79
- import * as cmd from "@computesdk/cmd";
80
-
81
- // src/cli/commands.ts
82
- import { createCompute } from "computesdk";
99
+ var init_state = __esm({
100
+ "src/cli/state.ts"() {
101
+ "use strict";
102
+ init_esm_shims();
103
+ }
104
+ });
83
105
 
84
106
  // src/cli/output.ts
85
- var colors = {
86
- reset: "\x1B[0m",
87
- bright: "\x1B[1m",
88
- dim: "\x1B[2m",
89
- cyan: "\x1B[36m",
90
- green: "\x1B[32m",
91
- yellow: "\x1B[33m",
92
- red: "\x1B[31m",
93
- blue: "\x1B[34m",
94
- magenta: "\x1B[35m"
95
- };
96
- var c = {
97
- bold: (text) => `${colors.bright}${text}${colors.reset}`,
98
- dim: (text) => `${colors.dim}${text}${colors.reset}`,
99
- cyan: (text) => `${colors.cyan}${text}${colors.reset}`,
100
- green: (text) => `${colors.green}${text}${colors.reset}`,
101
- yellow: (text) => `${colors.yellow}${text}${colors.reset}`,
102
- red: (text) => `${colors.red}${text}${colors.reset}`,
103
- blue: (text) => `${colors.blue}${text}${colors.reset}`,
104
- magenta: (text) => `${colors.magenta}${text}${colors.reset}`
105
- };
107
+ var output_exports = {};
108
+ __export(output_exports, {
109
+ Spinner: () => Spinner,
110
+ c: () => c,
111
+ formatDuration: () => formatDuration,
112
+ logCommand: () => logCommand,
113
+ logError: () => logError,
114
+ logInfo: () => logInfo,
115
+ logSuccess: () => logSuccess,
116
+ logWarning: () => logWarning,
117
+ showHelp: () => showHelp,
118
+ showInfo: () => showInfo,
119
+ showWelcome: () => showWelcome
120
+ });
106
121
  function showWelcome(availableProviders, currentProvider, useDirectMode) {
107
122
  console.log(c.bold(c.cyan("\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")));
108
123
  console.log(c.bold(c.cyan("\u2551 ComputeSDK Workbench \u2551")));
@@ -151,39 +166,6 @@ function showInfo(state) {
151
166
  console.log(` Uptime: ${formatUptime(state)}`);
152
167
  console.log("");
153
168
  }
154
- var Spinner = class {
155
- constructor(text) {
156
- this.interval = null;
157
- this.frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
158
- this.currentFrame = 0;
159
- this.text = text;
160
- }
161
- start() {
162
- process.stdout.write("\x1B[?25l");
163
- this.interval = setInterval(() => {
164
- const frame = this.frames[this.currentFrame];
165
- process.stdout.write(`\r${c.cyan(frame)} ${this.text}`);
166
- this.currentFrame = (this.currentFrame + 1) % this.frames.length;
167
- }, 80);
168
- return this;
169
- }
170
- succeed(text) {
171
- this.stop();
172
- console.log(`${c.green("\u2705")} ${text || this.text}`);
173
- }
174
- fail(text) {
175
- this.stop();
176
- console.log(`${c.red("\u274C")} ${text || this.text}`);
177
- }
178
- stop() {
179
- if (this.interval) {
180
- clearInterval(this.interval);
181
- this.interval = null;
182
- }
183
- process.stdout.write("\r\x1B[K");
184
- process.stdout.write("\x1B[?25h");
185
- }
186
- };
187
169
  function formatDuration(ms) {
188
170
  const seconds = ms / 1e3;
189
171
  if (seconds < 1) {
@@ -222,6 +204,9 @@ ${c.bold("Provider Modes:")}
222
204
  ${c.bold("Sandbox Management:")}
223
205
  ${c.cyan("restart")} Restart current sandbox
224
206
  ${c.cyan("destroy")} Destroy current sandbox
207
+ ${c.cyan("connect <url> [token]")} Connect to existing sandbox via URL
208
+ ${c.dim("Example: connect https://sandbox-123.localhost:8080")}
209
+ ${c.dim("Example: connect https://sandbox-123.localhost:8080 your_token")}
225
210
  ${c.cyan("info")} Show sandbox info (provider, uptime)
226
211
 
227
212
  ${c.bold("Environment:")}
@@ -257,6 +242,20 @@ ${c.bold("Running Commands:")}
257
242
  ${c.cyan('filesystem.exists("/path")')}
258
243
  ${c.cyan('filesystem.remove("/file")')}
259
244
 
245
+ ${c.dim("Named Sandboxes (gateway mode only):")}
246
+ ${c.cyan("create()")} ${c.dim("// Create & switch to new sandbox")}
247
+ ${c.cyan('create({ namespace: "h" })')} ${c.dim("// Create with namespace & switch")}
248
+ ${c.cyan('findOrCreate({ name: "my-app" })')} ${c.dim("// Find or create & switch")}
249
+ ${c.cyan('find({ name: "my-app" })')} ${c.dim("// Find existing & switch")}
250
+
251
+ ${c.dim("Note: Prompts before switching if you already have an active sandbox")}
252
+
253
+ ${c.dim("Child Sandboxes (gateway mode only):")}
254
+ ${c.cyan("child.create()")} ${c.dim("// Create child sandbox")}
255
+ ${c.cyan("child.list()")} ${c.dim("// List all children")}
256
+ ${c.cyan('child.retrieve("sandbox-id")')} ${c.dim("// Get child info")}
257
+ ${c.cyan('child.destroy("sandbox-id")')} ${c.dim("// Delete child")}
258
+
260
259
  ${c.dim("Sandbox Methods:")}
261
260
  ${c.cyan("getUrl({ port: 3000 })")} ${c.dim("// Get public URL")}
262
261
  ${c.cyan(`runCode("console.log('hi')", "node")`)}
@@ -292,6 +291,71 @@ function logError(message) {
292
291
  function logWarning(message) {
293
292
  console.log(c.yellow(`\u26A0\uFE0F ${message}`));
294
293
  }
294
+ function logInfo(message) {
295
+ console.log(c.blue(`\u2139\uFE0F ${message}`));
296
+ }
297
+ var colors, c, Spinner;
298
+ var init_output = __esm({
299
+ "src/cli/output.ts"() {
300
+ "use strict";
301
+ init_esm_shims();
302
+ init_state();
303
+ colors = {
304
+ reset: "\x1B[0m",
305
+ bright: "\x1B[1m",
306
+ dim: "\x1B[2m",
307
+ cyan: "\x1B[36m",
308
+ green: "\x1B[32m",
309
+ yellow: "\x1B[33m",
310
+ red: "\x1B[31m",
311
+ blue: "\x1B[34m",
312
+ magenta: "\x1B[35m"
313
+ };
314
+ c = {
315
+ bold: (text) => `${colors.bright}${text}${colors.reset}`,
316
+ dim: (text) => `${colors.dim}${text}${colors.reset}`,
317
+ cyan: (text) => `${colors.cyan}${text}${colors.reset}`,
318
+ green: (text) => `${colors.green}${text}${colors.reset}`,
319
+ yellow: (text) => `${colors.yellow}${text}${colors.reset}`,
320
+ red: (text) => `${colors.red}${text}${colors.reset}`,
321
+ blue: (text) => `${colors.blue}${text}${colors.reset}`,
322
+ magenta: (text) => `${colors.magenta}${text}${colors.reset}`
323
+ };
324
+ Spinner = class {
325
+ constructor(text) {
326
+ this.interval = null;
327
+ this.frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
328
+ this.currentFrame = 0;
329
+ this.text = text;
330
+ }
331
+ start() {
332
+ process.stdout.write("\x1B[?25l");
333
+ this.interval = setInterval(() => {
334
+ const frame = this.frames[this.currentFrame];
335
+ process.stdout.write(`\r${c.cyan(frame)} ${this.text}`);
336
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
337
+ }, 80);
338
+ return this;
339
+ }
340
+ succeed(text) {
341
+ this.stop();
342
+ console.log(`${c.green("\u2705")} ${text || this.text}`);
343
+ }
344
+ fail(text) {
345
+ this.stop();
346
+ console.log(`${c.red("\u274C")} ${text || this.text}`);
347
+ }
348
+ stop() {
349
+ if (this.interval) {
350
+ clearInterval(this.interval);
351
+ this.interval = null;
352
+ }
353
+ process.stdout.write("\r\x1B[K");
354
+ process.stdout.write("\x1B[?25h");
355
+ }
356
+ };
357
+ }
358
+ });
295
359
 
296
360
  // src/cli/providers.ts
297
361
  import {
@@ -299,14 +363,6 @@ import {
299
363
  PROVIDER_NAMES as SHARED_PROVIDER_NAMES,
300
364
  getProviderConfigFromEnv
301
365
  } from "computesdk";
302
- var PROVIDER_NAMES = [
303
- "gateway",
304
- ...SHARED_PROVIDER_NAMES
305
- ];
306
- var PROVIDER_AUTH = {
307
- gateway: [["COMPUTESDK_API_KEY"]],
308
- ...SHARED_PROVIDER_AUTH
309
- };
310
366
  function getProviderStatus(provider) {
311
367
  const authOptions = PROVIDER_AUTH[provider];
312
368
  if (typeof process === "undefined") {
@@ -488,29 +544,136 @@ function getProviderConfig(providerName) {
488
544
  }
489
545
  return getProviderConfigFromEnv(providerName);
490
546
  }
547
+ var PROVIDER_NAMES, PROVIDER_AUTH;
548
+ var init_providers = __esm({
549
+ "src/cli/providers.ts"() {
550
+ "use strict";
551
+ init_esm_shims();
552
+ init_output();
553
+ PROVIDER_NAMES = [
554
+ "gateway",
555
+ ...SHARED_PROVIDER_NAMES
556
+ ];
557
+ PROVIDER_AUTH = {
558
+ gateway: [["COMPUTESDK_API_KEY"]],
559
+ ...SHARED_PROVIDER_AUTH
560
+ };
561
+ }
562
+ });
491
563
 
492
564
  // src/cli/commands.ts
565
+ var commands_exports = {};
566
+ __export(commands_exports, {
567
+ cleanupOnExit: () => cleanupOnExit,
568
+ confirmSandboxSwitch: () => confirmSandboxSwitch,
569
+ connectToSandbox: () => connectToSandbox,
570
+ createProviderCommand: () => createProviderCommand,
571
+ createSandbox: () => createSandbox,
572
+ destroySandbox: () => destroySandbox,
573
+ ensureSandbox: () => ensureSandbox,
574
+ getComputeInstance: () => getComputeInstance,
575
+ restartSandbox: () => restartSandbox,
576
+ runCommand: () => runCommand,
577
+ showMode: () => showMode,
578
+ showVerbose: () => showVerbose,
579
+ switchProvider: () => switchProvider,
580
+ toggleMode: () => toggleMode,
581
+ toggleVerbose: () => toggleVerbose
582
+ });
583
+ import { createCompute } from "computesdk";
493
584
  import * as readline from "readline";
494
- async function confirm(question) {
585
+ async function confirm(question, defaultYes = false) {
495
586
  return new Promise((resolve) => {
496
587
  const rl = readline.createInterface({
497
588
  input: process.stdin,
498
589
  output: process.stdout
499
590
  });
500
591
  process.stdin.resume();
501
- rl.question(`${question} (y/N): `, (answer) => {
592
+ const promptSuffix = defaultYes ? "(Y/n)" : "(y/N)";
593
+ rl.question(`${question} ${promptSuffix}: `, (answer) => {
502
594
  rl.close();
503
595
  const trimmed = answer.trim().toLowerCase();
504
- resolve(trimmed === "y" || trimmed === "yes");
596
+ if (trimmed === "") {
597
+ resolve(defaultYes);
598
+ } else {
599
+ resolve(trimmed === "y" || trimmed === "yes");
600
+ }
505
601
  });
506
602
  });
507
603
  }
604
+ async function confirmSandboxSwitch(state) {
605
+ if (!hasSandbox(state)) {
606
+ return true;
607
+ }
608
+ return await confirm("Switch to new sandbox?", true);
609
+ }
508
610
  async function ensureSandbox(state) {
509
611
  if (hasSandbox(state)) {
510
612
  return;
511
613
  }
512
614
  await createSandbox(state);
513
615
  }
616
+ async function getComputeInstance(state) {
617
+ if (state.compute) {
618
+ return state.compute;
619
+ }
620
+ const providerName = state.currentProvider || autoDetectProvider(false);
621
+ const useDirect = state.useDirectMode;
622
+ if (!providerName) {
623
+ throw new Error("No provider configured.");
624
+ }
625
+ let compute2;
626
+ if (useDirect) {
627
+ const providerModule = await loadProvider(providerName);
628
+ const providerFactory = providerModule[providerName];
629
+ if (!providerFactory) {
630
+ throw new Error(`Provider ${providerName} does not export a factory function`);
631
+ }
632
+ const config2 = getProviderConfig(providerName);
633
+ compute2 = createCompute({
634
+ defaultProvider: providerFactory(config2)
635
+ });
636
+ } else {
637
+ const gatewayModule = await import("computesdk");
638
+ const gatewayFactory = gatewayModule.gateway;
639
+ const providerConfig = getProviderConfig(providerName);
640
+ const providerHeaders = {};
641
+ switch (providerName) {
642
+ case "e2b":
643
+ if (providerConfig.apiKey) providerHeaders["X-E2B-API-Key"] = providerConfig.apiKey;
644
+ break;
645
+ case "railway":
646
+ if (providerConfig.apiKey) providerHeaders["X-Railway-API-Key"] = providerConfig.apiKey;
647
+ if (providerConfig.projectId) providerHeaders["X-Railway-Project-ID"] = providerConfig.projectId;
648
+ if (providerConfig.environmentId) providerHeaders["X-Railway-Environment-ID"] = providerConfig.environmentId;
649
+ break;
650
+ case "daytona":
651
+ if (providerConfig.apiKey) providerHeaders["X-Daytona-API-Key"] = providerConfig.apiKey;
652
+ break;
653
+ case "modal":
654
+ if (providerConfig.tokenId) providerHeaders["X-Modal-Token-ID"] = providerConfig.tokenId;
655
+ if (providerConfig.tokenSecret) providerHeaders["X-Modal-Token-Secret"] = providerConfig.tokenSecret;
656
+ break;
657
+ case "vercel":
658
+ if (providerConfig.token) providerHeaders["X-Vercel-Token"] = providerConfig.token;
659
+ if (providerConfig.teamId) providerHeaders["X-Vercel-Team-ID"] = providerConfig.teamId;
660
+ if (providerConfig.projectId) providerHeaders["X-Vercel-Project-ID"] = providerConfig.projectId;
661
+ break;
662
+ }
663
+ const config2 = {
664
+ apiKey: process.env.COMPUTESDK_API_KEY,
665
+ provider: providerName,
666
+ // Tell gateway which backend to use
667
+ providerHeaders
668
+ // Pass provider credentials via headers
669
+ };
670
+ compute2 = createCompute({
671
+ defaultProvider: gatewayFactory(config2)
672
+ });
673
+ }
674
+ state.compute = compute2;
675
+ return compute2;
676
+ }
514
677
  async function createSandbox(state) {
515
678
  const providerName = state.currentProvider || autoDetectProvider(false);
516
679
  const useDirect = state.useDirectMode;
@@ -540,55 +703,7 @@ async function createSandbox(state) {
540
703
  const spinner = new Spinner(`Creating sandbox with ${modeLabel}...`).start();
541
704
  const startTime = Date.now();
542
705
  try {
543
- let compute2;
544
- if (useDirect) {
545
- const providerModule = await loadProvider(providerName);
546
- const providerFactory = providerModule[providerName];
547
- if (!providerFactory) {
548
- throw new Error(`Provider ${providerName} does not export a factory function`);
549
- }
550
- const config2 = getProviderConfig(providerName);
551
- compute2 = createCompute({
552
- defaultProvider: providerFactory(config2)
553
- });
554
- } else {
555
- const gatewayModule = await import("computesdk");
556
- const gatewayFactory = gatewayModule.gateway;
557
- const providerConfig = getProviderConfig(providerName);
558
- const providerHeaders = {};
559
- switch (providerName) {
560
- case "e2b":
561
- if (providerConfig.apiKey) providerHeaders["X-E2B-API-Key"] = providerConfig.apiKey;
562
- break;
563
- case "railway":
564
- if (providerConfig.apiKey) providerHeaders["X-Railway-API-Key"] = providerConfig.apiKey;
565
- if (providerConfig.projectId) providerHeaders["X-Railway-Project-ID"] = providerConfig.projectId;
566
- if (providerConfig.environmentId) providerHeaders["X-Railway-Environment-ID"] = providerConfig.environmentId;
567
- break;
568
- case "daytona":
569
- if (providerConfig.apiKey) providerHeaders["X-Daytona-API-Key"] = providerConfig.apiKey;
570
- break;
571
- case "modal":
572
- if (providerConfig.tokenId) providerHeaders["X-Modal-Token-ID"] = providerConfig.tokenId;
573
- if (providerConfig.tokenSecret) providerHeaders["X-Modal-Token-Secret"] = providerConfig.tokenSecret;
574
- break;
575
- case "vercel":
576
- if (providerConfig.token) providerHeaders["X-Vercel-Token"] = providerConfig.token;
577
- if (providerConfig.teamId) providerHeaders["X-Vercel-Team-ID"] = providerConfig.teamId;
578
- if (providerConfig.projectId) providerHeaders["X-Vercel-Project-ID"] = providerConfig.projectId;
579
- break;
580
- }
581
- const config2 = {
582
- apiKey: process.env.COMPUTESDK_API_KEY,
583
- provider: providerName,
584
- // Tell gateway which backend to use
585
- providerHeaders
586
- // Pass provider credentials via headers
587
- };
588
- compute2 = createCompute({
589
- defaultProvider: gatewayFactory(config2)
590
- });
591
- }
706
+ const compute2 = await getComputeInstance(state);
592
707
  const result = await compute2.sandbox.create();
593
708
  const duration = Date.now() - startTime;
594
709
  setSandbox(state, result, providerName);
@@ -732,6 +847,7 @@ async function switchProvider(state, mode, providerName) {
732
847
  await destroySandbox(state);
733
848
  state.currentProvider = actualProvider;
734
849
  state.useDirectMode = useDirect;
850
+ state.compute = null;
735
851
  const modeStr = useDirect ? `${actualProvider} (direct)` : `${actualProvider} (via gateway)`;
736
852
  logSuccess(`Switched to ${modeStr}`);
737
853
  } else {
@@ -740,6 +856,7 @@ async function switchProvider(state, mode, providerName) {
740
856
  } else {
741
857
  state.currentProvider = actualProvider;
742
858
  state.useDirectMode = useDirect;
859
+ state.compute = null;
743
860
  const modeStr = useDirect ? `${actualProvider} (direct)` : `${actualProvider} (via gateway)`;
744
861
  logSuccess(`Switched to ${modeStr}`);
745
862
  }
@@ -817,6 +934,62 @@ Verbose mode: ${status}`);
817
934
  Toggle with: ${c.cyan("verbose")}
818
935
  `);
819
936
  }
937
+ async function connectToSandbox(state, sandboxUrl, token) {
938
+ if (!sandboxUrl) {
939
+ logError("Usage: connect <sandbox_url> [token]");
940
+ console.log("Example: connect https://sandbox-123.localhost:8080");
941
+ console.log("Example: connect https://sandbox-123.localhost:8080 your_access_token");
942
+ return;
943
+ }
944
+ const cleanUrl = sandboxUrl.replace(/\/$/, "");
945
+ if (hasSandbox(state)) {
946
+ const shouldDestroy = await confirm("Disconnect from current sandbox?");
947
+ if (!shouldDestroy) {
948
+ logWarning("Keeping current sandbox. Connection cancelled.");
949
+ return;
950
+ }
951
+ clearSandbox(state);
952
+ }
953
+ const spinner = new Spinner(`Connecting to ${cleanUrl}...`).start();
954
+ const startTime = Date.now();
955
+ try {
956
+ const { Sandbox } = await import("@computesdk/client");
957
+ let WebSocket;
958
+ try {
959
+ const wsModule = await import("ws");
960
+ WebSocket = wsModule.default;
961
+ } catch {
962
+ logError('Failed to import "ws" module. Please install it: pnpm add ws');
963
+ throw new Error('Missing "ws" dependency');
964
+ }
965
+ const sandbox = new Sandbox({
966
+ sandboxUrl: cleanUrl,
967
+ sandboxId: "",
968
+ // Will be populated when we get info
969
+ provider: "connected",
970
+ // Mark as directly connected
971
+ token,
972
+ // Optional access token
973
+ WebSocket
974
+ });
975
+ const info = await sandbox.getInfo();
976
+ const duration = Date.now() - startTime;
977
+ setSandbox(state, sandbox, "connected");
978
+ spinner.succeed(`Connected to sandbox ${c.dim(`(${formatDuration(duration)})`)}`);
979
+ console.log(c.dim(`Provider: ${info.provider || "unknown"}`));
980
+ console.log(c.dim(`Sandbox ID: ${info.id || "unknown"}`));
981
+ } catch (error) {
982
+ const duration = Date.now() - startTime;
983
+ spinner.fail(`Failed to connect ${c.dim(`(${formatDuration(duration)})`)}`);
984
+ if (error instanceof Error) {
985
+ logError(`Error: ${error.message}`);
986
+ if (error.stack) {
987
+ console.log(c.dim(error.stack));
988
+ }
989
+ }
990
+ throw error;
991
+ }
992
+ }
820
993
  async function cleanupOnExit(state, replServer) {
821
994
  if (!hasSandbox(state)) {
822
995
  return;
@@ -825,6 +998,10 @@ async function cleanupOnExit(state, replServer) {
825
998
  replServer.pause();
826
999
  }
827
1000
  console.log("");
1001
+ if (state.currentProvider === "connected") {
1002
+ logWarning("Disconnecting from external sandbox (not destroying).");
1003
+ return;
1004
+ }
828
1005
  const shouldDestroy = await confirm("Destroy active sandbox?");
829
1006
  if (shouldDestroy) {
830
1007
  await destroySandbox(state);
@@ -832,8 +1009,34 @@ async function cleanupOnExit(state, replServer) {
832
1009
  logWarning("Sandbox left running. It may incur costs.");
833
1010
  }
834
1011
  }
1012
+ var init_commands = __esm({
1013
+ "src/cli/commands.ts"() {
1014
+ "use strict";
1015
+ init_esm_shims();
1016
+ init_state();
1017
+ init_output();
1018
+ init_providers();
1019
+ }
1020
+ });
1021
+
1022
+ // src/bin/workbench.ts
1023
+ init_esm_shims();
1024
+ import { config } from "dotenv";
1025
+
1026
+ // src/cli/index.ts
1027
+ init_esm_shims();
1028
+ init_state();
1029
+
1030
+ // src/cli/repl.ts
1031
+ init_esm_shims();
1032
+ init_commands();
1033
+ init_output();
1034
+ init_providers();
1035
+ import * as repl from "repl";
1036
+ import * as cmd from "@computesdk/cmd";
835
1037
 
836
1038
  // src/cli/types.ts
1039
+ init_esm_shims();
837
1040
  function isCommand(value) {
838
1041
  return Array.isArray(value) && value.length > 0 && typeof value[0] === "string";
839
1042
  }
@@ -952,6 +1155,9 @@ function injectWorkbenchCommands(replServer, state) {
952
1155
  replServer.context.destroy = async () => {
953
1156
  await destroySandbox(state);
954
1157
  };
1158
+ replServer.context.connect = async (url, token) => {
1159
+ await connectToSandbox(state, url, token);
1160
+ };
955
1161
  replServer.context.info = () => showInfo(state);
956
1162
  replServer.context.verbose = () => {
957
1163
  toggleVerbose(state);
@@ -984,6 +1190,96 @@ function injectWorkbenchCommands(replServer, state) {
984
1190
  }
985
1191
  return sandbox.runCode(code, runtime);
986
1192
  };
1193
+ replServer.context.create = async (options) => {
1194
+ if (state.useDirectMode) {
1195
+ throw new Error('Named sandboxes are only available in gateway mode. Use "mode gateway" to switch.');
1196
+ }
1197
+ const { getComputeInstance: getComputeInstance2, confirmSandboxSwitch: confirmSandboxSwitch2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
1198
+ const { setSandbox: setSandbox2 } = await Promise.resolve().then(() => (init_state(), state_exports));
1199
+ const { logSuccess: logSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
1200
+ const shouldSwitch = await confirmSandboxSwitch2(state);
1201
+ if (!shouldSwitch) {
1202
+ const compute3 = await getComputeInstance2(state);
1203
+ const sandbox2 = await compute3.sandbox.create(options);
1204
+ return {
1205
+ sandboxId: sandbox2.sandboxId,
1206
+ provider: sandbox2.provider,
1207
+ metadata: sandbox2.getInstance().config.metadata || {}
1208
+ };
1209
+ }
1210
+ const compute2 = await getComputeInstance2(state);
1211
+ const sandbox = await compute2.sandbox.create(options);
1212
+ setSandbox2(state, sandbox, sandbox.provider);
1213
+ logSuccess2(`Switched to sandbox ${sandbox.sandboxId}`);
1214
+ return {
1215
+ sandboxId: sandbox.sandboxId,
1216
+ provider: sandbox.provider,
1217
+ metadata: sandbox.getInstance().config.metadata || {}
1218
+ };
1219
+ };
1220
+ replServer.context.findOrCreate = async (options) => {
1221
+ if (state.useDirectMode) {
1222
+ throw new Error('Named sandboxes (findOrCreate) are only available in gateway mode. Use "mode gateway" to switch.');
1223
+ }
1224
+ const { getComputeInstance: getComputeInstance2, confirmSandboxSwitch: confirmSandboxSwitch2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
1225
+ const { setSandbox: setSandbox2 } = await Promise.resolve().then(() => (init_state(), state_exports));
1226
+ const { logSuccess: logSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
1227
+ const shouldSwitch = await confirmSandboxSwitch2(state);
1228
+ if (!shouldSwitch) {
1229
+ const compute3 = await getComputeInstance2(state);
1230
+ const sandbox2 = await compute3.sandbox.findOrCreate(options);
1231
+ return {
1232
+ sandboxId: sandbox2.sandboxId,
1233
+ provider: sandbox2.provider,
1234
+ name: options.name,
1235
+ namespace: options.namespace || "default",
1236
+ metadata: sandbox2.getInstance().config.metadata || {}
1237
+ };
1238
+ }
1239
+ const compute2 = await getComputeInstance2(state);
1240
+ const sandbox = await compute2.sandbox.findOrCreate(options);
1241
+ setSandbox2(state, sandbox, sandbox.provider);
1242
+ logSuccess2(`Switched to sandbox ${sandbox.sandboxId}`);
1243
+ return {
1244
+ sandboxId: sandbox.sandboxId,
1245
+ provider: sandbox.provider,
1246
+ name: options.name,
1247
+ namespace: options.namespace || "default",
1248
+ metadata: sandbox.getInstance().config.metadata || {}
1249
+ };
1250
+ };
1251
+ replServer.context.find = async (options) => {
1252
+ if (state.useDirectMode) {
1253
+ throw new Error('Named sandboxes (find) are only available in gateway mode. Use "mode gateway" to switch.');
1254
+ }
1255
+ const { getComputeInstance: getComputeInstance2, confirmSandboxSwitch: confirmSandboxSwitch2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
1256
+ const { setSandbox: setSandbox2 } = await Promise.resolve().then(() => (init_state(), state_exports));
1257
+ const { logSuccess: logSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
1258
+ const compute2 = await getComputeInstance2(state);
1259
+ const sandbox = await compute2.sandbox.find(options);
1260
+ if (!sandbox) {
1261
+ return null;
1262
+ }
1263
+ const shouldSwitch = await confirmSandboxSwitch2(state);
1264
+ if (!shouldSwitch) {
1265
+ return {
1266
+ sandboxId: sandbox.sandboxId,
1267
+ provider: sandbox.provider,
1268
+ name: options.name,
1269
+ namespace: options.namespace || "default",
1270
+ metadata: sandbox.getInstance().config.metadata || {}
1271
+ };
1272
+ }
1273
+ setSandbox2(state, sandbox, sandbox.provider);
1274
+ logSuccess2(`Switched to sandbox ${sandbox.sandboxId}`);
1275
+ return {
1276
+ sandboxId: sandbox.sandboxId,
1277
+ provider: sandbox.provider,
1278
+ name: options.name,
1279
+ namespace: options.namespace || "default",
1280
+ metadata: sandbox.getInstance().config.metadata || {}
1281
+ };
1282
+ };
987
1283
  replServer.context.filesystem = {
988
1284
  get readFile() {
989
1285
  return async (path4) => {
@@ -1040,6 +1336,60 @@ function injectWorkbenchCommands(replServer, state) {
1040
1336
  };
1041
1337
  }
1042
1338
  };
1339
+ replServer.context.child = {
1340
+ get create() {
1341
+ return async () => {
1342
+ if (state.useDirectMode) {
1343
+ throw new Error('Child sandboxes are only available in gateway mode. Use "mode gateway" to switch.');
1344
+ }
1345
+ const sandbox = state.currentSandbox;
1346
+ if (!sandbox) {
1347
+ throw new Error("No active sandbox. Run a command to auto-create one.");
1348
+ }
1349
+ const instance = sandbox.getInstance();
1350
+ return instance.child.create();
1351
+ };
1352
+ },
1353
+ get list() {
1354
+ return async () => {
1355
+ if (state.useDirectMode) {
1356
+ throw new Error('Child sandboxes are only available in gateway mode. Use "mode gateway" to switch.');
1357
+ }
1358
+ const sandbox = state.currentSandbox;
1359
+ if (!sandbox) {
1360
+ throw new Error("No active sandbox. Run a command to auto-create one.");
1361
+ }
1362
+ const instance = sandbox.getInstance();
1363
+ return instance.child.list();
1364
+ };
1365
+ },
1366
+ get retrieve() {
1367
+ return async (subdomain) => {
1368
+ if (state.useDirectMode) {
1369
+ throw new Error('Child sandboxes are only available in gateway mode. Use "mode gateway" to switch.');
1370
+ }
1371
+ const sandbox = state.currentSandbox;
1372
+ if (!sandbox) {
1373
+ throw new Error("No active sandbox. Run a command to auto-create one.");
1374
+ }
1375
+ const instance = sandbox.getInstance();
1376
+ return instance.child.retrieve(subdomain);
1377
+ };
1378
+ },
1379
+ get destroy() {
1380
+ return async (subdomain, options) => {
1381
+ if (state.useDirectMode) {
1382
+ throw new Error('Child sandboxes are only available in gateway mode. Use "mode gateway" to switch.');
1383
+ }
1384
+ const sandbox = state.currentSandbox;
1385
+ if (!sandbox) {
1386
+ throw new Error("No active sandbox. Run a command to auto-create one.");
1387
+ }
1388
+ const instance = sandbox.getInstance();
1389
+ return instance.child.destroy(subdomain, options);
1390
+ };
1391
+ }
1392
+ };
1043
1393
  replServer.context.getInstance = () => {
1044
1394
  const sandbox = state.currentSandbox;
1045
1395
  if (!sandbox) {
@@ -1050,7 +1400,7 @@ function injectWorkbenchCommands(replServer, state) {
1050
1400
  }
1051
1401
  function setupSmartEvaluator(replServer, state) {
1052
1402
  const originalEval = replServer.eval;
1053
- const workbenchCommands = /* @__PURE__ */ new Set(["help", "providers", "info", "env", "restart", "destroy", "mode", "verbose", "sandboxInfo"]);
1403
+ const workbenchCommands = /* @__PURE__ */ new Set(["help", "providers", "info", "env", "restart", "destroy", "mode", "verbose", "sandboxInfo", "connect"]);
1054
1404
  replServer.eval = function(cmd3, context, filename, callback) {
1055
1405
  const trimmedCmd = cmd3.trim();
1056
1406
  const providerMatch = trimmedCmd.match(/^provider(?:\s+(direct|gateway))?\s+(\w+)$/);
@@ -1131,6 +1481,8 @@ function setupAutocomplete(replServer, state) {
1131
1481
  "providers": [],
1132
1482
  "restart": [],
1133
1483
  "destroy": [],
1484
+ "connect": [],
1485
+ // Connect takes a URL argument
1134
1486
  "info": [],
1135
1487
  "env": [],
1136
1488
  "help": [],
@@ -1224,6 +1576,9 @@ function setupHistory(replServer) {
1224
1576
  }
1225
1577
 
1226
1578
  // src/cli/index.ts
1579
+ init_output();
1580
+ init_providers();
1581
+ init_commands();
1227
1582
  async function startWorkbench() {
1228
1583
  const state = createState();
1229
1584
  state.availableProviders = getAvailableProviders();