@computesdk/workbench 0.1.0 → 1.0.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.
- package/README.md +42 -10
- package/dist/bin/workbench.js +299 -117
- package/dist/bin/workbench.js.map +1 -1
- package/dist/index.js +274 -113
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/bin/workbench-ts.ts +16 -2
package/README.md
CHANGED
|
@@ -67,7 +67,8 @@ drwxr-xr-x 2 user user 4096 Dec 12 19:01 repo
|
|
|
67
67
|
|
|
68
68
|
### Workbench Commands
|
|
69
69
|
|
|
70
|
-
- `provider <name>` - Switch provider (
|
|
70
|
+
- `provider <name>` - Switch provider via gateway (e.g., `provider e2b`)
|
|
71
|
+
- `provider direct <name>` - Switch to direct provider connection (e.g., `provider direct e2b`)
|
|
71
72
|
- `providers` - List all providers with status
|
|
72
73
|
- `restart` - Restart current sandbox
|
|
73
74
|
- `destroy` - Destroy current sandbox
|
|
@@ -76,6 +77,14 @@ drwxr-xr-x 2 user user 4096 Dec 12 19:01 repo
|
|
|
76
77
|
- `help` - Show this help
|
|
77
78
|
- `exit` - Exit workbench
|
|
78
79
|
|
|
80
|
+
**Provider Modes:**
|
|
81
|
+
- **Gateway mode (default)**: Routes through ComputeSDK API, zero-config setup
|
|
82
|
+
- Example: `provider e2b` uses E2B via gateway
|
|
83
|
+
- Requires: `COMPUTESDK_API_KEY` + provider credentials
|
|
84
|
+
- **Direct mode**: Connects directly to provider, requires provider packages
|
|
85
|
+
- Example: `provider direct e2b` connects directly to E2B
|
|
86
|
+
- Requires: Provider package installed (`@computesdk/e2b`) + provider credentials
|
|
87
|
+
|
|
79
88
|
### Running Commands
|
|
80
89
|
|
|
81
90
|
Just type any `@computesdk/cmd` function. Tab autocomplete works!
|
|
@@ -102,6 +111,9 @@ mkdir('/app/src')
|
|
|
102
111
|
ls('/home')
|
|
103
112
|
cat('/home/file.txt')
|
|
104
113
|
cp('/src', '/dest', { recursive: true })
|
|
114
|
+
rm('/file.txt') // Remove file
|
|
115
|
+
rm.rf('/directory') // Force remove anything
|
|
116
|
+
rm.auto('/path') // Smart remove (auto-detects file vs directory)
|
|
105
117
|
```
|
|
106
118
|
|
|
107
119
|
**Network:**
|
|
@@ -114,19 +126,39 @@ wget('https://file.com/download.zip')
|
|
|
114
126
|
|
|
115
127
|
## Provider Switching
|
|
116
128
|
|
|
117
|
-
|
|
129
|
+
### Gateway Mode (Default - Zero Config)
|
|
130
|
+
|
|
131
|
+
Switch between providers via the gateway:
|
|
118
132
|
|
|
119
133
|
```
|
|
134
|
+
workbench> provider e2b
|
|
135
|
+
✅ Switched to e2b (via gateway)
|
|
136
|
+
|
|
137
|
+
workbench> npm.install('express')
|
|
138
|
+
⏳ Creating sandbox with e2b (via gateway)...
|
|
139
|
+
✅ Sandbox ready (1.2s)
|
|
140
|
+
Running: npm install express
|
|
141
|
+
✅ Completed (3.2s)
|
|
142
|
+
|
|
120
143
|
workbench> provider railway
|
|
121
144
|
Destroy current sandbox? (y/N): y
|
|
122
|
-
✅
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
✅ Switched to railway (via gateway)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Direct Mode (Advanced)
|
|
149
|
+
|
|
150
|
+
Connect directly to providers (requires provider packages):
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
workbench> provider direct e2b
|
|
154
|
+
✅ Switched to e2b (direct)
|
|
155
|
+
|
|
156
|
+
workbench> pwd()
|
|
157
|
+
⏳ Creating sandbox with e2b (direct)...
|
|
158
|
+
✅ Sandbox ready (2.5s)
|
|
159
|
+
Running: pwd
|
|
160
|
+
/home
|
|
161
|
+
✅ Completed (0.1s)
|
|
130
162
|
```
|
|
131
163
|
|
|
132
164
|
## Tab Autocomplete
|
package/dist/bin/workbench.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// ../../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
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
7
|
+
var getDirname = () => path.dirname(getFilename());
|
|
8
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
9
|
+
|
|
3
10
|
// src/bin/workbench.ts
|
|
4
11
|
import { config } from "dotenv";
|
|
5
12
|
|
|
@@ -10,7 +17,8 @@ function createState() {
|
|
|
10
17
|
currentSandbox: null,
|
|
11
18
|
sandboxCreatedAt: null,
|
|
12
19
|
availableProviders: [],
|
|
13
|
-
|
|
20
|
+
useDirectMode: false,
|
|
21
|
+
// Default to gateway mode
|
|
14
22
|
verbose: false
|
|
15
23
|
};
|
|
16
24
|
}
|
|
@@ -79,16 +87,22 @@ var c = {
|
|
|
79
87
|
blue: (text) => `${colors.blue}${text}${colors.reset}`,
|
|
80
88
|
magenta: (text) => `${colors.magenta}${text}${colors.reset}`
|
|
81
89
|
};
|
|
82
|
-
function showWelcome(availableProviders, currentProvider) {
|
|
90
|
+
function showWelcome(availableProviders, currentProvider, useDirectMode) {
|
|
83
91
|
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")));
|
|
84
92
|
console.log(c.bold(c.cyan("\u2551 ComputeSDK Workbench \u2551")));
|
|
85
93
|
console.log(c.bold(c.cyan("\u255A\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\u255D\n")));
|
|
86
94
|
if (availableProviders.length > 0) {
|
|
87
|
-
|
|
95
|
+
const backendProviders = availableProviders.filter((p) => p !== "gateway");
|
|
96
|
+
console.log(`Providers available: ${backendProviders.join(", ")}`);
|
|
88
97
|
if (currentProvider) {
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
if (useDirectMode) {
|
|
99
|
+
console.log(`Current provider: ${c.green(currentProvider)} (\u{1F517} direct mode)
|
|
100
|
+
`);
|
|
101
|
+
} else {
|
|
102
|
+
const backendProvider = currentProvider === "gateway" ? backendProviders[0] || "auto" : currentProvider;
|
|
103
|
+
console.log(`Current provider: ${c.green(backendProvider)} (\u{1F310} via gateway)
|
|
91
104
|
`);
|
|
105
|
+
}
|
|
92
106
|
} else {
|
|
93
107
|
console.log(`
|
|
94
108
|
${c.dim('Tip: Use "provider <name>" to select a provider')}
|
|
@@ -104,58 +118,6 @@ ${c.dim('Tip: Use "provider <name>" to select a provider')}
|
|
|
104
118
|
}
|
|
105
119
|
console.log(c.dim('Type "help" for available commands\n'));
|
|
106
120
|
}
|
|
107
|
-
function showHelp() {
|
|
108
|
-
console.log(`
|
|
109
|
-
${c.bold("Workbench Commands:")}
|
|
110
|
-
${c.cyan("provider <name>")} Switch provider (gateway, e2b, railway, etc.)
|
|
111
|
-
${c.cyan("providers")} List all providers with status
|
|
112
|
-
${c.cyan("mode")} Show current mode (gateway vs direct)
|
|
113
|
-
${c.cyan("mode gateway")} Force gateway mode
|
|
114
|
-
${c.cyan("mode direct")} Force direct mode (auto-detect provider)
|
|
115
|
-
${c.cyan("restart")} Restart current sandbox
|
|
116
|
-
${c.cyan("destroy")} Destroy current sandbox
|
|
117
|
-
${c.cyan("info")} Show sandbox info
|
|
118
|
-
${c.cyan("env")} Show environment/credentials status
|
|
119
|
-
${c.cyan("verbose")} Toggle verbose output (show full results)
|
|
120
|
-
${c.cyan("help")} Show this help
|
|
121
|
-
${c.cyan("exit")} or ${c.cyan(".exit")} Exit workbench
|
|
122
|
-
|
|
123
|
-
${c.bold("Provider Modes:")}
|
|
124
|
-
${c.cyan("gateway")} \u{1F310} Routes through ComputeSDK API (COMPUTESDK_API_KEY)
|
|
125
|
-
${c.cyan("e2b, railway, etc.")} \u{1F517} Direct connection to provider (requires provider package)
|
|
126
|
-
|
|
127
|
-
${c.bold("Running Commands:")}
|
|
128
|
-
Just type any ${c.cyan("@computesdk/cmd")} function:
|
|
129
|
-
${c.dim('npm.install("express")')}
|
|
130
|
-
${c.dim('git.clone("https://github.com/user/repo")')}
|
|
131
|
-
${c.dim('python("script.py")')}
|
|
132
|
-
${c.dim('mkdir("/app/src")')}
|
|
133
|
-
${c.dim('ls("/home")')}
|
|
134
|
-
|
|
135
|
-
${c.green("\u2728 Tab autocomplete works for all functions!")}
|
|
136
|
-
|
|
137
|
-
${c.bold("Background Execution:")}
|
|
138
|
-
Run commands in the background (returns immediately):
|
|
139
|
-
${c.dim('sh("sleep 10", { background: true })')}
|
|
140
|
-
${c.dim('sh("npm start", { background: true })')}
|
|
141
|
-
|
|
142
|
-
${c.bold("Examples:")}
|
|
143
|
-
${c.dim("# Install a package")}
|
|
144
|
-
${c.cyan('npm.install("express")')}
|
|
145
|
-
|
|
146
|
-
${c.dim("# Clone a repo")}
|
|
147
|
-
${c.cyan('git.clone("https://github.com/user/repo")')}
|
|
148
|
-
|
|
149
|
-
${c.dim("# Run Python code")}
|
|
150
|
-
${c.cyan(`python("-c", "print('hello')")`)}
|
|
151
|
-
|
|
152
|
-
${c.dim("# Start a server in background")}
|
|
153
|
-
${c.cyan('sh("python -m http.server 8000", { background: true })')}
|
|
154
|
-
|
|
155
|
-
${c.dim("# Switch providers")}
|
|
156
|
-
${c.cyan("provider railway")}
|
|
157
|
-
`);
|
|
158
|
-
}
|
|
159
121
|
function showInfo(state) {
|
|
160
122
|
if (!state.currentSandbox) {
|
|
161
123
|
console.log(c.yellow("\nNo active sandbox\n"));
|
|
@@ -214,6 +176,64 @@ function logSuccess(message, duration) {
|
|
|
214
176
|
const durationStr = duration ? ` (${formatDuration(duration)})` : "";
|
|
215
177
|
console.log(c.green(`\u2705 ${message}${durationStr}`));
|
|
216
178
|
}
|
|
179
|
+
function showHelp() {
|
|
180
|
+
console.log(`
|
|
181
|
+
${c.bold("ComputeSDK Workbench Commands")}
|
|
182
|
+
|
|
183
|
+
${c.bold("Provider Management:")}
|
|
184
|
+
${c.cyan("provider <name>")} Switch to provider via gateway (default)
|
|
185
|
+
${c.dim("Example: provider e2b")}
|
|
186
|
+
${c.cyan("provider direct <name>")} Connect directly to provider
|
|
187
|
+
${c.dim("Example: provider direct e2b")}
|
|
188
|
+
${c.cyan("providers")} List all providers with status
|
|
189
|
+
${c.cyan("mode <gateway|direct>")} Toggle default mode for next sandbox
|
|
190
|
+
|
|
191
|
+
${c.bold("Provider Modes:")}
|
|
192
|
+
${c.bold("Gateway (default)")}: Routes through ComputeSDK API, zero-config
|
|
193
|
+
\u2022 Requires: COMPUTESDK_API_KEY + provider credentials
|
|
194
|
+
\u2022 Usage: ${c.cyan("provider e2b")}
|
|
195
|
+
|
|
196
|
+
${c.bold("Direct")}: Connects directly to provider, requires packages
|
|
197
|
+
\u2022 Requires: Provider package installed + credentials
|
|
198
|
+
\u2022 Usage: ${c.cyan("provider direct e2b")}
|
|
199
|
+
|
|
200
|
+
${c.bold("Sandbox Management:")}
|
|
201
|
+
${c.cyan("restart")} Restart current sandbox
|
|
202
|
+
${c.cyan("destroy")} Destroy current sandbox
|
|
203
|
+
${c.cyan("info")} Show sandbox info (provider, uptime)
|
|
204
|
+
|
|
205
|
+
${c.bold("Environment:")}
|
|
206
|
+
${c.cyan("env")} Show environment/credentials status
|
|
207
|
+
${c.cyan("verbose")} Toggle verbose output mode
|
|
208
|
+
|
|
209
|
+
${c.bold("Help:")}
|
|
210
|
+
${c.cyan("help")} Show this help message
|
|
211
|
+
${c.cyan("exit")} or ${c.cyan(".exit")} Exit workbench
|
|
212
|
+
|
|
213
|
+
${c.bold("Running Commands:")}
|
|
214
|
+
Type any @computesdk/cmd function and it will run automatically:
|
|
215
|
+
|
|
216
|
+
${c.dim("Package Managers:")}
|
|
217
|
+
${c.cyan('npm.install("express")')}
|
|
218
|
+
${c.cyan('pip.install("requests")')}
|
|
219
|
+
|
|
220
|
+
${c.dim("Git:")}
|
|
221
|
+
${c.cyan('git.clone("https://github.com/user/repo")')}
|
|
222
|
+
${c.cyan("git.status()")}
|
|
223
|
+
|
|
224
|
+
${c.dim("Filesystem:")}
|
|
225
|
+
${c.cyan('ls("/home")')}
|
|
226
|
+
${c.cyan('cat("/etc/hosts")')}
|
|
227
|
+
${c.cyan('rm.rf("/tmp")')} ${c.dim("// Force remove")}
|
|
228
|
+
${c.cyan('rm.auto("/path")')} ${c.dim("// Smart remove")}
|
|
229
|
+
|
|
230
|
+
${c.bold("Background Execution:")}
|
|
231
|
+
${c.cyan('sh("npm start", { background: true })')}
|
|
232
|
+
${c.cyan('sh("python -m http.server 8000", { background: true })')}
|
|
233
|
+
|
|
234
|
+
${c.dim("Press Tab for autocomplete on all commands!")}
|
|
235
|
+
`);
|
|
236
|
+
}
|
|
217
237
|
function logError(message) {
|
|
218
238
|
console.log(c.red(`\u274C ${message}`));
|
|
219
239
|
}
|
|
@@ -395,12 +415,44 @@ async function loadProvider(providerName) {
|
|
|
395
415
|
}
|
|
396
416
|
function getProviderConfig(providerName) {
|
|
397
417
|
const config2 = {};
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
418
|
+
switch (providerName) {
|
|
419
|
+
case "e2b":
|
|
420
|
+
if (process.env.E2B_API_KEY) config2.apiKey = process.env.E2B_API_KEY;
|
|
421
|
+
break;
|
|
422
|
+
case "railway":
|
|
423
|
+
if (process.env.RAILWAY_API_KEY) config2.apiKey = process.env.RAILWAY_API_KEY;
|
|
424
|
+
if (process.env.RAILWAY_PROJECT_ID) config2.projectId = process.env.RAILWAY_PROJECT_ID;
|
|
425
|
+
if (process.env.RAILWAY_ENVIRONMENT_ID) config2.environmentId = process.env.RAILWAY_ENVIRONMENT_ID;
|
|
426
|
+
break;
|
|
427
|
+
case "daytona":
|
|
428
|
+
if (process.env.DAYTONA_API_KEY) config2.apiKey = process.env.DAYTONA_API_KEY;
|
|
429
|
+
break;
|
|
430
|
+
case "modal":
|
|
431
|
+
if (process.env.MODAL_TOKEN_ID) config2.tokenId = process.env.MODAL_TOKEN_ID;
|
|
432
|
+
if (process.env.MODAL_TOKEN_SECRET) config2.tokenSecret = process.env.MODAL_TOKEN_SECRET;
|
|
433
|
+
break;
|
|
434
|
+
case "runloop":
|
|
435
|
+
if (process.env.RUNLOOP_API_KEY) config2.apiKey = process.env.RUNLOOP_API_KEY;
|
|
436
|
+
break;
|
|
437
|
+
case "vercel":
|
|
438
|
+
if (process.env.VERCEL_TOKEN) config2.token = process.env.VERCEL_TOKEN;
|
|
439
|
+
if (process.env.VERCEL_TEAM_ID) config2.teamId = process.env.VERCEL_TEAM_ID;
|
|
440
|
+
if (process.env.VERCEL_PROJECT_ID) config2.projectId = process.env.VERCEL_PROJECT_ID;
|
|
441
|
+
break;
|
|
442
|
+
case "cloudflare":
|
|
443
|
+
if (process.env.CLOUDFLARE_API_TOKEN) config2.apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
444
|
+
if (process.env.CLOUDFLARE_ACCOUNT_ID) config2.accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
445
|
+
break;
|
|
446
|
+
case "codesandbox":
|
|
447
|
+
if (process.env.CSB_API_KEY) config2.apiKey = process.env.CSB_API_KEY;
|
|
448
|
+
break;
|
|
449
|
+
case "blaxel":
|
|
450
|
+
if (process.env.BL_API_KEY) config2.apiKey = process.env.BL_API_KEY;
|
|
451
|
+
if (process.env.BL_WORKSPACE) config2.workspace = process.env.BL_WORKSPACE;
|
|
452
|
+
break;
|
|
453
|
+
case "gateway":
|
|
454
|
+
if (process.env.COMPUTESDK_API_KEY) config2.apiKey = process.env.COMPUTESDK_API_KEY;
|
|
455
|
+
break;
|
|
404
456
|
}
|
|
405
457
|
return config2;
|
|
406
458
|
}
|
|
@@ -428,23 +480,36 @@ async function ensureSandbox(state) {
|
|
|
428
480
|
await createSandbox(state);
|
|
429
481
|
}
|
|
430
482
|
async function createSandbox(state) {
|
|
431
|
-
const providerName = state.currentProvider || autoDetectProvider(
|
|
483
|
+
const providerName = state.currentProvider || autoDetectProvider(false);
|
|
484
|
+
const useDirect = state.useDirectMode;
|
|
432
485
|
if (!providerName) {
|
|
433
486
|
logError('No provider configured. Run "env" to see setup instructions.');
|
|
434
487
|
throw new Error("No provider available");
|
|
435
488
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
489
|
+
let modeLabel;
|
|
490
|
+
let actualProviderName;
|
|
491
|
+
if (useDirect) {
|
|
492
|
+
modeLabel = `${providerName} (direct)`;
|
|
493
|
+
actualProviderName = providerName;
|
|
494
|
+
if (!isProviderReady(providerName)) {
|
|
495
|
+
logError(`Provider ${providerName} is not fully configured for direct mode.`);
|
|
496
|
+
console.log(getProviderSetupHelp(providerName));
|
|
497
|
+
throw new Error("Provider not ready");
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
modeLabel = `${providerName} (via gateway)`;
|
|
501
|
+
actualProviderName = "gateway";
|
|
502
|
+
if (!isProviderReady("gateway")) {
|
|
503
|
+
logError("Gateway mode requires COMPUTESDK_API_KEY");
|
|
504
|
+
console.log(getProviderSetupHelp("gateway"));
|
|
505
|
+
throw new Error("Gateway not ready");
|
|
506
|
+
}
|
|
440
507
|
}
|
|
441
|
-
const spinner = new Spinner(`Creating sandbox with ${
|
|
508
|
+
const spinner = new Spinner(`Creating sandbox with ${modeLabel}...`).start();
|
|
442
509
|
const startTime = Date.now();
|
|
443
510
|
try {
|
|
444
511
|
let compute;
|
|
445
|
-
if (
|
|
446
|
-
compute = createCompute();
|
|
447
|
-
} else {
|
|
512
|
+
if (useDirect) {
|
|
448
513
|
const providerModule = await loadProvider(providerName);
|
|
449
514
|
const providerFactory = providerModule[providerName];
|
|
450
515
|
if (!providerFactory) {
|
|
@@ -454,6 +519,43 @@ async function createSandbox(state) {
|
|
|
454
519
|
compute = createCompute({
|
|
455
520
|
defaultProvider: providerFactory(config2)
|
|
456
521
|
});
|
|
522
|
+
} else {
|
|
523
|
+
const gatewayModule = await import("computesdk");
|
|
524
|
+
const gatewayFactory = gatewayModule.gateway;
|
|
525
|
+
const providerConfig = getProviderConfig(providerName);
|
|
526
|
+
const providerHeaders = {};
|
|
527
|
+
switch (providerName) {
|
|
528
|
+
case "e2b":
|
|
529
|
+
if (providerConfig.apiKey) providerHeaders["X-E2B-API-Key"] = providerConfig.apiKey;
|
|
530
|
+
break;
|
|
531
|
+
case "railway":
|
|
532
|
+
if (providerConfig.apiKey) providerHeaders["X-Railway-API-Key"] = providerConfig.apiKey;
|
|
533
|
+
if (providerConfig.projectId) providerHeaders["X-Railway-Project-ID"] = providerConfig.projectId;
|
|
534
|
+
if (providerConfig.environmentId) providerHeaders["X-Railway-Environment-ID"] = providerConfig.environmentId;
|
|
535
|
+
break;
|
|
536
|
+
case "daytona":
|
|
537
|
+
if (providerConfig.apiKey) providerHeaders["X-Daytona-API-Key"] = providerConfig.apiKey;
|
|
538
|
+
break;
|
|
539
|
+
case "modal":
|
|
540
|
+
if (providerConfig.tokenId) providerHeaders["X-Modal-Token-ID"] = providerConfig.tokenId;
|
|
541
|
+
if (providerConfig.tokenSecret) providerHeaders["X-Modal-Token-Secret"] = providerConfig.tokenSecret;
|
|
542
|
+
break;
|
|
543
|
+
case "vercel":
|
|
544
|
+
if (providerConfig.token) providerHeaders["X-Vercel-Token"] = providerConfig.token;
|
|
545
|
+
if (providerConfig.teamId) providerHeaders["X-Vercel-Team-ID"] = providerConfig.teamId;
|
|
546
|
+
if (providerConfig.projectId) providerHeaders["X-Vercel-Project-ID"] = providerConfig.projectId;
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
const config2 = {
|
|
550
|
+
apiKey: process.env.COMPUTESDK_API_KEY,
|
|
551
|
+
provider: providerName,
|
|
552
|
+
// Tell gateway which backend to use
|
|
553
|
+
providerHeaders
|
|
554
|
+
// Pass provider credentials via headers
|
|
555
|
+
};
|
|
556
|
+
compute = createCompute({
|
|
557
|
+
defaultProvider: gatewayFactory(config2)
|
|
558
|
+
});
|
|
457
559
|
}
|
|
458
560
|
const result = await compute.sandbox.create();
|
|
459
561
|
const duration = Date.now() - startTime;
|
|
@@ -468,6 +570,12 @@ async function createSandbox(state) {
|
|
|
468
570
|
Install it with: ${c.cyan(`npm install @computesdk/${providerName}`)}
|
|
469
571
|
`);
|
|
470
572
|
}
|
|
573
|
+
if (error instanceof Error) {
|
|
574
|
+
logError(`Error: ${error.message}`);
|
|
575
|
+
if (error.stack) {
|
|
576
|
+
console.log(c.dim(error.stack));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
471
579
|
throw error;
|
|
472
580
|
}
|
|
473
581
|
}
|
|
@@ -523,68 +631,100 @@ async function runCommand(state, command) {
|
|
|
523
631
|
throw error;
|
|
524
632
|
}
|
|
525
633
|
}
|
|
526
|
-
async function switchProvider(state,
|
|
527
|
-
|
|
528
|
-
|
|
634
|
+
async function switchProvider(state, mode, providerName) {
|
|
635
|
+
let useDirect = false;
|
|
636
|
+
let actualProvider = mode;
|
|
637
|
+
if (mode === "direct") {
|
|
638
|
+
if (!providerName) {
|
|
639
|
+
logError("Usage: provider direct <name>");
|
|
640
|
+
console.log("Example: provider direct e2b");
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
useDirect = true;
|
|
644
|
+
actualProvider = providerName;
|
|
645
|
+
} else if (mode === "gateway") {
|
|
646
|
+
if (!providerName) {
|
|
647
|
+
logError("Usage: provider gateway <name>");
|
|
648
|
+
console.log("Example: provider gateway e2b");
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
useDirect = false;
|
|
652
|
+
actualProvider = providerName;
|
|
653
|
+
}
|
|
654
|
+
if (actualProvider === "gateway") {
|
|
655
|
+
actualProvider = autoDetectProvider(false) || "e2b";
|
|
656
|
+
}
|
|
657
|
+
if (!isValidProvider(actualProvider)) {
|
|
658
|
+
logError(`Unknown provider: ${actualProvider}`);
|
|
529
659
|
console.log(`Available providers: e2b, railway, daytona, modal, runloop, vercel, cloudflare, codesandbox, blaxel`);
|
|
530
660
|
return;
|
|
531
661
|
}
|
|
532
|
-
if (!isProviderReady(
|
|
533
|
-
logError(
|
|
534
|
-
console.log(getProviderSetupHelp(
|
|
662
|
+
if (!useDirect && !isProviderReady("gateway")) {
|
|
663
|
+
logError("Gateway mode requires COMPUTESDK_API_KEY");
|
|
664
|
+
console.log(getProviderSetupHelp("gateway"));
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (useDirect && !isProviderReady(actualProvider)) {
|
|
668
|
+
logError(`Provider ${actualProvider} is not fully configured for direct mode.`);
|
|
669
|
+
console.log(getProviderSetupHelp(actualProvider));
|
|
535
670
|
return;
|
|
536
671
|
}
|
|
537
672
|
if (hasSandbox(state)) {
|
|
538
673
|
const shouldDestroy = await confirm("Destroy current sandbox?");
|
|
539
674
|
if (shouldDestroy) {
|
|
540
675
|
await destroySandbox(state);
|
|
541
|
-
state.currentProvider =
|
|
542
|
-
|
|
676
|
+
state.currentProvider = actualProvider;
|
|
677
|
+
state.useDirectMode = useDirect;
|
|
678
|
+
const modeStr = useDirect ? `${actualProvider} (direct)` : `${actualProvider} (via gateway)`;
|
|
679
|
+
logSuccess(`Switched to ${modeStr}`);
|
|
543
680
|
} else {
|
|
544
681
|
logWarning("Keeping current sandbox. Provider remains unchanged.");
|
|
545
682
|
}
|
|
546
683
|
} else {
|
|
547
|
-
state.currentProvider =
|
|
548
|
-
|
|
684
|
+
state.currentProvider = actualProvider;
|
|
685
|
+
state.useDirectMode = useDirect;
|
|
686
|
+
const modeStr = useDirect ? `${actualProvider} (direct)` : `${actualProvider} (via gateway)`;
|
|
687
|
+
logSuccess(`Switched to ${modeStr}`);
|
|
549
688
|
}
|
|
550
689
|
}
|
|
551
690
|
function createProviderCommand(state) {
|
|
552
|
-
return async function provider(
|
|
553
|
-
if (!
|
|
691
|
+
return async function provider(mode, providerName) {
|
|
692
|
+
if (!mode) {
|
|
554
693
|
if (state.currentProvider) {
|
|
694
|
+
const modeStr = state.useDirectMode ? "direct" : "via gateway";
|
|
555
695
|
console.log(`
|
|
556
|
-
Current provider: ${c.green(state.currentProvider)}
|
|
696
|
+
Current provider: ${c.green(state.currentProvider)} (${modeStr})
|
|
557
697
|
`);
|
|
558
698
|
} else {
|
|
559
699
|
console.log(c.yellow("\nNo provider selected\n"));
|
|
560
700
|
}
|
|
561
701
|
return;
|
|
562
702
|
}
|
|
563
|
-
await switchProvider(state,
|
|
703
|
+
await switchProvider(state, mode, providerName);
|
|
564
704
|
};
|
|
565
705
|
}
|
|
566
706
|
async function toggleMode(state, mode) {
|
|
567
|
-
const newMode = mode || (state.
|
|
568
|
-
if (newMode === "
|
|
569
|
-
state.
|
|
570
|
-
logSuccess("Switched to gateway mode \u{1F310}");
|
|
571
|
-
console.log(c.dim("Next sandbox will use gateway (requires COMPUTESDK_API_KEY)\n"));
|
|
572
|
-
if (hasSandbox(state) && state.currentProvider !== "gateway") {
|
|
573
|
-
console.log(c.yellow("Current sandbox is in direct mode."));
|
|
574
|
-
console.log(c.dim('Run "restart" to switch to gateway mode\n'));
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
state.forceGatewayMode = false;
|
|
707
|
+
const newMode = mode || (state.useDirectMode ? "gateway" : "direct");
|
|
708
|
+
if (newMode === "direct") {
|
|
709
|
+
state.useDirectMode = true;
|
|
578
710
|
logSuccess("Switched to direct mode \u{1F517}");
|
|
579
711
|
console.log(c.dim("Next sandbox will use direct provider packages\n"));
|
|
580
|
-
if (hasSandbox(state) && state.
|
|
712
|
+
if (hasSandbox(state) && !state.useDirectMode) {
|
|
581
713
|
console.log(c.yellow("Current sandbox is in gateway mode."));
|
|
582
714
|
console.log(c.dim('Run "restart" to switch to direct mode\n'));
|
|
583
715
|
}
|
|
716
|
+
} else {
|
|
717
|
+
state.useDirectMode = false;
|
|
718
|
+
logSuccess("Switched to gateway mode \u{1F310}");
|
|
719
|
+
console.log(c.dim("Next sandbox will use gateway (requires COMPUTESDK_API_KEY)\n"));
|
|
720
|
+
if (hasSandbox(state) && state.useDirectMode) {
|
|
721
|
+
console.log(c.yellow("Current sandbox is in direct mode."));
|
|
722
|
+
console.log(c.dim('Run "restart" to switch to gateway mode\n'));
|
|
723
|
+
}
|
|
584
724
|
}
|
|
585
725
|
}
|
|
586
726
|
function showMode(state) {
|
|
587
|
-
const mode = state.
|
|
727
|
+
const mode = state.useDirectMode ? "direct" : "gateway";
|
|
588
728
|
const icon = mode === "gateway" ? "\u{1F310}" : "\u{1F517}";
|
|
589
729
|
console.log(`
|
|
590
730
|
Current mode: ${c.green(mode)} ${icon}`);
|
|
@@ -594,7 +734,7 @@ Current mode: ${c.green(mode)} ${icon}`);
|
|
|
594
734
|
console.log(c.dim("Direct connection to providers (requires provider packages)"));
|
|
595
735
|
}
|
|
596
736
|
console.log(`
|
|
597
|
-
|
|
737
|
+
Switch with: ${c.cyan("provider e2b")} (gateway) or ${c.cyan("provider direct e2b")} (direct)
|
|
598
738
|
`);
|
|
599
739
|
}
|
|
600
740
|
function toggleVerbose(state) {
|
|
@@ -642,7 +782,7 @@ function isCommand(value) {
|
|
|
642
782
|
}
|
|
643
783
|
|
|
644
784
|
// src/cli/repl.ts
|
|
645
|
-
import * as
|
|
785
|
+
import * as path2 from "path";
|
|
646
786
|
import * as os from "os";
|
|
647
787
|
function createREPL(state) {
|
|
648
788
|
const replServer = repl.start({
|
|
@@ -769,10 +909,18 @@ function setupSmartEvaluator(replServer, state) {
|
|
|
769
909
|
const workbenchCommands = /* @__PURE__ */ new Set(["help", "providers", "info", "env", "restart", "destroy", "mode", "verbose"]);
|
|
770
910
|
replServer.eval = function(cmd3, context, filename, callback) {
|
|
771
911
|
const trimmedCmd = cmd3.trim();
|
|
772
|
-
const providerMatch = trimmedCmd.match(/^provider
|
|
912
|
+
const providerMatch = trimmedCmd.match(/^provider(?:\s+(direct|gateway))?\s+(\w+)$/);
|
|
773
913
|
if (providerMatch) {
|
|
774
|
-
const
|
|
775
|
-
const
|
|
914
|
+
const mode = providerMatch[1] || null;
|
|
915
|
+
const providerName = providerMatch[2];
|
|
916
|
+
const providerCmd = mode ? `await provider('${mode}', '${providerName}')` : `await provider('${providerName}')`;
|
|
917
|
+
originalEval.call(this, providerCmd, context, filename, callback);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const providerOnlyMatch = trimmedCmd.match(/^provider\s+(direct|gateway)$/);
|
|
921
|
+
if (providerOnlyMatch) {
|
|
922
|
+
const mode = providerOnlyMatch[1];
|
|
923
|
+
const providerCmd = `await provider('${mode}')`;
|
|
776
924
|
originalEval.call(this, providerCmd, context, filename, callback);
|
|
777
925
|
return;
|
|
778
926
|
}
|
|
@@ -828,12 +976,17 @@ function setupAutocomplete(replServer, state) {
|
|
|
828
976
|
};
|
|
829
977
|
replServer.completer = function(line, callback) {
|
|
830
978
|
const trimmed = line.trim();
|
|
831
|
-
if (!
|
|
979
|
+
if (!line.includes(" ") && !line.includes(".")) {
|
|
832
980
|
const commands = Object.keys(workbenchCommands);
|
|
833
981
|
const hits = commands.filter((cmd3) => cmd3.startsWith(trimmed));
|
|
834
982
|
if (originalCompleter) {
|
|
835
|
-
originalCompleter.call(replServer, line, (err,
|
|
836
|
-
if (err) {
|
|
983
|
+
originalCompleter.call(replServer, line, (err, result) => {
|
|
984
|
+
if (err || !result) {
|
|
985
|
+
callback(null, [hits, trimmed]);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const [contextHits, partial] = result;
|
|
989
|
+
if (!Array.isArray(contextHits)) {
|
|
837
990
|
callback(null, [hits, trimmed]);
|
|
838
991
|
return;
|
|
839
992
|
}
|
|
@@ -845,25 +998,32 @@ function setupAutocomplete(replServer, state) {
|
|
|
845
998
|
callback(null, [hits.length ? hits : commands, trimmed]);
|
|
846
999
|
return;
|
|
847
1000
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const
|
|
1001
|
+
if (line.includes(" ") && !line.includes(".")) {
|
|
1002
|
+
const parts = line.split(" ");
|
|
1003
|
+
const command = parts[0].trim();
|
|
1004
|
+
const partial = parts.slice(1).join(" ").trim();
|
|
851
1005
|
const suggestions = workbenchCommands[command];
|
|
852
1006
|
if (suggestions && suggestions.length > 0) {
|
|
853
1007
|
const hits = suggestions.filter((s) => s.startsWith(partial)).map((s) => `${command} ${s}`);
|
|
854
|
-
callback(null, [hits.length ? hits : suggestions.map((s) => `${command} ${s}`),
|
|
1008
|
+
callback(null, [hits.length ? hits : suggestions.map((s) => `${command} ${s}`), line]);
|
|
855
1009
|
return;
|
|
856
1010
|
}
|
|
857
1011
|
}
|
|
858
1012
|
if (originalCompleter) {
|
|
859
|
-
originalCompleter.call(replServer, line,
|
|
1013
|
+
originalCompleter.call(replServer, line, (err, result) => {
|
|
1014
|
+
if (err || !result) {
|
|
1015
|
+
callback(null, [[], line]);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
callback(null, result);
|
|
1019
|
+
});
|
|
860
1020
|
} else {
|
|
861
1021
|
callback(null, [[], line]);
|
|
862
1022
|
}
|
|
863
1023
|
};
|
|
864
1024
|
}
|
|
865
1025
|
function setupHistory(replServer) {
|
|
866
|
-
const historyFile =
|
|
1026
|
+
const historyFile = path2.join(os.homedir(), ".computesdk_workbench_history");
|
|
867
1027
|
replServer.setupHistory(historyFile, (err) => {
|
|
868
1028
|
if (err) {
|
|
869
1029
|
}
|
|
@@ -874,8 +1034,16 @@ function setupHistory(replServer) {
|
|
|
874
1034
|
async function startWorkbench() {
|
|
875
1035
|
const state = createState();
|
|
876
1036
|
state.availableProviders = getAvailableProviders();
|
|
877
|
-
|
|
878
|
-
|
|
1037
|
+
const detectedProvider = autoDetectProvider();
|
|
1038
|
+
if (detectedProvider === "gateway") {
|
|
1039
|
+
const backendProviders = state.availableProviders.filter((p) => p !== "gateway");
|
|
1040
|
+
state.currentProvider = backendProviders[0] || "e2b";
|
|
1041
|
+
state.useDirectMode = false;
|
|
1042
|
+
} else {
|
|
1043
|
+
state.currentProvider = detectedProvider;
|
|
1044
|
+
state.useDirectMode = false;
|
|
1045
|
+
}
|
|
1046
|
+
showWelcome(state.availableProviders, state.currentProvider, state.useDirectMode);
|
|
879
1047
|
const replServer = createREPL(state);
|
|
880
1048
|
replServer.on("exit", async () => {
|
|
881
1049
|
await cleanupOnExit(state, replServer);
|
|
@@ -884,8 +1052,22 @@ async function startWorkbench() {
|
|
|
884
1052
|
}
|
|
885
1053
|
|
|
886
1054
|
// src/bin/workbench.ts
|
|
887
|
-
import * as
|
|
888
|
-
|
|
1055
|
+
import * as path3 from "path";
|
|
1056
|
+
import * as fs from "fs";
|
|
1057
|
+
var possibleEnvPaths = [
|
|
1058
|
+
path3.join(process.cwd(), ".env"),
|
|
1059
|
+
// Current directory
|
|
1060
|
+
path3.join(process.cwd(), "../../.env"),
|
|
1061
|
+
// Monorepo root (if running from packages/workbench)
|
|
1062
|
+
path3.join(__dirname, "../../.env")
|
|
1063
|
+
// Relative to this script
|
|
1064
|
+
];
|
|
1065
|
+
for (const envPath of possibleEnvPaths) {
|
|
1066
|
+
if (fs.existsSync(envPath)) {
|
|
1067
|
+
config({ path: envPath });
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
889
1071
|
startWorkbench().catch((error) => {
|
|
890
1072
|
console.error("Fatal error:", error);
|
|
891
1073
|
process.exit(1);
|