sandsnap 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 +287 -0
- package/dist/commands/delete.d.ts +9 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +50 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/evolve.d.ts +17 -0
- package/dist/commands/evolve.d.ts.map +1 -0
- package/dist/commands/evolve.js +228 -0
- package/dist/commands/evolve.js.map +1 -0
- package/dist/commands/list.d.ts +10 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +47 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/prune.d.ts +13 -0
- package/dist/commands/prune.d.ts.map +1 -0
- package/dist/commands/prune.js +75 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/run.d.ts +15 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +132 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/sandboxes.d.ts +11 -0
- package/dist/commands/sandboxes.d.ts.map +1 -0
- package/dist/commands/sandboxes.js +79 -0
- package/dist/commands/sandboxes.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/client.d.ts +14 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +30 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/copy.d.ts +14 -0
- package/dist/lib/copy.d.ts.map +1 -0
- package/dist/lib/copy.js +91 -0
- package/dist/lib/copy.js.map +1 -0
- package/dist/lib/output.d.ts +28 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +69 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/parse.d.ts +23 -0
- package/dist/lib/parse.d.ts.map +1 -0
- package/dist/lib/parse.js +58 -0
- package/dist/lib/parse.js.map +1 -0
- package/dist/lib/parse.test.d.ts +2 -0
- package/dist/lib/parse.test.d.ts.map +1 -0
- package/dist/lib/parse.test.js +139 -0
- package/dist/lib/parse.test.js.map +1 -0
- package/dist/lib/stdin.d.ts +24 -0
- package/dist/lib/stdin.d.ts.map +1 -0
- package/dist/lib/stdin.js +79 -0
- package/dist/lib/stdin.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Libo Shen
|
|
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,287 @@
|
|
|
1
|
+
# sandsnap
|
|
2
|
+
|
|
3
|
+
A CLI for managing Deno Sandbox environments with simplified snapshot-based workflows. Create, evolve, and run isolated Linux environments without dealing with volumes directly.
|
|
4
|
+
|
|
5
|
+
## Concept
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
base-image ──[commands]──► snapshot-A ──[commands]──► snapshot-B
|
|
9
|
+
╲
|
|
10
|
+
──[different commands]──► snapshot-C
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Snapshots are immutable environment states. You **evolve** from one snapshot to create another, or **run** ephemeral commands against a snapshot without modifying it.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Snapshot-based workflow** - Think in terms of environment states, not volumes
|
|
18
|
+
- **Evolve environments** - `snapshot A` + `commands` → `snapshot B`
|
|
19
|
+
- **Ephemeral execution** - Run commands against a snapshot without modifying it
|
|
20
|
+
- **Heredoc support** - Perfect for AI agents and scripting
|
|
21
|
+
- **Auto-cleanup** - Smart deletion of snapshots and their backing storage
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Using npm
|
|
27
|
+
npm install -g sandsnap
|
|
28
|
+
|
|
29
|
+
# Or run directly with npx
|
|
30
|
+
npx sandsnap <command>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires Node.js 24+ and a [Deno Deploy](https://console.deno.com/) account.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
1. Create an account at [console.deno.com](https://console.deno.com/)
|
|
38
|
+
2. Go to **Settings → Organization tokens** and create a token
|
|
39
|
+
3. Set the token:
|
|
40
|
+
```bash
|
|
41
|
+
export DENO_DEPLOY_TOKEN=<your-token>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Create a snapshot with Python installed
|
|
48
|
+
sandsnap evolve python-env <<'EOF'
|
|
49
|
+
sudo apt-get update
|
|
50
|
+
sudo apt-get install -y python3 python3-pip
|
|
51
|
+
python3 --version
|
|
52
|
+
EOF
|
|
53
|
+
|
|
54
|
+
# Run commands against it (ephemeral - changes discarded)
|
|
55
|
+
sandsnap run python-env <<'EOF'
|
|
56
|
+
python3 -c "print('Hello from sandbox!')"
|
|
57
|
+
EOF
|
|
58
|
+
|
|
59
|
+
# Evolve further from existing snapshot
|
|
60
|
+
sandsnap evolve python-ml --from python-env <<'EOF'
|
|
61
|
+
pip3 install numpy pandas scikit-learn
|
|
62
|
+
python3 -c "import numpy; print(numpy.__version__)"
|
|
63
|
+
EOF
|
|
64
|
+
|
|
65
|
+
# Interactive shell
|
|
66
|
+
sandsnap run python-ml
|
|
67
|
+
|
|
68
|
+
# List all snapshots
|
|
69
|
+
sandsnap list
|
|
70
|
+
|
|
71
|
+
# Delete a snapshot (auto-cleans backing storage)
|
|
72
|
+
sandsnap delete python-env --force
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
### `sandsnap evolve <name>`
|
|
78
|
+
|
|
79
|
+
Create a new snapshot by running commands on a base state.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# From base Debian image (default)
|
|
83
|
+
sandsnap evolve my-env
|
|
84
|
+
|
|
85
|
+
# From existing snapshot
|
|
86
|
+
sandsnap evolve my-env-v2 --from my-env
|
|
87
|
+
|
|
88
|
+
# With script file
|
|
89
|
+
sandsnap evolve my-env --script setup.sh
|
|
90
|
+
|
|
91
|
+
# With heredoc
|
|
92
|
+
sandsnap evolve my-env <<'EOF'
|
|
93
|
+
apt-get update
|
|
94
|
+
apt-get install -y curl git
|
|
95
|
+
EOF
|
|
96
|
+
|
|
97
|
+
# Overwrite existing snapshot
|
|
98
|
+
sandsnap evolve my-env --overwrite <<'EOF'
|
|
99
|
+
# new setup
|
|
100
|
+
EOF
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Options:**
|
|
104
|
+
| Option | Description | Default |
|
|
105
|
+
|--------|-------------|---------|
|
|
106
|
+
| `--from <snapshot>` | Base snapshot to evolve from | debian-13 |
|
|
107
|
+
| `--timeout <duration>` | Sandbox timeout (e.g., `10m`, `30m`) | `10m` |
|
|
108
|
+
| `--capacity <size>` | Volume capacity (e.g., `2GiB`, `10GiB`) | `10GiB` |
|
|
109
|
+
| `--memory <size>` | Sandbox memory (e.g., `1GiB`, `2GiB`, `4GiB`) | `1280MiB` |
|
|
110
|
+
| `--region <region>` | Region (`ord` or `ams`) | `ord` |
|
|
111
|
+
| `--env <KEY=VALUE>` | Set environment variable (repeatable) | - |
|
|
112
|
+
| `--copy <src:dst>` | Copy file/dir from host to sandbox (repeatable) | - |
|
|
113
|
+
| `--script <file>` | Read commands from file | - |
|
|
114
|
+
| `--overwrite` | Replace existing snapshot | `false` |
|
|
115
|
+
|
|
116
|
+
### `sandsnap run <snapshot>`
|
|
117
|
+
|
|
118
|
+
Run commands in an ephemeral sandbox. Changes are discarded after exit.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Interactive shell
|
|
122
|
+
sandsnap run my-env
|
|
123
|
+
|
|
124
|
+
# Run commands
|
|
125
|
+
sandsnap run my-env <<'EOF'
|
|
126
|
+
echo "Hello"
|
|
127
|
+
python3 script.py
|
|
128
|
+
EOF
|
|
129
|
+
|
|
130
|
+
# With script file
|
|
131
|
+
sandsnap run my-env --script test.sh
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Options:**
|
|
135
|
+
| Option | Description | Default |
|
|
136
|
+
|--------|-------------|---------|
|
|
137
|
+
| `--timeout <duration>` | Sandbox timeout | `session` |
|
|
138
|
+
| `--memory <size>` | Sandbox memory (e.g., `1GiB`, `2GiB`, `4GiB`) | `1280MiB` |
|
|
139
|
+
| `--region <region>` | Region (`ord` or `ams`) | `ord` |
|
|
140
|
+
| `--env <KEY=VALUE>` | Set environment variable (repeatable) | - |
|
|
141
|
+
| `--copy <src:dst>` | Copy file/dir from host to sandbox (repeatable) | - |
|
|
142
|
+
| `--copy-out <src:dst>` | Copy file/dir from sandbox to host (repeatable) | - |
|
|
143
|
+
| `--script <file>` | Read commands from file | - |
|
|
144
|
+
|
|
145
|
+
### `sandsnap list`
|
|
146
|
+
|
|
147
|
+
List all snapshots.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
sandsnap list
|
|
151
|
+
sandsnap list --json
|
|
152
|
+
sandsnap list --region ord
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `sandsnap delete <name>`
|
|
156
|
+
|
|
157
|
+
Delete a snapshot and clean up its backing storage.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
sandsnap delete my-env
|
|
161
|
+
sandsnap delete my-env --force # Skip confirmation
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `sandsnap prune`
|
|
165
|
+
|
|
166
|
+
Clean up orphaned temporary volumes.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
sandsnap prune --dry-run # See what would be deleted
|
|
170
|
+
sandsnap prune --force # Delete without confirmation
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `sandsnap sandboxes`
|
|
174
|
+
|
|
175
|
+
Manage running sandboxes (for debugging).
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
sandsnap sandboxes list # List all sandboxes
|
|
179
|
+
sandsnap sandboxes kill <id> # Kill a specific sandbox
|
|
180
|
+
sandsnap sandboxes kill-all # Kill all running sandboxes
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Global Options
|
|
184
|
+
|
|
185
|
+
| Option | Description |
|
|
186
|
+
|--------|-------------|
|
|
187
|
+
| `-v, --verbose` | Show detailed progress logs |
|
|
188
|
+
| `-h, --help` | Show help |
|
|
189
|
+
| `--version` | Show version |
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
### Setting up a development environment
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Create base environment
|
|
197
|
+
sandsnap evolve dev-base <<'EOF'
|
|
198
|
+
sudo apt-get update
|
|
199
|
+
sudo apt-get install -y git curl wget build-essential
|
|
200
|
+
EOF
|
|
201
|
+
|
|
202
|
+
# Add Node.js
|
|
203
|
+
sandsnap evolve dev-node --from dev-base <<'EOF'
|
|
204
|
+
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
|
205
|
+
sudo apt-get install -y nodejs
|
|
206
|
+
node --version
|
|
207
|
+
EOF
|
|
208
|
+
|
|
209
|
+
# Add Python
|
|
210
|
+
sandsnap evolve dev-python --from dev-base <<'EOF'
|
|
211
|
+
sudo apt-get install -y python3 python3-pip python3-venv
|
|
212
|
+
python3 --version
|
|
213
|
+
EOF
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Running tests in isolation
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Run tests against a clean environment every time
|
|
220
|
+
sandsnap run dev-node <<'EOF'
|
|
221
|
+
cd /tmp
|
|
222
|
+
git clone https://github.com/user/repo.git
|
|
223
|
+
cd repo
|
|
224
|
+
npm install
|
|
225
|
+
npm test
|
|
226
|
+
EOF
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Environment variables
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Pass environment variables to the sandbox
|
|
233
|
+
sandsnap run python-env \
|
|
234
|
+
--env API_KEY=secret123 \
|
|
235
|
+
--env DEBUG=true \
|
|
236
|
+
<<'EOF'
|
|
237
|
+
echo "API_KEY=$API_KEY"
|
|
238
|
+
python3 -c "import os; print(os.environ.get('DEBUG'))"
|
|
239
|
+
EOF
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### File copy workflow
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Copy files into sandbox, process, copy results out
|
|
246
|
+
sandsnap run python-env \
|
|
247
|
+
--copy ./data:/home/app/data \
|
|
248
|
+
--copy ./config.json:/home/app/config.json \
|
|
249
|
+
--copy-out /home/app/results:/tmp/results \
|
|
250
|
+
<<'EOF'
|
|
251
|
+
python3 process.py --config /home/app/config.json
|
|
252
|
+
EOF
|
|
253
|
+
|
|
254
|
+
# Check results
|
|
255
|
+
ls /tmp/results/
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### AI agent workflow
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Agent can use heredocs to run arbitrary commands
|
|
262
|
+
RESULT=$(sandsnap run python-env <<'EOF'
|
|
263
|
+
python3 -c "
|
|
264
|
+
import json
|
|
265
|
+
result = {'status': 'ok', 'value': 42}
|
|
266
|
+
print(json.dumps(result))
|
|
267
|
+
"
|
|
268
|
+
EOF
|
|
269
|
+
)
|
|
270
|
+
echo "$RESULT"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Notes
|
|
274
|
+
|
|
275
|
+
- Commands run as `app` user; use `sudo` for system operations
|
|
276
|
+
- Snapshots are region-specific (`ord` = Chicago, `ams` = Amsterdam)
|
|
277
|
+
- Default timeout is `session` for `run` (dies when CLI exits) and `10m` for `evolve`
|
|
278
|
+
|
|
279
|
+
## Environment Variables
|
|
280
|
+
|
|
281
|
+
| Variable | Description |
|
|
282
|
+
|----------|-------------|
|
|
283
|
+
| `DENO_DEPLOY_TOKEN` | Required. Your Deno Deploy organization token |
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer delete - Delete a snapshot (and its parent volume if possible)
|
|
3
|
+
*/
|
|
4
|
+
interface DeleteOptions {
|
|
5
|
+
force?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function deleteSnapshot(name: string, options: DeleteOptions): Promise<void>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CxF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer delete - Delete a snapshot (and its parent volume if possible)
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../lib/client.js";
|
|
5
|
+
import { confirm } from "../lib/stdin.js";
|
|
6
|
+
import { success, error, info } from "../lib/output.js";
|
|
7
|
+
export async function deleteSnapshot(name, options) {
|
|
8
|
+
const client = getClient();
|
|
9
|
+
try {
|
|
10
|
+
// Get snapshot to find its parent volume
|
|
11
|
+
const snapshot = await client.snapshots.get(name);
|
|
12
|
+
if (!snapshot) {
|
|
13
|
+
error(`Snapshot '${name}' not found.`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
// Confirm deletion unless --force
|
|
17
|
+
if (!options.force) {
|
|
18
|
+
const confirmed = await confirm(`Delete snapshot '${name}'?`);
|
|
19
|
+
if (!confirmed) {
|
|
20
|
+
console.log("Aborted.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Get parent volume info before deleting snapshot
|
|
25
|
+
const parentVolumeId = snapshot.volume?.id;
|
|
26
|
+
const parentVolumeSlug = snapshot.volume?.slug;
|
|
27
|
+
// Delete the snapshot
|
|
28
|
+
await client.snapshots.delete(name);
|
|
29
|
+
success(`Deleted snapshot '${name}'`);
|
|
30
|
+
// Try to delete parent volume (smart cleanup)
|
|
31
|
+
if (parentVolumeId && parentVolumeSlug) {
|
|
32
|
+
// Only delete if it's a temp volume we created (starts with 'tmp-')
|
|
33
|
+
if (parentVolumeSlug.startsWith('tmp-')) {
|
|
34
|
+
try {
|
|
35
|
+
await client.volumes.delete(parentVolumeId);
|
|
36
|
+
info(`Cleaned up parent volume '${parentVolumeSlug}'`);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Volume may have other snapshots depending on it, that's ok
|
|
40
|
+
info(`Parent volume '${parentVolumeSlug}' kept (may have other dependents)`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
error(`Failed to delete snapshot: ${err}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAMxD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,OAAsB;IACvE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,CAAC;QACH,yCAAyC;QACzC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,KAAK,CAAC,aAAa,IAAI,cAAc,CAAC,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,kCAAkC;QAClC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC,CAAC;YAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACxB,OAAO;YACT,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3C,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;QAE/C,sBAAsB;QACtB,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;QAEtC,8CAA8C;QAC9C,IAAI,cAAc,IAAI,gBAAgB,EAAE,CAAC;YACvC,oEAAoE;YACpE,IAAI,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;oBAC5C,IAAI,CAAC,6BAA6B,gBAAgB,GAAG,CAAC,CAAC;gBACzD,CAAC;gBAAC,MAAM,CAAC;oBACP,6DAA6D;oBAC7D,IAAI,CAAC,kBAAkB,gBAAgB,oCAAoC,CAAC,CAAC;gBAC/E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer evolve - Create a new snapshot by running commands on a base state
|
|
3
|
+
*/
|
|
4
|
+
interface EvolveOptions {
|
|
5
|
+
from?: string;
|
|
6
|
+
timeout: string;
|
|
7
|
+
capacity: string;
|
|
8
|
+
memory: string;
|
|
9
|
+
region: string;
|
|
10
|
+
env: string[];
|
|
11
|
+
copy: string[];
|
|
12
|
+
script?: string;
|
|
13
|
+
overwrite?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function evolve(name: string, options: EvolveOptions): Promise<void>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=evolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evolve.d.ts","sourceRoot":"","sources":["../../src/commands/evolve.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6IhF"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer evolve - Create a new snapshot by running commands on a base state
|
|
3
|
+
*/
|
|
4
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
5
|
+
if (value !== null && value !== void 0) {
|
|
6
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
7
|
+
var dispose, inner;
|
|
8
|
+
if (async) {
|
|
9
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
10
|
+
dispose = value[Symbol.asyncDispose];
|
|
11
|
+
}
|
|
12
|
+
if (dispose === void 0) {
|
|
13
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
14
|
+
dispose = value[Symbol.dispose];
|
|
15
|
+
if (async) inner = dispose;
|
|
16
|
+
}
|
|
17
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
18
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
19
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
20
|
+
}
|
|
21
|
+
else if (async) {
|
|
22
|
+
env.stack.push({ async: true });
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
};
|
|
26
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
27
|
+
return function (env) {
|
|
28
|
+
function fail(e) {
|
|
29
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
30
|
+
env.hasError = true;
|
|
31
|
+
}
|
|
32
|
+
var r, s = 0;
|
|
33
|
+
function next() {
|
|
34
|
+
while (r = env.stack.pop()) {
|
|
35
|
+
try {
|
|
36
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
37
|
+
if (r.dispose) {
|
|
38
|
+
var result = r.dispose.call(r.value);
|
|
39
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
40
|
+
}
|
|
41
|
+
else s |= 1;
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
fail(e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
48
|
+
if (env.hasError) throw env.error;
|
|
49
|
+
}
|
|
50
|
+
return next();
|
|
51
|
+
};
|
|
52
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
53
|
+
var e = new Error(message);
|
|
54
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
55
|
+
});
|
|
56
|
+
import { spawn } from "node:child_process";
|
|
57
|
+
import { Sandbox } from "@deno/sandbox";
|
|
58
|
+
import { getClient, snapshotExists, tempVolumeSlug } from "../lib/client.js";
|
|
59
|
+
import { isInteractive, readStdin, readScriptFile, promptOnFailure } from "../lib/stdin.js";
|
|
60
|
+
import { info, success, error, step } from "../lib/output.js";
|
|
61
|
+
import { copyToSandbox } from "../lib/copy.js";
|
|
62
|
+
import { parseCapacity, parseMemory, parseTimeout, parseRegion, parseEnvVars } from "../lib/parse.js";
|
|
63
|
+
export async function evolve(name, options) {
|
|
64
|
+
const client = getClient();
|
|
65
|
+
const volumeSlug = tempVolumeSlug();
|
|
66
|
+
// Parse and validate options
|
|
67
|
+
const capacity = parseCapacity(options.capacity);
|
|
68
|
+
const timeout = parseTimeout(options.timeout);
|
|
69
|
+
const region = parseRegion(options.region);
|
|
70
|
+
const memory = parseMemory(options.memory);
|
|
71
|
+
const env = parseEnvVars(options.env);
|
|
72
|
+
// Determine the source for the volume
|
|
73
|
+
const fromSource = options.from || "builtin:debian-13";
|
|
74
|
+
try {
|
|
75
|
+
// Check if target snapshot already exists
|
|
76
|
+
if (!options.overwrite && await snapshotExists(name)) {
|
|
77
|
+
error(`Snapshot '${name}' already exists. Use --overwrite to replace it.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// If overwriting, delete existing snapshot first
|
|
81
|
+
if (options.overwrite && await snapshotExists(name)) {
|
|
82
|
+
info(`Deleting existing snapshot '${name}'...`);
|
|
83
|
+
await client.snapshots.delete(name);
|
|
84
|
+
}
|
|
85
|
+
// Determine script source
|
|
86
|
+
let script = null;
|
|
87
|
+
const interactive = isInteractive();
|
|
88
|
+
if (options.script) {
|
|
89
|
+
script = await readScriptFile(options.script);
|
|
90
|
+
}
|
|
91
|
+
else if (!interactive) {
|
|
92
|
+
// Read from stdin (heredoc or pipe)
|
|
93
|
+
script = await readStdin();
|
|
94
|
+
}
|
|
95
|
+
// Step 1: Create bootable volume
|
|
96
|
+
step(1, 4, `Creating volume from ${options.from ? `snapshot '${options.from}'` : "base image"}...`);
|
|
97
|
+
const volume = await client.volumes.create({
|
|
98
|
+
slug: volumeSlug,
|
|
99
|
+
region: region,
|
|
100
|
+
capacity: capacity,
|
|
101
|
+
from: fromSource,
|
|
102
|
+
});
|
|
103
|
+
info(`Volume created: ${volume.id}`);
|
|
104
|
+
let shouldSnapshot = false;
|
|
105
|
+
// Step 2 & 3: Boot sandbox and execute commands
|
|
106
|
+
// Use a block so sandbox is closed before we snapshot
|
|
107
|
+
{
|
|
108
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
109
|
+
try {
|
|
110
|
+
step(2, 4, "Booting sandbox...");
|
|
111
|
+
const sandbox = __addDisposableResource(env_1, await Sandbox.create({
|
|
112
|
+
region: region,
|
|
113
|
+
root: volumeSlug,
|
|
114
|
+
timeout: timeout,
|
|
115
|
+
memory: memory,
|
|
116
|
+
env: Object.keys(env).length > 0 ? env : undefined,
|
|
117
|
+
}), true);
|
|
118
|
+
info(`Sandbox ready: ${sandbox.id}`);
|
|
119
|
+
// Copy files into sandbox
|
|
120
|
+
if (options.copy.length > 0) {
|
|
121
|
+
await copyToSandbox(sandbox, options.copy);
|
|
122
|
+
}
|
|
123
|
+
// Step 3: Execute commands
|
|
124
|
+
step(3, 4, script ? "Executing script..." : "Opening interactive shell...");
|
|
125
|
+
shouldSnapshot = true;
|
|
126
|
+
if (script) {
|
|
127
|
+
// Write script to temp file and execute
|
|
128
|
+
await sandbox.fs.writeTextFile("/tmp/sandboxer-script.sh", script);
|
|
129
|
+
try {
|
|
130
|
+
await sandbox.sh `bash /tmp/sandboxer-script.sh`;
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
shouldSnapshot = false;
|
|
134
|
+
error(`Script failed: ${err}`);
|
|
135
|
+
// Prompt user for action
|
|
136
|
+
const action = await promptOnFailure();
|
|
137
|
+
if (action === "shell") {
|
|
138
|
+
info("Opening shell for debugging...");
|
|
139
|
+
await openInteractiveShell(sandbox);
|
|
140
|
+
// After shell, ask again
|
|
141
|
+
const action2 = await promptOnFailure();
|
|
142
|
+
shouldSnapshot = action2 === "save";
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
shouldSnapshot = action === "save";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Interactive mode
|
|
151
|
+
await openInteractiveShell(sandbox);
|
|
152
|
+
shouldSnapshot = true; // User explicitly exited
|
|
153
|
+
}
|
|
154
|
+
// Explicitly kill sandbox before snapshotting
|
|
155
|
+
info("Stopping sandbox...");
|
|
156
|
+
await sandbox.kill();
|
|
157
|
+
// Wait a moment for volume to unmount
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
159
|
+
}
|
|
160
|
+
catch (e_1) {
|
|
161
|
+
env_1.error = e_1;
|
|
162
|
+
env_1.hasError = true;
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
const result_1 = __disposeResources(env_1);
|
|
166
|
+
if (result_1)
|
|
167
|
+
await result_1;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Sandbox is now stopped
|
|
171
|
+
if (!shouldSnapshot) {
|
|
172
|
+
info("Discarding changes...");
|
|
173
|
+
// Clean up volume
|
|
174
|
+
try {
|
|
175
|
+
await client.volumes.delete(volumeSlug);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Ignore
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Step 4: Create snapshot (sandbox must be closed first)
|
|
183
|
+
step(4, 4, `Creating snapshot '${name}'...`);
|
|
184
|
+
await client.volumes.snapshot(volume.id, {
|
|
185
|
+
slug: name,
|
|
186
|
+
});
|
|
187
|
+
success(`Snapshot '${name}' created!`);
|
|
188
|
+
// Note: We don't delete the volume here because the snapshot
|
|
189
|
+
// depends on it (copy-on-write). The volume is now "owned" by
|
|
190
|
+
// the snapshot and will be managed by the Deno Sandbox service.
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
error(`Failed: ${err}`);
|
|
194
|
+
// Attempt to clean up volume on error
|
|
195
|
+
try {
|
|
196
|
+
await client.volumes.delete(volumeSlug);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// Ignore cleanup errors
|
|
200
|
+
}
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Open an interactive SSH shell to the sandbox
|
|
206
|
+
*/
|
|
207
|
+
async function openInteractiveShell(sandbox) {
|
|
208
|
+
const ssh = await sandbox.exposeSsh();
|
|
209
|
+
info(`Connecting via SSH to ${ssh.username}@${ssh.hostname}...`);
|
|
210
|
+
console.log("(Type 'exit' when done)\n");
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
const sshProcess = spawn("ssh", [
|
|
213
|
+
"-o", "StrictHostKeyChecking=no",
|
|
214
|
+
"-o", "UserKnownHostsFile=/dev/null",
|
|
215
|
+
"-o", "LogLevel=ERROR",
|
|
216
|
+
"-t",
|
|
217
|
+
`${ssh.username}@${ssh.hostname}`,
|
|
218
|
+
], {
|
|
219
|
+
stdio: "inherit",
|
|
220
|
+
});
|
|
221
|
+
sshProcess.on("close", () => {
|
|
222
|
+
console.log("");
|
|
223
|
+
resolve();
|
|
224
|
+
});
|
|
225
|
+
sshProcess.on("error", reject);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=evolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evolve.js","sourceRoot":"","sources":["../../src/commands/evolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6C9D"}
|