@computesdk/just-bash 0.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.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/dist/index.d.mts +48 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +211 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 computesdk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# @computesdk/just-bash
|
|
2
|
+
|
|
3
|
+
[just-bash](https://justbash.dev/) provider for ComputeSDK - Local sandboxed bash execution with a virtual filesystem. No external services, containers, or authentication required.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @computesdk/just-bash
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Gateway Mode (Recommended)
|
|
14
|
+
|
|
15
|
+
Use the gateway for zero-config auto-detection:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { compute } from 'computesdk';
|
|
19
|
+
|
|
20
|
+
// just-bash is always available - no credentials needed
|
|
21
|
+
const sandbox = await compute.sandbox.create();
|
|
22
|
+
|
|
23
|
+
const result = await sandbox.runCode('echo "Hello from just-bash!"');
|
|
24
|
+
console.log(result.output); // "Hello from just-bash!"
|
|
25
|
+
|
|
26
|
+
await sandbox.destroy();
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Direct Mode
|
|
30
|
+
|
|
31
|
+
For direct SDK usage without the gateway:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { justBash } from '@computesdk/just-bash';
|
|
35
|
+
|
|
36
|
+
const compute = justBash({});
|
|
37
|
+
|
|
38
|
+
const sandbox = await compute.sandbox.create();
|
|
39
|
+
|
|
40
|
+
const result = await sandbox.runCommand('echo "Hello" | tr a-z A-Z');
|
|
41
|
+
console.log(result.stdout); // "HELLO"
|
|
42
|
+
|
|
43
|
+
await sandbox.destroy();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
### Environment Variables
|
|
49
|
+
|
|
50
|
+
No environment variables are required. just-bash runs entirely locally.
|
|
51
|
+
|
|
52
|
+
### Configuration Options
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
interface JustBashConfig {
|
|
56
|
+
/** Enable Python support via pyodide (disabled by default) */
|
|
57
|
+
python?: boolean;
|
|
58
|
+
/** Initial files to populate in the virtual filesystem */
|
|
59
|
+
files?: Record<string, string>;
|
|
60
|
+
/** Initial environment variables */
|
|
61
|
+
env?: Record<string, string>;
|
|
62
|
+
/** Working directory (defaults to /home/user) */
|
|
63
|
+
cwd?: string;
|
|
64
|
+
/** Custom filesystem implementation (see Filesystem Backends below) */
|
|
65
|
+
fs?: IFileSystem;
|
|
66
|
+
/** Custom commands created with defineCommand() (see Custom Commands below) */
|
|
67
|
+
customCommands?: CustomCommand[];
|
|
68
|
+
/** Network configuration for curl (disabled by default) */
|
|
69
|
+
network?: NetworkConfig;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Filesystem Backends
|
|
74
|
+
|
|
75
|
+
By default, just-bash uses an **InMemoryFs** — a pure in-memory filesystem. You can swap in alternative backends depending on your use case:
|
|
76
|
+
|
|
77
|
+
### InMemoryFs (default)
|
|
78
|
+
|
|
79
|
+
All files live in memory. Fast, deterministic, fully isolated.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const compute = justBash({}); // InMemoryFs is the default
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### OverlayFs (copy-on-write)
|
|
86
|
+
|
|
87
|
+
Reads from a real directory on disk; writes stay in memory. The underlying directory is never modified.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { OverlayFs } from 'just-bash';
|
|
91
|
+
|
|
92
|
+
const compute = justBash({
|
|
93
|
+
fs: new OverlayFs({ root: '/path/to/project' }),
|
|
94
|
+
cwd: '/path/to/project',
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### ReadWriteFs (direct disk access)
|
|
99
|
+
|
|
100
|
+
Reads and writes go directly to a real directory. Use with caution — changes are real.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { ReadWriteFs } from 'just-bash';
|
|
104
|
+
|
|
105
|
+
const compute = justBash({
|
|
106
|
+
fs: new ReadWriteFs({ root: '/tmp/sandbox' }),
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### MountableFs (compose multiple filesystems)
|
|
111
|
+
|
|
112
|
+
Mount different filesystem backends at different paths.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { MountableFs, InMemoryFs } from 'just-bash';
|
|
116
|
+
import { OverlayFs } from 'just-bash';
|
|
117
|
+
|
|
118
|
+
const compute = justBash({
|
|
119
|
+
fs: new MountableFs({
|
|
120
|
+
base: new InMemoryFs(),
|
|
121
|
+
mounts: [
|
|
122
|
+
{ mountPoint: '/project', filesystem: new OverlayFs({ root: '/real/project' }) },
|
|
123
|
+
],
|
|
124
|
+
}),
|
|
125
|
+
cwd: '/project',
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Custom Commands
|
|
130
|
+
|
|
131
|
+
You can extend just-bash with custom commands using `defineCommand()`:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { justBash } from '@computesdk/just-bash';
|
|
135
|
+
import { defineCommand } from 'just-bash';
|
|
136
|
+
|
|
137
|
+
const hello = defineCommand('hello', async (args, ctx) => {
|
|
138
|
+
const name = args[0] || 'world';
|
|
139
|
+
return {
|
|
140
|
+
stdout: `Hello, ${name}!\n`,
|
|
141
|
+
stderr: '',
|
|
142
|
+
exitCode: 0,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const compute = justBash({ customCommands: [hello] });
|
|
147
|
+
const sandbox = await compute.sandbox.create();
|
|
148
|
+
|
|
149
|
+
const result = await sandbox.runCommand('hello Alice');
|
|
150
|
+
console.log(result.stdout); // "Hello, Alice!\n"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Custom commands receive a context object (`ctx`) with access to:
|
|
154
|
+
- `ctx.fs` — the virtual filesystem
|
|
155
|
+
- `ctx.cwd` — current working directory
|
|
156
|
+
- `ctx.env` — environment variables
|
|
157
|
+
- `ctx.stdin` — standard input
|
|
158
|
+
- `ctx.exec(command)` — run subcommands
|
|
159
|
+
|
|
160
|
+
## API Reference
|
|
161
|
+
|
|
162
|
+
### Code Execution
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Execute bash scripts
|
|
166
|
+
const result = await sandbox.runCode(`
|
|
167
|
+
for i in 1 2 3; do
|
|
168
|
+
echo "Number: $i"
|
|
169
|
+
done
|
|
170
|
+
`);
|
|
171
|
+
console.log(result.output);
|
|
172
|
+
// Number: 1
|
|
173
|
+
// Number: 2
|
|
174
|
+
// Number: 3
|
|
175
|
+
|
|
176
|
+
// Execute Python code (requires python: true in config)
|
|
177
|
+
const compute = justBash({ python: true });
|
|
178
|
+
const sandbox = await compute.sandbox.create();
|
|
179
|
+
|
|
180
|
+
const result = await sandbox.runCode(`
|
|
181
|
+
import json
|
|
182
|
+
data = {"message": "Hello from Python"}
|
|
183
|
+
print(json.dumps(data))
|
|
184
|
+
`, 'python');
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Command Execution
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Pipes and text processing
|
|
191
|
+
const result = await sandbox.runCommand('echo -e "banana\\napple\\ncherry" | sort');
|
|
192
|
+
console.log(result.stdout); // "apple\nbanana\ncherry\n"
|
|
193
|
+
|
|
194
|
+
// JSON processing with jq
|
|
195
|
+
await sandbox.filesystem.writeFile('/data.json', '[{"name":"Alice"},{"name":"Bob"}]');
|
|
196
|
+
const result = await sandbox.runCommand('cat /data.json | jq ".[].name"');
|
|
197
|
+
|
|
198
|
+
// Environment variables per command
|
|
199
|
+
const result = await sandbox.runCommand('echo $MY_VAR', { env: { MY_VAR: 'hello' } });
|
|
200
|
+
|
|
201
|
+
// Working directory per command
|
|
202
|
+
const result = await sandbox.runCommand('cat config.json', { cwd: '/app' });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Filesystem Operations
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Write file
|
|
209
|
+
await sandbox.filesystem.writeFile('/tmp/hello.txt', 'Hello World');
|
|
210
|
+
|
|
211
|
+
// Read file
|
|
212
|
+
const content = await sandbox.filesystem.readFile('/tmp/hello.txt');
|
|
213
|
+
|
|
214
|
+
// Create directory
|
|
215
|
+
await sandbox.filesystem.mkdir('/tmp/data');
|
|
216
|
+
|
|
217
|
+
// List directory contents
|
|
218
|
+
const files = await sandbox.filesystem.readdir('/tmp');
|
|
219
|
+
|
|
220
|
+
// Check if file exists
|
|
221
|
+
const exists = await sandbox.filesystem.exists('/tmp/hello.txt');
|
|
222
|
+
|
|
223
|
+
// Remove file or directory
|
|
224
|
+
await sandbox.filesystem.remove('/tmp/hello.txt');
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Sandbox Management
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// Get sandbox info
|
|
231
|
+
const info = await sandbox.getInfo();
|
|
232
|
+
console.log(info.id, info.provider, info.status);
|
|
233
|
+
|
|
234
|
+
// List all active sandboxes
|
|
235
|
+
const sandboxes = await compute.sandbox.list();
|
|
236
|
+
|
|
237
|
+
// Get sandbox by ID
|
|
238
|
+
const existing = await compute.sandbox.getById('sandbox-id');
|
|
239
|
+
|
|
240
|
+
// Destroy sandbox
|
|
241
|
+
await sandbox.destroy();
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Pre-populated Files
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
const compute = justBash({
|
|
248
|
+
files: {
|
|
249
|
+
'/app/config.json': '{"port": 3000}',
|
|
250
|
+
'/app/data.csv': 'name,age\nAlice,25\nBob,30',
|
|
251
|
+
},
|
|
252
|
+
env: {
|
|
253
|
+
APP_ENV: 'production',
|
|
254
|
+
},
|
|
255
|
+
cwd: '/app',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const sandbox = await compute.sandbox.create();
|
|
259
|
+
const result = await sandbox.runCommand('cat config.json');
|
|
260
|
+
console.log(result.stdout); // {"port": 3000}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Built-in Commands
|
|
264
|
+
|
|
265
|
+
just-bash includes 60+ built-in commands:
|
|
266
|
+
|
|
267
|
+
| Category | Commands |
|
|
268
|
+
|----------|----------|
|
|
269
|
+
| **File ops** | `cat`, `cp`, `ls`, `mkdir`, `mv`, `rm`, `touch`, `tree`, `ln`, `find` |
|
|
270
|
+
| **Text processing** | `awk`, `grep`, `sed`, `cut`, `sort`, `uniq`, `wc`, `head`, `tail`, `tr` |
|
|
271
|
+
| **Data processing** | `jq` (JSON), `yq` (YAML/XML), `sqlite3` (SQLite), CSV tools |
|
|
272
|
+
| **Compression** | `gzip`, `tar` |
|
|
273
|
+
| **Utilities** | `echo`, `printf`, `date`, `seq`, `timeout`, `basename`, `dirname` |
|
|
274
|
+
| **Shell** | `export`, `source`, `alias`, `test`, `read`, `set`, `unset` |
|
|
275
|
+
|
|
276
|
+
## Error Handling
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { justBash } from '@computesdk/just-bash';
|
|
280
|
+
|
|
281
|
+
const compute = justBash({});
|
|
282
|
+
const sandbox = await compute.sandbox.create();
|
|
283
|
+
|
|
284
|
+
const result = await sandbox.runCommand('cat /nonexistent');
|
|
285
|
+
if (result.exitCode !== 0) {
|
|
286
|
+
console.error('Command failed:', result.stderr);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// getUrl throws since just-bash has no network
|
|
290
|
+
try {
|
|
291
|
+
await sandbox.getUrl({ port: 3000 });
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error(error.message); // "just-bash is a local sandbox without network capabilities..."
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Examples
|
|
298
|
+
|
|
299
|
+
### Data Pipeline
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { justBash } from '@computesdk/just-bash';
|
|
303
|
+
|
|
304
|
+
const compute = justBash({});
|
|
305
|
+
const sandbox = await compute.sandbox.create();
|
|
306
|
+
|
|
307
|
+
// Create CSV data
|
|
308
|
+
await sandbox.filesystem.writeFile('/data/sales.csv',
|
|
309
|
+
'product,quantity,price\nWidget,100,9.99\nGadget,50,24.99\nDoohickey,200,4.99'
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Process with awk
|
|
313
|
+
const result = await sandbox.runCommand(
|
|
314
|
+
'cat /data/sales.csv | tail -n +2 | awk -F, \'{ total += $2 * $3 } END { printf "Total revenue: $%.2f\\n", total }\''
|
|
315
|
+
);
|
|
316
|
+
console.log(result.stdout); // Total revenue: $3247.50
|
|
317
|
+
|
|
318
|
+
await sandbox.destroy();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### JSON Processing
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { justBash } from '@computesdk/just-bash';
|
|
325
|
+
|
|
326
|
+
const compute = justBash({
|
|
327
|
+
files: {
|
|
328
|
+
'/data/users.json': JSON.stringify([
|
|
329
|
+
{ name: 'Alice', age: 30, role: 'admin' },
|
|
330
|
+
{ name: 'Bob', age: 25, role: 'user' },
|
|
331
|
+
{ name: 'Charlie', age: 35, role: 'admin' },
|
|
332
|
+
]),
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
const sandbox = await compute.sandbox.create();
|
|
336
|
+
|
|
337
|
+
// Filter admins and extract names
|
|
338
|
+
const result = await sandbox.runCommand(
|
|
339
|
+
'cat /data/users.json | jq \'[.[] | select(.role == "admin") | .name]\''
|
|
340
|
+
);
|
|
341
|
+
console.log(result.stdout); // ["Alice", "Charlie"]
|
|
342
|
+
|
|
343
|
+
await sandbox.destroy();
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Script Execution
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { justBash } from '@computesdk/just-bash';
|
|
350
|
+
|
|
351
|
+
const compute = justBash({});
|
|
352
|
+
const sandbox = await compute.sandbox.create();
|
|
353
|
+
|
|
354
|
+
const result = await sandbox.runCode(`
|
|
355
|
+
#!/bin/bash
|
|
356
|
+
count=0
|
|
357
|
+
for f in /proc/self/status /etc/hostname; do
|
|
358
|
+
if test -f "$f"; then
|
|
359
|
+
count=$((count + 1))
|
|
360
|
+
fi
|
|
361
|
+
done
|
|
362
|
+
echo "Found $count system files"
|
|
363
|
+
`);
|
|
364
|
+
|
|
365
|
+
console.log(result.output);
|
|
366
|
+
|
|
367
|
+
await sandbox.destroy();
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Limitations
|
|
371
|
+
|
|
372
|
+
- **No Network Access** - `getUrl()` is not supported; `curl` requires explicit `network` config
|
|
373
|
+
- **No Real Processes** - Commands are interpreted in TypeScript, not executed as real OS processes
|
|
374
|
+
- **No Node.js Runtime** - `runCode` with `node` runtime executes as bash, not actual Node.js
|
|
375
|
+
- **In-Memory by Default** - Files don't persist unless you use `OverlayFs`, `ReadWriteFs`, or `MountableFs`
|
|
376
|
+
- **Python via Pyodide** - Python support requires `python: true` and uses pyodide (WebAssembly-based)
|
|
377
|
+
|
|
378
|
+
## When to Use just-bash
|
|
379
|
+
|
|
380
|
+
- **Testing & Development** - Fast, no-cost sandbox for development and CI
|
|
381
|
+
- **AI Agent Tools** - Secure bash execution for AI code generation
|
|
382
|
+
- **Offline Environments** - No network or API keys required
|
|
383
|
+
- **Unit Tests** - Deterministic, isolated test environments
|
|
384
|
+
|
|
385
|
+
## License
|
|
386
|
+
|
|
387
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
import { Bash, BashOptions } from 'just-bash';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* just-bash-specific configuration options
|
|
6
|
+
*/
|
|
7
|
+
interface JustBashConfig {
|
|
8
|
+
/** Enable Python support via pyodide (disabled by default) */
|
|
9
|
+
python?: boolean;
|
|
10
|
+
/** Initial files to populate in the virtual filesystem */
|
|
11
|
+
files?: BashOptions['files'];
|
|
12
|
+
/** Initial environment variables */
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
/** Working directory (defaults to /home/user) */
|
|
15
|
+
cwd?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Custom filesystem implementation.
|
|
18
|
+
* Defaults to InMemoryFs. Use OverlayFs for copy-on-write over a real directory,
|
|
19
|
+
* ReadWriteFs for direct disk access, or MountableFs to combine multiple filesystems.
|
|
20
|
+
*/
|
|
21
|
+
fs?: BashOptions['fs'];
|
|
22
|
+
/**
|
|
23
|
+
* Custom commands to register alongside built-in commands.
|
|
24
|
+
* Created with `defineCommand()` from just-bash.
|
|
25
|
+
*/
|
|
26
|
+
customCommands?: BashOptions['customCommands'];
|
|
27
|
+
/** Network configuration for commands like curl */
|
|
28
|
+
network?: BashOptions['network'];
|
|
29
|
+
}
|
|
30
|
+
/** Internal sandbox state */
|
|
31
|
+
interface JustBashSandbox {
|
|
32
|
+
bash: Bash;
|
|
33
|
+
id: string;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
config: JustBashConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a just-bash provider instance using the factory pattern
|
|
39
|
+
*
|
|
40
|
+
* just-bash provides local sandboxed bash execution with:
|
|
41
|
+
* - Virtual filesystem (in-memory)
|
|
42
|
+
* - 60+ built-in commands (cat, grep, sed, awk, jq, etc.)
|
|
43
|
+
* - Python support via pyodide
|
|
44
|
+
* - No external dependencies or authentication required
|
|
45
|
+
*/
|
|
46
|
+
declare const justBash: (config: JustBashConfig) => _computesdk_provider.Provider<JustBashSandbox, any, any>;
|
|
47
|
+
|
|
48
|
+
export { type JustBashConfig, type JustBashSandbox, justBash };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
import { Bash, BashOptions } from 'just-bash';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* just-bash-specific configuration options
|
|
6
|
+
*/
|
|
7
|
+
interface JustBashConfig {
|
|
8
|
+
/** Enable Python support via pyodide (disabled by default) */
|
|
9
|
+
python?: boolean;
|
|
10
|
+
/** Initial files to populate in the virtual filesystem */
|
|
11
|
+
files?: BashOptions['files'];
|
|
12
|
+
/** Initial environment variables */
|
|
13
|
+
env?: Record<string, string>;
|
|
14
|
+
/** Working directory (defaults to /home/user) */
|
|
15
|
+
cwd?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Custom filesystem implementation.
|
|
18
|
+
* Defaults to InMemoryFs. Use OverlayFs for copy-on-write over a real directory,
|
|
19
|
+
* ReadWriteFs for direct disk access, or MountableFs to combine multiple filesystems.
|
|
20
|
+
*/
|
|
21
|
+
fs?: BashOptions['fs'];
|
|
22
|
+
/**
|
|
23
|
+
* Custom commands to register alongside built-in commands.
|
|
24
|
+
* Created with `defineCommand()` from just-bash.
|
|
25
|
+
*/
|
|
26
|
+
customCommands?: BashOptions['customCommands'];
|
|
27
|
+
/** Network configuration for commands like curl */
|
|
28
|
+
network?: BashOptions['network'];
|
|
29
|
+
}
|
|
30
|
+
/** Internal sandbox state */
|
|
31
|
+
interface JustBashSandbox {
|
|
32
|
+
bash: Bash;
|
|
33
|
+
id: string;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
config: JustBashConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a just-bash provider instance using the factory pattern
|
|
39
|
+
*
|
|
40
|
+
* just-bash provides local sandboxed bash execution with:
|
|
41
|
+
* - Virtual filesystem (in-memory)
|
|
42
|
+
* - 60+ built-in commands (cat, grep, sed, awk, jq, etc.)
|
|
43
|
+
* - Python support via pyodide
|
|
44
|
+
* - No external dependencies or authentication required
|
|
45
|
+
*/
|
|
46
|
+
declare const justBash: (config: JustBashConfig) => _computesdk_provider.Provider<JustBashSandbox, any, any>;
|
|
47
|
+
|
|
48
|
+
export { type JustBashConfig, type JustBashSandbox, justBash };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
justBash: () => justBash
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_just_bash = require("just-bash");
|
|
27
|
+
var import_provider = require("@computesdk/provider");
|
|
28
|
+
var activeSandboxes = /* @__PURE__ */ new Map();
|
|
29
|
+
var justBash = (0, import_provider.defineProvider)({
|
|
30
|
+
name: "just-bash",
|
|
31
|
+
methods: {
|
|
32
|
+
sandbox: {
|
|
33
|
+
/**
|
|
34
|
+
* Create a new just-bash sandbox
|
|
35
|
+
*/
|
|
36
|
+
create: async (config, options) => {
|
|
37
|
+
const sandboxId = options?.sandboxId || `just-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
38
|
+
if (options?.sandboxId && activeSandboxes.has(options.sandboxId)) {
|
|
39
|
+
const existing = activeSandboxes.get(options.sandboxId);
|
|
40
|
+
return { sandbox: existing, sandboxId: existing.id };
|
|
41
|
+
}
|
|
42
|
+
const bash = new import_just_bash.Bash({
|
|
43
|
+
files: config.files,
|
|
44
|
+
env: {
|
|
45
|
+
...config.env,
|
|
46
|
+
...options?.envs
|
|
47
|
+
},
|
|
48
|
+
cwd: config.cwd || "/home/user",
|
|
49
|
+
python: config.python ?? options?.runtime === "python",
|
|
50
|
+
fs: config.fs,
|
|
51
|
+
customCommands: config.customCommands,
|
|
52
|
+
network: config.network
|
|
53
|
+
});
|
|
54
|
+
const sandbox = {
|
|
55
|
+
bash,
|
|
56
|
+
id: sandboxId,
|
|
57
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
58
|
+
config
|
|
59
|
+
};
|
|
60
|
+
activeSandboxes.set(sandboxId, sandbox);
|
|
61
|
+
return { sandbox, sandboxId };
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* Get an existing just-bash sandbox by ID
|
|
65
|
+
*/
|
|
66
|
+
getById: async (_config, sandboxId) => {
|
|
67
|
+
const sandbox = activeSandboxes.get(sandboxId);
|
|
68
|
+
if (!sandbox) return null;
|
|
69
|
+
return { sandbox, sandboxId };
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* List all active just-bash sandboxes
|
|
73
|
+
*/
|
|
74
|
+
list: async (_config) => {
|
|
75
|
+
return Array.from(activeSandboxes.entries()).map(([sandboxId, sandbox]) => ({
|
|
76
|
+
sandbox,
|
|
77
|
+
sandboxId
|
|
78
|
+
}));
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* Destroy a just-bash sandbox
|
|
82
|
+
*/
|
|
83
|
+
destroy: async (_config, sandboxId) => {
|
|
84
|
+
activeSandboxes.delete(sandboxId);
|
|
85
|
+
},
|
|
86
|
+
/**
|
|
87
|
+
* Execute code in the sandbox
|
|
88
|
+
*
|
|
89
|
+
* For Python: executes via the built-in python3 command (pyodide)
|
|
90
|
+
* For Node/JS: wraps code in a bash script that evaluates it
|
|
91
|
+
*/
|
|
92
|
+
runCode: async (sandbox, code, runtime) => {
|
|
93
|
+
const effectiveRuntime = runtime || (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") || code.includes("raise ") ? "python" : "node");
|
|
94
|
+
let result;
|
|
95
|
+
if (effectiveRuntime === "python") {
|
|
96
|
+
const tempFile = `/tmp/_computesdk_run_${Date.now()}.py`;
|
|
97
|
+
await sandbox.bash.writeFile(tempFile, code);
|
|
98
|
+
result = await sandbox.bash.exec(`python3 ${tempFile}`);
|
|
99
|
+
} else {
|
|
100
|
+
const tempFile = `/tmp/_computesdk_run_${Date.now()}.sh`;
|
|
101
|
+
await sandbox.bash.writeFile(tempFile, code);
|
|
102
|
+
result = await sandbox.bash.exec(`bash ${tempFile}`);
|
|
103
|
+
}
|
|
104
|
+
const output = result.stderr ? `${result.stdout}${result.stdout && result.stderr ? "\n" : ""}${result.stderr}` : result.stdout;
|
|
105
|
+
return {
|
|
106
|
+
output,
|
|
107
|
+
exitCode: result.exitCode,
|
|
108
|
+
language: effectiveRuntime
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Execute a shell command in the sandbox
|
|
113
|
+
*/
|
|
114
|
+
runCommand: async (sandbox, command, options) => {
|
|
115
|
+
const startTime = Date.now();
|
|
116
|
+
try {
|
|
117
|
+
const execOptions = {};
|
|
118
|
+
if (options?.env && Object.keys(options.env).length > 0) {
|
|
119
|
+
execOptions.env = options.env;
|
|
120
|
+
}
|
|
121
|
+
if (options?.cwd) {
|
|
122
|
+
execOptions.cwd = options.cwd;
|
|
123
|
+
}
|
|
124
|
+
const result = await sandbox.bash.exec(
|
|
125
|
+
command,
|
|
126
|
+
Object.keys(execOptions).length > 0 ? execOptions : void 0
|
|
127
|
+
);
|
|
128
|
+
return {
|
|
129
|
+
stdout: result.stdout || "",
|
|
130
|
+
stderr: result.stderr || "",
|
|
131
|
+
exitCode: result.exitCode,
|
|
132
|
+
durationMs: Date.now() - startTime
|
|
133
|
+
};
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return {
|
|
136
|
+
stdout: "",
|
|
137
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
138
|
+
exitCode: 127,
|
|
139
|
+
durationMs: Date.now() - startTime
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
/**
|
|
144
|
+
* Get sandbox information
|
|
145
|
+
*/
|
|
146
|
+
getInfo: async (sandbox) => {
|
|
147
|
+
return {
|
|
148
|
+
id: sandbox.id,
|
|
149
|
+
provider: "just-bash",
|
|
150
|
+
runtime: sandbox.config.python ? "python" : "node",
|
|
151
|
+
status: "running",
|
|
152
|
+
createdAt: sandbox.createdAt,
|
|
153
|
+
timeout: 0,
|
|
154
|
+
// No timeout - local execution
|
|
155
|
+
metadata: {
|
|
156
|
+
type: "local",
|
|
157
|
+
cwd: sandbox.bash.getCwd()
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
/**
|
|
162
|
+
* Get URL for a port - not supported for local execution
|
|
163
|
+
*/
|
|
164
|
+
getUrl: async (_sandbox, options) => {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`just-bash is a local sandbox without network capabilities. Cannot expose port ${options.port}.`
|
|
167
|
+
);
|
|
168
|
+
},
|
|
169
|
+
/**
|
|
170
|
+
* Filesystem operations using the just-bash virtual filesystem
|
|
171
|
+
*/
|
|
172
|
+
filesystem: {
|
|
173
|
+
readFile: async (sandbox, path, _runCommand) => {
|
|
174
|
+
return sandbox.bash.readFile(path);
|
|
175
|
+
},
|
|
176
|
+
writeFile: async (sandbox, path, content, _runCommand) => {
|
|
177
|
+
const parentDir = path.substring(0, path.lastIndexOf("/")) || "/";
|
|
178
|
+
if (parentDir !== "/") {
|
|
179
|
+
await sandbox.bash.exec(`mkdir -p ${parentDir}`);
|
|
180
|
+
}
|
|
181
|
+
await sandbox.bash.writeFile(path, content);
|
|
182
|
+
},
|
|
183
|
+
mkdir: async (sandbox, path, _runCommand) => {
|
|
184
|
+
const result = await sandbox.bash.exec(`mkdir -p ${path}`);
|
|
185
|
+
if (result.exitCode !== 0) {
|
|
186
|
+
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
readdir: async (sandbox, path, _runCommand) => {
|
|
190
|
+
const result = await sandbox.bash.exec(`ls -la ${path}`);
|
|
191
|
+
if (result.exitCode !== 0) {
|
|
192
|
+
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
193
|
+
}
|
|
194
|
+
const entries = [];
|
|
195
|
+
const lines = result.stdout.trim().split("\n");
|
|
196
|
+
for (const line of lines) {
|
|
197
|
+
if (!line || line.startsWith("total")) continue;
|
|
198
|
+
const parts = line.split(/\s+/);
|
|
199
|
+
if (parts.length < 9) continue;
|
|
200
|
+
const permissions = parts[0];
|
|
201
|
+
const name = parts.slice(8).join(" ");
|
|
202
|
+
if (name === "." || name === "..") continue;
|
|
203
|
+
entries.push({
|
|
204
|
+
name,
|
|
205
|
+
type: permissions.startsWith("d") ? "directory" : "file",
|
|
206
|
+
size: parseInt(parts[4], 10) || 0,
|
|
207
|
+
modified: /* @__PURE__ */ new Date()
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return entries;
|
|
211
|
+
},
|
|
212
|
+
exists: async (sandbox, path, _runCommand) => {
|
|
213
|
+
const result = await sandbox.bash.exec(`test -f ${path} || test -d ${path}`);
|
|
214
|
+
return result.exitCode === 0;
|
|
215
|
+
},
|
|
216
|
+
remove: async (sandbox, path, _runCommand) => {
|
|
217
|
+
const result = await sandbox.bash.exec(`rm -rf ${path}`);
|
|
218
|
+
if (result.exitCode !== 0) {
|
|
219
|
+
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Get the native JustBashSandbox instance for advanced usage
|
|
225
|
+
*/
|
|
226
|
+
getInstance: (sandbox) => {
|
|
227
|
+
return sandbox;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
233
|
+
0 && (module.exports = {
|
|
234
|
+
justBash
|
|
235
|
+
});
|
|
236
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * just-bash Provider - Factory-based Implementation\n *\n * Local sandboxed bash execution using the just-bash package.\n * Provides a virtual filesystem and bash shell environment\n * without requiring any external services or containers.\n *\n * Features:\n * - Pure TypeScript bash interpreter (no real processes)\n * - In-memory virtual filesystem\n * - Python support via pyodide (optional)\n * - Shell command execution with 60+ built-in commands\n * - No authentication required - runs entirely locally\n */\n\nimport { Bash } from 'just-bash';\nimport type { BashExecResult, BashOptions } from 'just-bash';\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n/**\n * just-bash-specific configuration options\n */\nexport interface JustBashConfig {\n /** Enable Python support via pyodide (disabled by default) */\n python?: boolean;\n /** Initial files to populate in the virtual filesystem */\n files?: BashOptions['files'];\n /** Initial environment variables */\n env?: Record<string, string>;\n /** Working directory (defaults to /home/user) */\n cwd?: string;\n /**\n * Custom filesystem implementation.\n * Defaults to InMemoryFs. Use OverlayFs for copy-on-write over a real directory,\n * ReadWriteFs for direct disk access, or MountableFs to combine multiple filesystems.\n */\n fs?: BashOptions['fs'];\n /**\n * Custom commands to register alongside built-in commands.\n * Created with `defineCommand()` from just-bash.\n */\n customCommands?: BashOptions['customCommands'];\n /** Network configuration for commands like curl */\n network?: BashOptions['network'];\n}\n\n/** Internal sandbox state */\ninterface JustBashSandbox {\n bash: Bash;\n id: string;\n createdAt: Date;\n config: JustBashConfig;\n}\n\n/** Active sandboxes registry */\nconst activeSandboxes = new Map<string, JustBashSandbox>();\n\n/**\n * Create a just-bash provider instance using the factory pattern\n *\n * just-bash provides local sandboxed bash execution with:\n * - Virtual filesystem (in-memory)\n * - 60+ built-in commands (cat, grep, sed, awk, jq, etc.)\n * - Python support via pyodide\n * - No external dependencies or authentication required\n */\nexport const justBash = defineProvider<JustBashSandbox, JustBashConfig>({\n name: 'just-bash',\n methods: {\n sandbox: {\n /**\n * Create a new just-bash sandbox\n */\n create: async (config: JustBashConfig, options?: CreateSandboxOptions) => {\n const sandboxId = options?.sandboxId || `just-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n // Check if reconnecting to existing sandbox\n if (options?.sandboxId && activeSandboxes.has(options.sandboxId)) {\n const existing = activeSandboxes.get(options.sandboxId)!;\n return { sandbox: existing, sandboxId: existing.id };\n }\n\n const bash = new Bash({\n files: config.files,\n env: {\n ...config.env,\n ...options?.envs,\n },\n cwd: config.cwd || '/home/user',\n python: config.python ?? (options?.runtime === 'python'),\n fs: config.fs,\n customCommands: config.customCommands,\n network: config.network,\n });\n\n const sandbox: JustBashSandbox = {\n bash,\n id: sandboxId,\n createdAt: new Date(),\n config,\n };\n\n activeSandboxes.set(sandboxId, sandbox);\n return { sandbox, sandboxId };\n },\n\n /**\n * Get an existing just-bash sandbox by ID\n */\n getById: async (_config: JustBashConfig, sandboxId: string) => {\n const sandbox = activeSandboxes.get(sandboxId);\n if (!sandbox) return null;\n return { sandbox, sandboxId };\n },\n\n /**\n * List all active just-bash sandboxes\n */\n list: async (_config: JustBashConfig) => {\n return Array.from(activeSandboxes.entries()).map(([sandboxId, sandbox]) => ({\n sandbox,\n sandboxId,\n }));\n },\n\n /**\n * Destroy a just-bash sandbox\n */\n destroy: async (_config: JustBashConfig, sandboxId: string) => {\n activeSandboxes.delete(sandboxId);\n },\n\n /**\n * Execute code in the sandbox\n *\n * For Python: executes via the built-in python3 command (pyodide)\n * For Node/JS: wraps code in a bash script that evaluates it\n */\n runCode: async (sandbox: JustBashSandbox, code: string, runtime?: Runtime): Promise<CodeResult> => {\n const effectiveRuntime = runtime || (\n code.includes('print(') ||\n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\") ||\n code.includes('raise ')\n ? 'python'\n : 'node'\n );\n\n let result: BashExecResult;\n\n if (effectiveRuntime === 'python') {\n // Use python3 command (requires python: true in config)\n // Write code to a temp file and execute it to avoid quoting issues\n const tempFile = `/tmp/_computesdk_run_${Date.now()}.py`;\n await sandbox.bash.writeFile(tempFile, code);\n result = await sandbox.bash.exec(`python3 ${tempFile}`);\n } else {\n // For node/JS, execute as bash script\n // just-bash doesn't have a real Node.js runtime,\n // so we execute the code as a bash script\n const tempFile = `/tmp/_computesdk_run_${Date.now()}.sh`;\n await sandbox.bash.writeFile(tempFile, code);\n result = await sandbox.bash.exec(`bash ${tempFile}`);\n }\n\n const output = result.stderr\n ? `${result.stdout}${result.stdout && result.stderr ? '\\n' : ''}${result.stderr}`\n : result.stdout;\n\n return {\n output,\n exitCode: result.exitCode,\n language: effectiveRuntime,\n };\n },\n\n /**\n * Execute a shell command in the sandbox\n */\n runCommand: async (sandbox: JustBashSandbox, command: string, options?: RunCommandOptions): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const execOptions: { env?: Record<string, string>; cwd?: string } = {};\n\n if (options?.env && Object.keys(options.env).length > 0) {\n execOptions.env = options.env;\n }\n\n if (options?.cwd) {\n execOptions.cwd = options.cwd;\n }\n\n const result = await sandbox.bash.exec(\n command,\n Object.keys(execOptions).length > 0 ? execOptions : undefined\n );\n\n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n /**\n * Get sandbox information\n */\n getInfo: async (sandbox: JustBashSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.id,\n provider: 'just-bash',\n runtime: sandbox.config.python ? 'python' : 'node',\n status: 'running',\n createdAt: sandbox.createdAt,\n timeout: 0, // No timeout - local execution\n metadata: {\n type: 'local',\n cwd: sandbox.bash.getCwd(),\n },\n };\n },\n\n /**\n * Get URL for a port - not supported for local execution\n */\n getUrl: async (_sandbox: JustBashSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n throw new Error(\n `just-bash is a local sandbox without network capabilities. Cannot expose port ${options.port}.`\n );\n },\n\n /**\n * Filesystem operations using the just-bash virtual filesystem\n */\n filesystem: {\n readFile: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<string> => {\n return sandbox.bash.readFile(path);\n },\n\n writeFile: async (sandbox: JustBashSandbox, path: string, content: string, _runCommand): Promise<void> => {\n // Ensure parent directory exists\n const parentDir = path.substring(0, path.lastIndexOf('/')) || '/';\n if (parentDir !== '/') {\n await sandbox.bash.exec(`mkdir -p ${parentDir}`);\n }\n await sandbox.bash.writeFile(path, content);\n },\n\n mkdir: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<void> => {\n const result = await sandbox.bash.exec(`mkdir -p ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<FileEntry[]> => {\n const result = await sandbox.bash.exec(`ls -la ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n\n const entries: FileEntry[] = [];\n const lines = result.stdout.trim().split('\\n');\n\n for (const line of lines) {\n // Skip total line and empty lines\n if (!line || line.startsWith('total')) continue;\n\n const parts = line.split(/\\s+/);\n if (parts.length < 9) continue;\n\n const permissions = parts[0];\n const name = parts.slice(8).join(' ');\n\n // Skip . and ..\n if (name === '.' || name === '..') continue;\n\n entries.push({\n name,\n type: permissions.startsWith('d') ? 'directory' as const : 'file' as const,\n size: parseInt(parts[4], 10) || 0,\n modified: new Date(),\n });\n }\n\n return entries;\n },\n\n exists: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<boolean> => {\n const result = await sandbox.bash.exec(`test -f ${path} || test -d ${path}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<void> => {\n const result = await sandbox.bash.exec(`rm -rf ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n /**\n * Get the native JustBashSandbox instance for advanced usage\n */\n getInstance: (sandbox: JustBashSandbox): JustBashSandbox => {\n return sandbox;\n },\n },\n },\n});\n\n// Export types\nexport type { JustBashSandbox };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,uBAAqB;AAErB,sBAA+B;AA+C/B,IAAM,kBAAkB,oBAAI,IAA6B;AAWlD,IAAM,eAAW,gCAAgD;AAAA,EACtE,MAAM;AAAA,EACN,SAAS;AAAA,IACP,SAAS;AAAA;AAAA;AAAA;AAAA,MAIP,QAAQ,OAAO,QAAwB,YAAmC;AACxE,cAAM,YAAY,SAAS,aAAa,aAAa,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAGzG,YAAI,SAAS,aAAa,gBAAgB,IAAI,QAAQ,SAAS,GAAG;AAChE,gBAAM,WAAW,gBAAgB,IAAI,QAAQ,SAAS;AACtD,iBAAO,EAAE,SAAS,UAAU,WAAW,SAAS,GAAG;AAAA,QACrD;AAEA,cAAM,OAAO,IAAI,sBAAK;AAAA,UACpB,OAAO,OAAO;AAAA,UACd,KAAK;AAAA,YACH,GAAG,OAAO;AAAA,YACV,GAAG,SAAS;AAAA,UACd;AAAA,UACA,KAAK,OAAO,OAAO;AAAA,UACnB,QAAQ,OAAO,UAAW,SAAS,YAAY;AAAA,UAC/C,IAAI,OAAO;AAAA,UACX,gBAAgB,OAAO;AAAA,UACvB,SAAS,OAAO;AAAA,QAClB,CAAC;AAED,cAAM,UAA2B;AAAA,UAC/B;AAAA,UACA,IAAI;AAAA,UACJ,WAAW,oBAAI,KAAK;AAAA,UACpB;AAAA,QACF;AAEA,wBAAgB,IAAI,WAAW,OAAO;AACtC,eAAO,EAAE,SAAS,UAAU;AAAA,MAC9B;AAAA;AAAA;AAAA;AAAA,MAKA,SAAS,OAAO,SAAyB,cAAsB;AAC7D,cAAM,UAAU,gBAAgB,IAAI,SAAS;AAC7C,YAAI,CAAC,QAAS,QAAO;AACrB,eAAO,EAAE,SAAS,UAAU;AAAA,MAC9B;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,OAAO,YAA4B;AACvC,eAAO,MAAM,KAAK,gBAAgB,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,WAAW,OAAO,OAAO;AAAA,UAC1E;AAAA,UACA;AAAA,QACF,EAAE;AAAA,MACJ;AAAA;AAAA;AAAA;AAAA,MAKA,SAAS,OAAO,SAAyB,cAAsB;AAC7D,wBAAgB,OAAO,SAAS;AAAA,MAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,SAAS,OAAO,SAA0B,MAAc,YAA2C;AACjG,cAAM,mBAAmB,YACvB,KAAK,SAAS,QAAQ,KACtB,KAAK,SAAS,SAAS,KACvB,KAAK,SAAS,MAAM,KACpB,KAAK,SAAS,MAAM,KACpB,KAAK,SAAS,OAAO,KACrB,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,QAAQ,IAClB,WACA;AAGN,YAAI;AAEJ,YAAI,qBAAqB,UAAU;AAGjC,gBAAM,WAAW,wBAAwB,KAAK,IAAI,CAAC;AACnD,gBAAM,QAAQ,KAAK,UAAU,UAAU,IAAI;AAC3C,mBAAS,MAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,EAAE;AAAA,QACxD,OAAO;AAIL,gBAAM,WAAW,wBAAwB,KAAK,IAAI,CAAC;AACnD,gBAAM,QAAQ,KAAK,UAAU,UAAU,IAAI;AAC3C,mBAAS,MAAM,QAAQ,KAAK,KAAK,QAAQ,QAAQ,EAAE;AAAA,QACrD;AAEA,cAAM,SAAS,OAAO,SAClB,GAAG,OAAO,MAAM,GAAG,OAAO,UAAU,OAAO,SAAS,OAAO,EAAE,GAAG,OAAO,MAAM,KAC7E,OAAO;AAEX,eAAO;AAAA,UACL;AAAA,UACA,UAAU,OAAO;AAAA,UACjB,UAAU;AAAA,QACZ;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,YAAY,OAAO,SAA0B,SAAiB,YAAwD;AACpH,cAAM,YAAY,KAAK,IAAI;AAC3B,YAAI;AACF,gBAAM,cAA8D,CAAC;AAErE,cAAI,SAAS,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,SAAS,GAAG;AACvD,wBAAY,MAAM,QAAQ;AAAA,UAC5B;AAEA,cAAI,SAAS,KAAK;AAChB,wBAAY,MAAM,QAAQ;AAAA,UAC5B;AAEA,gBAAM,SAAS,MAAM,QAAQ,KAAK;AAAA,YAChC;AAAA,YACA,OAAO,KAAK,WAAW,EAAE,SAAS,IAAI,cAAc;AAAA,UACtD;AAEA,iBAAO;AAAA,YACL,QAAQ,OAAO,UAAU;AAAA,YACzB,QAAQ,OAAO,UAAU;AAAA,YACzB,UAAU,OAAO;AAAA,YACjB,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC7D,UAAU;AAAA,YACV,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,SAAS,OAAO,YAAmD;AACjE,eAAO;AAAA,UACL,IAAI,QAAQ;AAAA,UACZ,UAAU;AAAA,UACV,SAAS,QAAQ,OAAO,SAAS,WAAW;AAAA,UAC5C,QAAQ;AAAA,UACR,WAAW,QAAQ;AAAA,UACnB,SAAS;AAAA;AAAA,UACT,UAAU;AAAA,YACR,MAAM;AAAA,YACN,KAAK,QAAQ,KAAK,OAAO;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,QAAQ,OAAO,UAA2B,YAAkE;AAC1G,cAAM,IAAI;AAAA,UACR,iFAAiF,QAAQ,IAAI;AAAA,QAC/F;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,YAAY;AAAA,QACV,UAAU,OAAO,SAA0B,MAAc,gBAAiC;AACxF,iBAAO,QAAQ,KAAK,SAAS,IAAI;AAAA,QACnC;AAAA,QAEA,WAAW,OAAO,SAA0B,MAAc,SAAiB,gBAA+B;AAExG,gBAAM,YAAY,KAAK,UAAU,GAAG,KAAK,YAAY,GAAG,CAAC,KAAK;AAC9D,cAAI,cAAc,KAAK;AACrB,kBAAM,QAAQ,KAAK,KAAK,YAAY,SAAS,EAAE;AAAA,UACjD;AACA,gBAAM,QAAQ,KAAK,UAAU,MAAM,OAAO;AAAA,QAC5C;AAAA,QAEA,OAAO,OAAO,SAA0B,MAAc,gBAA+B;AACnF,gBAAM,SAAS,MAAM,QAAQ,KAAK,KAAK,YAAY,IAAI,EAAE;AACzD,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,8BAA8B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACxE;AAAA,QACF;AAAA,QAEA,SAAS,OAAO,SAA0B,MAAc,gBAAsC;AAC5F,gBAAM,SAAS,MAAM,QAAQ,KAAK,KAAK,UAAU,IAAI,EAAE;AACvD,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,4BAA4B,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UACtE;AAEA,gBAAM,UAAuB,CAAC;AAC9B,gBAAM,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI;AAE7C,qBAAW,QAAQ,OAAO;AAExB,gBAAI,CAAC,QAAQ,KAAK,WAAW,OAAO,EAAG;AAEvC,kBAAM,QAAQ,KAAK,MAAM,KAAK;AAC9B,gBAAI,MAAM,SAAS,EAAG;AAEtB,kBAAM,cAAc,MAAM,CAAC;AAC3B,kBAAM,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AAGpC,gBAAI,SAAS,OAAO,SAAS,KAAM;AAEnC,oBAAQ,KAAK;AAAA,cACX;AAAA,cACA,MAAM,YAAY,WAAW,GAAG,IAAI,cAAuB;AAAA,cAC3D,MAAM,SAAS,MAAM,CAAC,GAAG,EAAE,KAAK;AAAA,cAChC,UAAU,oBAAI,KAAK;AAAA,YACrB,CAAC;AAAA,UACH;AAEA,iBAAO;AAAA,QACT;AAAA,QAEA,QAAQ,OAAO,SAA0B,MAAc,gBAAkC;AACvF,gBAAM,SAAS,MAAM,QAAQ,KAAK,KAAK,WAAW,IAAI,eAAe,IAAI,EAAE;AAC3E,iBAAO,OAAO,aAAa;AAAA,QAC7B;AAAA,QAEA,QAAQ,OAAO,SAA0B,MAAc,gBAA+B;AACpF,gBAAM,SAAS,MAAM,QAAQ,KAAK,KAAK,UAAU,IAAI,EAAE;AACvD,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,IAAI,MAAM,oBAAoB,IAAI,KAAK,OAAO,MAAM,EAAE;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,aAAa,CAAC,YAA8C;AAC1D,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF,CAAC;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Bash } from "just-bash";
|
|
3
|
+
import { defineProvider } from "@computesdk/provider";
|
|
4
|
+
var activeSandboxes = /* @__PURE__ */ new Map();
|
|
5
|
+
var justBash = defineProvider({
|
|
6
|
+
name: "just-bash",
|
|
7
|
+
methods: {
|
|
8
|
+
sandbox: {
|
|
9
|
+
/**
|
|
10
|
+
* Create a new just-bash sandbox
|
|
11
|
+
*/
|
|
12
|
+
create: async (config, options) => {
|
|
13
|
+
const sandboxId = options?.sandboxId || `just-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
14
|
+
if (options?.sandboxId && activeSandboxes.has(options.sandboxId)) {
|
|
15
|
+
const existing = activeSandboxes.get(options.sandboxId);
|
|
16
|
+
return { sandbox: existing, sandboxId: existing.id };
|
|
17
|
+
}
|
|
18
|
+
const bash = new Bash({
|
|
19
|
+
files: config.files,
|
|
20
|
+
env: {
|
|
21
|
+
...config.env,
|
|
22
|
+
...options?.envs
|
|
23
|
+
},
|
|
24
|
+
cwd: config.cwd || "/home/user",
|
|
25
|
+
python: config.python ?? options?.runtime === "python",
|
|
26
|
+
fs: config.fs,
|
|
27
|
+
customCommands: config.customCommands,
|
|
28
|
+
network: config.network
|
|
29
|
+
});
|
|
30
|
+
const sandbox = {
|
|
31
|
+
bash,
|
|
32
|
+
id: sandboxId,
|
|
33
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
34
|
+
config
|
|
35
|
+
};
|
|
36
|
+
activeSandboxes.set(sandboxId, sandbox);
|
|
37
|
+
return { sandbox, sandboxId };
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Get an existing just-bash sandbox by ID
|
|
41
|
+
*/
|
|
42
|
+
getById: async (_config, sandboxId) => {
|
|
43
|
+
const sandbox = activeSandboxes.get(sandboxId);
|
|
44
|
+
if (!sandbox) return null;
|
|
45
|
+
return { sandbox, sandboxId };
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* List all active just-bash sandboxes
|
|
49
|
+
*/
|
|
50
|
+
list: async (_config) => {
|
|
51
|
+
return Array.from(activeSandboxes.entries()).map(([sandboxId, sandbox]) => ({
|
|
52
|
+
sandbox,
|
|
53
|
+
sandboxId
|
|
54
|
+
}));
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* Destroy a just-bash sandbox
|
|
58
|
+
*/
|
|
59
|
+
destroy: async (_config, sandboxId) => {
|
|
60
|
+
activeSandboxes.delete(sandboxId);
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* Execute code in the sandbox
|
|
64
|
+
*
|
|
65
|
+
* For Python: executes via the built-in python3 command (pyodide)
|
|
66
|
+
* For Node/JS: wraps code in a bash script that evaluates it
|
|
67
|
+
*/
|
|
68
|
+
runCode: async (sandbox, code, runtime) => {
|
|
69
|
+
const effectiveRuntime = runtime || (code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") || code.includes("raise ") ? "python" : "node");
|
|
70
|
+
let result;
|
|
71
|
+
if (effectiveRuntime === "python") {
|
|
72
|
+
const tempFile = `/tmp/_computesdk_run_${Date.now()}.py`;
|
|
73
|
+
await sandbox.bash.writeFile(tempFile, code);
|
|
74
|
+
result = await sandbox.bash.exec(`python3 ${tempFile}`);
|
|
75
|
+
} else {
|
|
76
|
+
const tempFile = `/tmp/_computesdk_run_${Date.now()}.sh`;
|
|
77
|
+
await sandbox.bash.writeFile(tempFile, code);
|
|
78
|
+
result = await sandbox.bash.exec(`bash ${tempFile}`);
|
|
79
|
+
}
|
|
80
|
+
const output = result.stderr ? `${result.stdout}${result.stdout && result.stderr ? "\n" : ""}${result.stderr}` : result.stdout;
|
|
81
|
+
return {
|
|
82
|
+
output,
|
|
83
|
+
exitCode: result.exitCode,
|
|
84
|
+
language: effectiveRuntime
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Execute a shell command in the sandbox
|
|
89
|
+
*/
|
|
90
|
+
runCommand: async (sandbox, command, options) => {
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
try {
|
|
93
|
+
const execOptions = {};
|
|
94
|
+
if (options?.env && Object.keys(options.env).length > 0) {
|
|
95
|
+
execOptions.env = options.env;
|
|
96
|
+
}
|
|
97
|
+
if (options?.cwd) {
|
|
98
|
+
execOptions.cwd = options.cwd;
|
|
99
|
+
}
|
|
100
|
+
const result = await sandbox.bash.exec(
|
|
101
|
+
command,
|
|
102
|
+
Object.keys(execOptions).length > 0 ? execOptions : void 0
|
|
103
|
+
);
|
|
104
|
+
return {
|
|
105
|
+
stdout: result.stdout || "",
|
|
106
|
+
stderr: result.stderr || "",
|
|
107
|
+
exitCode: result.exitCode,
|
|
108
|
+
durationMs: Date.now() - startTime
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
stdout: "",
|
|
113
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
114
|
+
exitCode: 127,
|
|
115
|
+
durationMs: Date.now() - startTime
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Get sandbox information
|
|
121
|
+
*/
|
|
122
|
+
getInfo: async (sandbox) => {
|
|
123
|
+
return {
|
|
124
|
+
id: sandbox.id,
|
|
125
|
+
provider: "just-bash",
|
|
126
|
+
runtime: sandbox.config.python ? "python" : "node",
|
|
127
|
+
status: "running",
|
|
128
|
+
createdAt: sandbox.createdAt,
|
|
129
|
+
timeout: 0,
|
|
130
|
+
// No timeout - local execution
|
|
131
|
+
metadata: {
|
|
132
|
+
type: "local",
|
|
133
|
+
cwd: sandbox.bash.getCwd()
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* Get URL for a port - not supported for local execution
|
|
139
|
+
*/
|
|
140
|
+
getUrl: async (_sandbox, options) => {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`just-bash is a local sandbox without network capabilities. Cannot expose port ${options.port}.`
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* Filesystem operations using the just-bash virtual filesystem
|
|
147
|
+
*/
|
|
148
|
+
filesystem: {
|
|
149
|
+
readFile: async (sandbox, path, _runCommand) => {
|
|
150
|
+
return sandbox.bash.readFile(path);
|
|
151
|
+
},
|
|
152
|
+
writeFile: async (sandbox, path, content, _runCommand) => {
|
|
153
|
+
const parentDir = path.substring(0, path.lastIndexOf("/")) || "/";
|
|
154
|
+
if (parentDir !== "/") {
|
|
155
|
+
await sandbox.bash.exec(`mkdir -p ${parentDir}`);
|
|
156
|
+
}
|
|
157
|
+
await sandbox.bash.writeFile(path, content);
|
|
158
|
+
},
|
|
159
|
+
mkdir: async (sandbox, path, _runCommand) => {
|
|
160
|
+
const result = await sandbox.bash.exec(`mkdir -p ${path}`);
|
|
161
|
+
if (result.exitCode !== 0) {
|
|
162
|
+
throw new Error(`Failed to create directory ${path}: ${result.stderr}`);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
readdir: async (sandbox, path, _runCommand) => {
|
|
166
|
+
const result = await sandbox.bash.exec(`ls -la ${path}`);
|
|
167
|
+
if (result.exitCode !== 0) {
|
|
168
|
+
throw new Error(`Failed to list directory ${path}: ${result.stderr}`);
|
|
169
|
+
}
|
|
170
|
+
const entries = [];
|
|
171
|
+
const lines = result.stdout.trim().split("\n");
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
if (!line || line.startsWith("total")) continue;
|
|
174
|
+
const parts = line.split(/\s+/);
|
|
175
|
+
if (parts.length < 9) continue;
|
|
176
|
+
const permissions = parts[0];
|
|
177
|
+
const name = parts.slice(8).join(" ");
|
|
178
|
+
if (name === "." || name === "..") continue;
|
|
179
|
+
entries.push({
|
|
180
|
+
name,
|
|
181
|
+
type: permissions.startsWith("d") ? "directory" : "file",
|
|
182
|
+
size: parseInt(parts[4], 10) || 0,
|
|
183
|
+
modified: /* @__PURE__ */ new Date()
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return entries;
|
|
187
|
+
},
|
|
188
|
+
exists: async (sandbox, path, _runCommand) => {
|
|
189
|
+
const result = await sandbox.bash.exec(`test -f ${path} || test -d ${path}`);
|
|
190
|
+
return result.exitCode === 0;
|
|
191
|
+
},
|
|
192
|
+
remove: async (sandbox, path, _runCommand) => {
|
|
193
|
+
const result = await sandbox.bash.exec(`rm -rf ${path}`);
|
|
194
|
+
if (result.exitCode !== 0) {
|
|
195
|
+
throw new Error(`Failed to remove ${path}: ${result.stderr}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* Get the native JustBashSandbox instance for advanced usage
|
|
201
|
+
*/
|
|
202
|
+
getInstance: (sandbox) => {
|
|
203
|
+
return sandbox;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
export {
|
|
209
|
+
justBash
|
|
210
|
+
};
|
|
211
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * just-bash Provider - Factory-based Implementation\n *\n * Local sandboxed bash execution using the just-bash package.\n * Provides a virtual filesystem and bash shell environment\n * without requiring any external services or containers.\n *\n * Features:\n * - Pure TypeScript bash interpreter (no real processes)\n * - In-memory virtual filesystem\n * - Python support via pyodide (optional)\n * - Shell command execution with 60+ built-in commands\n * - No authentication required - runs entirely locally\n */\n\nimport { Bash } from 'just-bash';\nimport type { BashExecResult, BashOptions } from 'just-bash';\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions,\n} from 'computesdk';\n\n/**\n * just-bash-specific configuration options\n */\nexport interface JustBashConfig {\n /** Enable Python support via pyodide (disabled by default) */\n python?: boolean;\n /** Initial files to populate in the virtual filesystem */\n files?: BashOptions['files'];\n /** Initial environment variables */\n env?: Record<string, string>;\n /** Working directory (defaults to /home/user) */\n cwd?: string;\n /**\n * Custom filesystem implementation.\n * Defaults to InMemoryFs. Use OverlayFs for copy-on-write over a real directory,\n * ReadWriteFs for direct disk access, or MountableFs to combine multiple filesystems.\n */\n fs?: BashOptions['fs'];\n /**\n * Custom commands to register alongside built-in commands.\n * Created with `defineCommand()` from just-bash.\n */\n customCommands?: BashOptions['customCommands'];\n /** Network configuration for commands like curl */\n network?: BashOptions['network'];\n}\n\n/** Internal sandbox state */\ninterface JustBashSandbox {\n bash: Bash;\n id: string;\n createdAt: Date;\n config: JustBashConfig;\n}\n\n/** Active sandboxes registry */\nconst activeSandboxes = new Map<string, JustBashSandbox>();\n\n/**\n * Create a just-bash provider instance using the factory pattern\n *\n * just-bash provides local sandboxed bash execution with:\n * - Virtual filesystem (in-memory)\n * - 60+ built-in commands (cat, grep, sed, awk, jq, etc.)\n * - Python support via pyodide\n * - No external dependencies or authentication required\n */\nexport const justBash = defineProvider<JustBashSandbox, JustBashConfig>({\n name: 'just-bash',\n methods: {\n sandbox: {\n /**\n * Create a new just-bash sandbox\n */\n create: async (config: JustBashConfig, options?: CreateSandboxOptions) => {\n const sandboxId = options?.sandboxId || `just-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n // Check if reconnecting to existing sandbox\n if (options?.sandboxId && activeSandboxes.has(options.sandboxId)) {\n const existing = activeSandboxes.get(options.sandboxId)!;\n return { sandbox: existing, sandboxId: existing.id };\n }\n\n const bash = new Bash({\n files: config.files,\n env: {\n ...config.env,\n ...options?.envs,\n },\n cwd: config.cwd || '/home/user',\n python: config.python ?? (options?.runtime === 'python'),\n fs: config.fs,\n customCommands: config.customCommands,\n network: config.network,\n });\n\n const sandbox: JustBashSandbox = {\n bash,\n id: sandboxId,\n createdAt: new Date(),\n config,\n };\n\n activeSandboxes.set(sandboxId, sandbox);\n return { sandbox, sandboxId };\n },\n\n /**\n * Get an existing just-bash sandbox by ID\n */\n getById: async (_config: JustBashConfig, sandboxId: string) => {\n const sandbox = activeSandboxes.get(sandboxId);\n if (!sandbox) return null;\n return { sandbox, sandboxId };\n },\n\n /**\n * List all active just-bash sandboxes\n */\n list: async (_config: JustBashConfig) => {\n return Array.from(activeSandboxes.entries()).map(([sandboxId, sandbox]) => ({\n sandbox,\n sandboxId,\n }));\n },\n\n /**\n * Destroy a just-bash sandbox\n */\n destroy: async (_config: JustBashConfig, sandboxId: string) => {\n activeSandboxes.delete(sandboxId);\n },\n\n /**\n * Execute code in the sandbox\n *\n * For Python: executes via the built-in python3 command (pyodide)\n * For Node/JS: wraps code in a bash script that evaluates it\n */\n runCode: async (sandbox: JustBashSandbox, code: string, runtime?: Runtime): Promise<CodeResult> => {\n const effectiveRuntime = runtime || (\n code.includes('print(') ||\n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\") ||\n code.includes('raise ')\n ? 'python'\n : 'node'\n );\n\n let result: BashExecResult;\n\n if (effectiveRuntime === 'python') {\n // Use python3 command (requires python: true in config)\n // Write code to a temp file and execute it to avoid quoting issues\n const tempFile = `/tmp/_computesdk_run_${Date.now()}.py`;\n await sandbox.bash.writeFile(tempFile, code);\n result = await sandbox.bash.exec(`python3 ${tempFile}`);\n } else {\n // For node/JS, execute as bash script\n // just-bash doesn't have a real Node.js runtime,\n // so we execute the code as a bash script\n const tempFile = `/tmp/_computesdk_run_${Date.now()}.sh`;\n await sandbox.bash.writeFile(tempFile, code);\n result = await sandbox.bash.exec(`bash ${tempFile}`);\n }\n\n const output = result.stderr\n ? `${result.stdout}${result.stdout && result.stderr ? '\\n' : ''}${result.stderr}`\n : result.stdout;\n\n return {\n output,\n exitCode: result.exitCode,\n language: effectiveRuntime,\n };\n },\n\n /**\n * Execute a shell command in the sandbox\n */\n runCommand: async (sandbox: JustBashSandbox, command: string, options?: RunCommandOptions): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n const execOptions: { env?: Record<string, string>; cwd?: string } = {};\n\n if (options?.env && Object.keys(options.env).length > 0) {\n execOptions.env = options.env;\n }\n\n if (options?.cwd) {\n execOptions.cwd = options.cwd;\n }\n\n const result = await sandbox.bash.exec(\n command,\n Object.keys(execOptions).length > 0 ? execOptions : undefined\n );\n\n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exitCode,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127,\n durationMs: Date.now() - startTime,\n };\n }\n },\n\n /**\n * Get sandbox information\n */\n getInfo: async (sandbox: JustBashSandbox): Promise<SandboxInfo> => {\n return {\n id: sandbox.id,\n provider: 'just-bash',\n runtime: sandbox.config.python ? 'python' : 'node',\n status: 'running',\n createdAt: sandbox.createdAt,\n timeout: 0, // No timeout - local execution\n metadata: {\n type: 'local',\n cwd: sandbox.bash.getCwd(),\n },\n };\n },\n\n /**\n * Get URL for a port - not supported for local execution\n */\n getUrl: async (_sandbox: JustBashSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n throw new Error(\n `just-bash is a local sandbox without network capabilities. Cannot expose port ${options.port}.`\n );\n },\n\n /**\n * Filesystem operations using the just-bash virtual filesystem\n */\n filesystem: {\n readFile: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<string> => {\n return sandbox.bash.readFile(path);\n },\n\n writeFile: async (sandbox: JustBashSandbox, path: string, content: string, _runCommand): Promise<void> => {\n // Ensure parent directory exists\n const parentDir = path.substring(0, path.lastIndexOf('/')) || '/';\n if (parentDir !== '/') {\n await sandbox.bash.exec(`mkdir -p ${parentDir}`);\n }\n await sandbox.bash.writeFile(path, content);\n },\n\n mkdir: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<void> => {\n const result = await sandbox.bash.exec(`mkdir -p ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to create directory ${path}: ${result.stderr}`);\n }\n },\n\n readdir: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<FileEntry[]> => {\n const result = await sandbox.bash.exec(`ls -la ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to list directory ${path}: ${result.stderr}`);\n }\n\n const entries: FileEntry[] = [];\n const lines = result.stdout.trim().split('\\n');\n\n for (const line of lines) {\n // Skip total line and empty lines\n if (!line || line.startsWith('total')) continue;\n\n const parts = line.split(/\\s+/);\n if (parts.length < 9) continue;\n\n const permissions = parts[0];\n const name = parts.slice(8).join(' ');\n\n // Skip . and ..\n if (name === '.' || name === '..') continue;\n\n entries.push({\n name,\n type: permissions.startsWith('d') ? 'directory' as const : 'file' as const,\n size: parseInt(parts[4], 10) || 0,\n modified: new Date(),\n });\n }\n\n return entries;\n },\n\n exists: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<boolean> => {\n const result = await sandbox.bash.exec(`test -f ${path} || test -d ${path}`);\n return result.exitCode === 0;\n },\n\n remove: async (sandbox: JustBashSandbox, path: string, _runCommand): Promise<void> => {\n const result = await sandbox.bash.exec(`rm -rf ${path}`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to remove ${path}: ${result.stderr}`);\n }\n },\n },\n\n /**\n * Get the native JustBashSandbox instance for advanced usage\n */\n getInstance: (sandbox: JustBashSandbox): JustBashSandbox => {\n return sandbox;\n },\n },\n },\n});\n\n// Export types\nexport type { JustBashSandbox };\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@computesdk/just-bash",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "just-bash provider for ComputeSDK - local sandboxed bash execution with virtual filesystem",
|
|
5
|
+
"author": "ComputeSDK",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"computesdk": "2.3.0",
|
|
22
|
+
"@computesdk/provider": "1.0.28"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"just-bash": ">=2.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"just-bash": "^2.11.5",
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
31
|
+
"eslint": "^8.37.0",
|
|
32
|
+
"rimraf": "^5.0.0",
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"vitest": "^1.0.0",
|
|
36
|
+
"@computesdk/test-utils": "1.5.1"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"computesdk",
|
|
40
|
+
"just-bash",
|
|
41
|
+
"sandbox",
|
|
42
|
+
"code-execution",
|
|
43
|
+
"bash",
|
|
44
|
+
"shell",
|
|
45
|
+
"virtual-filesystem",
|
|
46
|
+
"provider",
|
|
47
|
+
"local"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/computesdk/computesdk.git",
|
|
52
|
+
"directory": "packages/just-bash"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://www.computesdk.com",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/computesdk/computesdk/issues"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsup",
|
|
60
|
+
"clean": "rimraf dist",
|
|
61
|
+
"dev": "tsup --watch",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest watch",
|
|
64
|
+
"test:coverage": "vitest run --coverage",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"lint": "eslint"
|
|
67
|
+
}
|
|
68
|
+
}
|