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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer list - List all snapshots
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../lib/client.js";
|
|
5
|
+
import { table, formatBytes } from "../lib/output.js";
|
|
6
|
+
export async function list(options) {
|
|
7
|
+
const client = getClient();
|
|
8
|
+
try {
|
|
9
|
+
const page = await client.snapshots.list();
|
|
10
|
+
let snapshots = page.items;
|
|
11
|
+
// Filter by region if specified
|
|
12
|
+
if (options.region) {
|
|
13
|
+
snapshots = snapshots.filter((s) => s.region === options.region);
|
|
14
|
+
}
|
|
15
|
+
if (options.json) {
|
|
16
|
+
// Snapshot class uses getters, need to extract properties manually
|
|
17
|
+
const data = snapshots.map((s) => ({
|
|
18
|
+
id: s.id,
|
|
19
|
+
slug: s.slug,
|
|
20
|
+
region: s.region,
|
|
21
|
+
allocatedSize: s.allocatedSize,
|
|
22
|
+
flattenedSize: s.flattenedSize,
|
|
23
|
+
isBootable: s.isBootable,
|
|
24
|
+
}));
|
|
25
|
+
console.log(JSON.stringify(data, null, 2));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (snapshots.length === 0) {
|
|
29
|
+
console.log("No snapshots found.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Format as table
|
|
33
|
+
const headers = ["NAME", "REGION", "SIZE", "BOOTABLE"];
|
|
34
|
+
const rows = snapshots.map((s) => [
|
|
35
|
+
s.slug || s.id,
|
|
36
|
+
s.region,
|
|
37
|
+
formatBytes(s.allocatedSize || 0),
|
|
38
|
+
s.isBootable ? "yes" : "no",
|
|
39
|
+
]);
|
|
40
|
+
console.log(table(headers, rows));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error("Failed to list snapshots:", err);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOtD,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;QAE3B,gCAAgC;QAChC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,mEAAmE;YACnE,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,UAAU,EAAE,CAAC,CAAC,UAAU;aACzB,CAAC,CAAC,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QAED,kBAAkB;QAClB,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAChC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE;YACd,CAAC,CAAC,MAAM;YACR,WAAW,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC;YACjC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;SAC5B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer prune - Clean up orphaned volumes
|
|
3
|
+
*
|
|
4
|
+
* Orphaned volumes are temp volumes (tmp-*) that no longer have any
|
|
5
|
+
* snapshots depending on them.
|
|
6
|
+
*/
|
|
7
|
+
interface PruneOptions {
|
|
8
|
+
force?: boolean;
|
|
9
|
+
dryRun?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function prune(options: PruneOptions): Promise<void>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=prune.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../../src/commands/prune.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EhE"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer prune - Clean up orphaned volumes
|
|
3
|
+
*
|
|
4
|
+
* Orphaned volumes are temp volumes (tmp-*) that no longer have any
|
|
5
|
+
* snapshots depending on them.
|
|
6
|
+
*/
|
|
7
|
+
import { getClient } from "../lib/client.js";
|
|
8
|
+
import { confirm } from "../lib/stdin.js";
|
|
9
|
+
import { success, error, info } from "../lib/output.js";
|
|
10
|
+
export async function prune(options) {
|
|
11
|
+
const client = getClient();
|
|
12
|
+
try {
|
|
13
|
+
// Get all volumes and snapshots
|
|
14
|
+
const [volumesPage, snapshotsPage] = await Promise.all([
|
|
15
|
+
client.volumes.list(),
|
|
16
|
+
client.snapshots.list(),
|
|
17
|
+
]);
|
|
18
|
+
const volumes = volumesPage.items;
|
|
19
|
+
const snapshots = snapshotsPage.items;
|
|
20
|
+
// Build set of volume IDs that have snapshots depending on them
|
|
21
|
+
const volumesWithSnapshots = new Set();
|
|
22
|
+
for (const snap of snapshots) {
|
|
23
|
+
if (snap.volume?.id) {
|
|
24
|
+
volumesWithSnapshots.add(snap.volume.id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Find orphaned temp volumes (tmp-* with no snapshots)
|
|
28
|
+
const orphanedVolumes = volumes.filter((vol) => vol.slug.startsWith('tmp-') && !volumesWithSnapshots.has(vol.id));
|
|
29
|
+
if (orphanedVolumes.length === 0) {
|
|
30
|
+
success("No orphaned volumes to clean up.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(`Found ${orphanedVolumes.length} orphaned volume(s):`);
|
|
34
|
+
for (const vol of orphanedVolumes) {
|
|
35
|
+
console.log(` - ${vol.slug}`);
|
|
36
|
+
}
|
|
37
|
+
if (options.dryRun) {
|
|
38
|
+
info("Dry run - no volumes deleted.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Confirm unless --force
|
|
42
|
+
if (!options.force) {
|
|
43
|
+
const confirmed = await confirm(`Delete ${orphanedVolumes.length} orphaned volume(s)?`);
|
|
44
|
+
if (!confirmed) {
|
|
45
|
+
console.log("Aborted.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Delete orphaned volumes
|
|
50
|
+
let deleted = 0;
|
|
51
|
+
let failed = 0;
|
|
52
|
+
for (const vol of orphanedVolumes) {
|
|
53
|
+
try {
|
|
54
|
+
await client.volumes.delete(vol.id);
|
|
55
|
+
deleted++;
|
|
56
|
+
info(`Deleted volume '${vol.slug}'`);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
failed++;
|
|
60
|
+
error(`Failed to delete '${vol.slug}': ${err}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (deleted > 0) {
|
|
64
|
+
success(`Cleaned up ${deleted} orphaned volume(s).`);
|
|
65
|
+
}
|
|
66
|
+
if (failed > 0) {
|
|
67
|
+
error(`Failed to delete ${failed} volume(s).`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
error(`Failed to prune: ${err}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/commands/prune.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer run - Run commands in an ephemeral sandbox
|
|
3
|
+
*/
|
|
4
|
+
interface RunOptions {
|
|
5
|
+
timeout: string;
|
|
6
|
+
memory: string;
|
|
7
|
+
region: string;
|
|
8
|
+
env: string[];
|
|
9
|
+
copy: string[];
|
|
10
|
+
copyOut: string[];
|
|
11
|
+
script?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function run(snapshot: string, options: RunOptions): Promise<void>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAqG9E"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer run - Run commands in an ephemeral sandbox
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { Sandbox } from "@deno/sandbox";
|
|
6
|
+
import { snapshotExists } from "../lib/client.js";
|
|
7
|
+
import { isInteractive, readStdin, readScriptFile } from "../lib/stdin.js";
|
|
8
|
+
import { info, success, error, step } from "../lib/output.js";
|
|
9
|
+
import { copyToSandbox, copyFromSandbox } from "../lib/copy.js";
|
|
10
|
+
import { parseMemory, parseTimeout, parseRegion, parseEnvVars } from "../lib/parse.js";
|
|
11
|
+
export async function run(snapshot, options) {
|
|
12
|
+
// Parse and validate options
|
|
13
|
+
const timeout = parseTimeout(options.timeout);
|
|
14
|
+
const region = parseRegion(options.region);
|
|
15
|
+
const memory = parseMemory(options.memory);
|
|
16
|
+
const env = parseEnvVars(options.env);
|
|
17
|
+
try {
|
|
18
|
+
// Check if snapshot exists
|
|
19
|
+
const exists = await snapshotExists(snapshot);
|
|
20
|
+
if (!exists) {
|
|
21
|
+
error(`Snapshot '${snapshot}' not found. Run 'sandboxer list' to see available snapshots.`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// Determine script source
|
|
25
|
+
let script = null;
|
|
26
|
+
const interactive = isInteractive();
|
|
27
|
+
if (options.script) {
|
|
28
|
+
script = await readScriptFile(options.script);
|
|
29
|
+
}
|
|
30
|
+
else if (!interactive) {
|
|
31
|
+
// Read from stdin (heredoc or pipe)
|
|
32
|
+
script = await readStdin();
|
|
33
|
+
}
|
|
34
|
+
// Boot ephemeral sandbox from snapshot
|
|
35
|
+
step(1, 2, `Booting sandbox from snapshot '${snapshot}'...`);
|
|
36
|
+
const sandbox = await Sandbox.create({
|
|
37
|
+
region: region,
|
|
38
|
+
root: snapshot,
|
|
39
|
+
timeout: timeout,
|
|
40
|
+
memory: memory,
|
|
41
|
+
env: Object.keys(env).length > 0 ? env : undefined,
|
|
42
|
+
});
|
|
43
|
+
info(`Sandbox ready: ${sandbox.id}`);
|
|
44
|
+
// Copy files into sandbox
|
|
45
|
+
if (options.copy.length > 0) {
|
|
46
|
+
await copyToSandbox(sandbox, options.copy);
|
|
47
|
+
}
|
|
48
|
+
// Setup cleanup on Ctrl+C
|
|
49
|
+
const cleanup = async () => {
|
|
50
|
+
info("\nCleaning up sandbox...");
|
|
51
|
+
try {
|
|
52
|
+
await sandbox.kill();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Ignore errors during cleanup
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
process.on("SIGINT", async () => {
|
|
59
|
+
await cleanup();
|
|
60
|
+
process.exit(130);
|
|
61
|
+
});
|
|
62
|
+
process.on("SIGTERM", async () => {
|
|
63
|
+
await cleanup();
|
|
64
|
+
process.exit(143);
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
// Execute commands
|
|
68
|
+
step(2, 2, script ? "Executing script..." : "Opening interactive shell...");
|
|
69
|
+
if (script) {
|
|
70
|
+
// Write script to temp file and execute
|
|
71
|
+
await sandbox.fs.writeTextFile("/tmp/sandsnap-script.sh", script);
|
|
72
|
+
try {
|
|
73
|
+
await sandbox.sh `bash /tmp/sandsnap-script.sh`;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
77
|
+
error(`Script failed with exit code ${err.code}`);
|
|
78
|
+
// Still try to copy out files before exiting
|
|
79
|
+
if (options.copyOut.length > 0) {
|
|
80
|
+
await copyFromSandbox(sandbox, options.copyOut);
|
|
81
|
+
}
|
|
82
|
+
await cleanup();
|
|
83
|
+
process.exit(typeof err.code === "number" ? err.code : 1);
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Interactive mode
|
|
90
|
+
await openInteractiveShell(sandbox);
|
|
91
|
+
}
|
|
92
|
+
// Copy files out from sandbox
|
|
93
|
+
if (options.copyOut.length > 0) {
|
|
94
|
+
await copyFromSandbox(sandbox, options.copyOut);
|
|
95
|
+
}
|
|
96
|
+
success(script ? "Script completed successfully." : "Session ended.");
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
// Always cleanup
|
|
100
|
+
await cleanup();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
error(`Failed: ${err}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Open an interactive SSH shell to the sandbox
|
|
110
|
+
*/
|
|
111
|
+
async function openInteractiveShell(sandbox) {
|
|
112
|
+
const ssh = await sandbox.exposeSsh();
|
|
113
|
+
info(`Connecting via SSH to ${ssh.username}@${ssh.hostname}...`);
|
|
114
|
+
console.log("(Type 'exit' when done)\n");
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const sshProcess = spawn("ssh", [
|
|
117
|
+
"-o", "StrictHostKeyChecking=no",
|
|
118
|
+
"-o", "UserKnownHostsFile=/dev/null",
|
|
119
|
+
"-o", "LogLevel=ERROR",
|
|
120
|
+
"-t",
|
|
121
|
+
`${ssh.username}@${ssh.hostname}`,
|
|
122
|
+
], {
|
|
123
|
+
stdio: "inherit",
|
|
124
|
+
});
|
|
125
|
+
sshProcess.on("close", () => {
|
|
126
|
+
console.log("");
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
sshProcess.on("error", reject);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer sandboxes - List and manage running sandboxes
|
|
3
|
+
*/
|
|
4
|
+
interface SandboxesOptions {
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function listSandboxes(options: SandboxesOptions): Promise<void>;
|
|
8
|
+
export declare function killSandbox(id: string): Promise<void>;
|
|
9
|
+
export declare function killAllSandboxes(): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=sandboxes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxes.d.ts","sourceRoot":"","sources":["../../src/commands/sandboxes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC5E;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU3D;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CA4BtD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandboxer sandboxes - List and manage running sandboxes
|
|
3
|
+
*/
|
|
4
|
+
import { getClient } from "../lib/client.js";
|
|
5
|
+
import { table, formatDate } from "../lib/output.js";
|
|
6
|
+
import { success, error, info } from "../lib/output.js";
|
|
7
|
+
export async function listSandboxes(options) {
|
|
8
|
+
const client = getClient();
|
|
9
|
+
try {
|
|
10
|
+
const sandboxes = await client.sandboxes.list();
|
|
11
|
+
if (options.json) {
|
|
12
|
+
const data = sandboxes.map((s) => ({
|
|
13
|
+
id: s.id,
|
|
14
|
+
region: s.region,
|
|
15
|
+
status: s.status,
|
|
16
|
+
createdAt: s.createdAt.toISOString(),
|
|
17
|
+
labels: s.labels,
|
|
18
|
+
}));
|
|
19
|
+
console.log(JSON.stringify(data, null, 2));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (sandboxes.length === 0) {
|
|
23
|
+
console.log("No running sandboxes.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const headers = ["ID", "REGION", "STATUS", "CREATED"];
|
|
27
|
+
const rows = sandboxes.map((s) => [
|
|
28
|
+
s.id,
|
|
29
|
+
s.region,
|
|
30
|
+
s.status,
|
|
31
|
+
formatDate(s.createdAt),
|
|
32
|
+
]);
|
|
33
|
+
console.log(table(headers, rows));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
error(`Failed to list sandboxes: ${err}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function killSandbox(id) {
|
|
41
|
+
try {
|
|
42
|
+
const { Sandbox } = await import("@deno/sandbox");
|
|
43
|
+
const sandbox = await Sandbox.connect(id);
|
|
44
|
+
await sandbox.kill();
|
|
45
|
+
success(`Killed sandbox ${id}`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
error(`Failed to kill sandbox: ${err}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function killAllSandboxes() {
|
|
53
|
+
const client = getClient();
|
|
54
|
+
try {
|
|
55
|
+
const allSandboxes = await client.sandboxes.list();
|
|
56
|
+
const sandboxes = allSandboxes.filter((s) => s.status === "running");
|
|
57
|
+
if (sandboxes.length === 0) {
|
|
58
|
+
info("No running sandboxes to kill.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
info(`Killing ${sandboxes.length} sandbox(es)...`);
|
|
62
|
+
const { Sandbox } = await import("@deno/sandbox");
|
|
63
|
+
for (const s of sandboxes) {
|
|
64
|
+
try {
|
|
65
|
+
const sandbox = await Sandbox.connect(s.id);
|
|
66
|
+
await sandbox.kill();
|
|
67
|
+
success(`Killed ${s.id}`);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
error(`Failed to kill ${s.id}: ${err}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
error(`Failed: ${err}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=sandboxes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxes.js","sourceRoot":"","sources":["../../src/commands/sandboxes.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from "commander";
|
|
3
|
+
import { evolve } from "./commands/evolve.js";
|
|
4
|
+
import { run } from "./commands/run.js";
|
|
5
|
+
import { list } from "./commands/list.js";
|
|
6
|
+
import { deleteSnapshot } from "./commands/delete.js";
|
|
7
|
+
import { prune } from "./commands/prune.js";
|
|
8
|
+
import { listSandboxes, killSandbox, killAllSandboxes } from "./commands/sandboxes.js";
|
|
9
|
+
import { setVerbose } from "./lib/output.js";
|
|
10
|
+
program
|
|
11
|
+
.name("sandsnap")
|
|
12
|
+
.description("CLI for managing Deno Sandbox environments with snapshot-based workflows")
|
|
13
|
+
.version("0.1.0")
|
|
14
|
+
.option("-v, --verbose", "Show detailed progress logs")
|
|
15
|
+
.hook("preAction", (thisCommand) => {
|
|
16
|
+
const opts = thisCommand.opts();
|
|
17
|
+
if (opts.verbose) {
|
|
18
|
+
setVerbose(true);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
program
|
|
22
|
+
.command("evolve <name>")
|
|
23
|
+
.description("Create a new snapshot by running commands on a base state")
|
|
24
|
+
.option("--from <snapshot>", "Base snapshot to evolve from (default: debian-13 base image)")
|
|
25
|
+
.option("--timeout <duration>", "Sandbox timeout (e.g., 10m, 30m)", "10m")
|
|
26
|
+
.option("--capacity <size>", "Volume capacity (e.g., 2GiB, 10GiB)", "10GiB")
|
|
27
|
+
.option("--memory <size>", "Sandbox memory (e.g., 1GiB, 2GiB, 4GiB)", "1280MiB")
|
|
28
|
+
.option("--region <region>", "Region (ord or ams)", "ord")
|
|
29
|
+
.option("--env <KEY=VALUE>", "Set environment variable (repeatable)", collect, [])
|
|
30
|
+
.option("--copy <src:dst>", "Copy file/dir from host to sandbox (repeatable)", collect, [])
|
|
31
|
+
.option("--script <file>", "Read commands from script file")
|
|
32
|
+
.option("--overwrite", "Overwrite existing snapshot with same name")
|
|
33
|
+
.action(evolve);
|
|
34
|
+
function collect(value, previous) {
|
|
35
|
+
return previous.concat([value]);
|
|
36
|
+
}
|
|
37
|
+
program
|
|
38
|
+
.command("run <snapshot>")
|
|
39
|
+
.description("Run commands in an ephemeral sandbox (changes discarded)")
|
|
40
|
+
.option("--timeout <duration>", "Sandbox timeout (e.g., 5m, 10m, session)", "session")
|
|
41
|
+
.option("--memory <size>", "Sandbox memory (e.g., 1GiB, 2GiB, 4GiB)", "1280MiB")
|
|
42
|
+
.option("--region <region>", "Region (ord or ams)", "ord")
|
|
43
|
+
.option("--env <KEY=VALUE>", "Set environment variable (repeatable)", collect, [])
|
|
44
|
+
.option("--copy <src:dst>", "Copy file/dir from host to sandbox (repeatable)", collect, [])
|
|
45
|
+
.option("--copy-out <src:dst>", "Copy file/dir from sandbox to host after execution (repeatable)", collect, [])
|
|
46
|
+
.option("--script <file>", "Read commands from script file")
|
|
47
|
+
.action(run);
|
|
48
|
+
program
|
|
49
|
+
.command("list")
|
|
50
|
+
.description("List all snapshots")
|
|
51
|
+
.option("--region <region>", "Filter by region")
|
|
52
|
+
.option("--json", "Output as JSON")
|
|
53
|
+
.action(list);
|
|
54
|
+
program
|
|
55
|
+
.command("delete <name>")
|
|
56
|
+
.description("Delete a snapshot")
|
|
57
|
+
.option("--force", "Skip confirmation prompt")
|
|
58
|
+
.action(deleteSnapshot);
|
|
59
|
+
program
|
|
60
|
+
.command("prune")
|
|
61
|
+
.description("Clean up orphaned temporary volumes")
|
|
62
|
+
.option("--force", "Skip confirmation prompt")
|
|
63
|
+
.option("--dry-run", "Show what would be deleted without deleting")
|
|
64
|
+
.action(prune);
|
|
65
|
+
// Sandbox management (for debugging/cleanup)
|
|
66
|
+
const sandboxesCmd = program
|
|
67
|
+
.command("sandboxes")
|
|
68
|
+
.description("Manage running sandboxes");
|
|
69
|
+
sandboxesCmd
|
|
70
|
+
.command("list")
|
|
71
|
+
.description("List running sandboxes")
|
|
72
|
+
.option("--json", "Output as JSON")
|
|
73
|
+
.action(listSandboxes);
|
|
74
|
+
sandboxesCmd
|
|
75
|
+
.command("kill <id>")
|
|
76
|
+
.description("Kill a running sandbox")
|
|
77
|
+
.action(killSandbox);
|
|
78
|
+
sandboxesCmd
|
|
79
|
+
.command("kill-all")
|
|
80
|
+
.description("Kill all running sandboxes")
|
|
81
|
+
.action(killAllSandboxes);
|
|
82
|
+
program.parse();
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared client instance and utilities
|
|
3
|
+
*/
|
|
4
|
+
import { Client } from "@deno/sandbox";
|
|
5
|
+
export declare function getClient(): Client;
|
|
6
|
+
/**
|
|
7
|
+
* Check if a snapshot exists
|
|
8
|
+
*/
|
|
9
|
+
export declare function snapshotExists(slug: string): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a unique temporary volume slug (max 32 chars)
|
|
12
|
+
*/
|
|
13
|
+
export declare function tempVolumeSlug(): string;
|
|
14
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAIvC,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAInE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAMvC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared client instance and utilities
|
|
3
|
+
*/
|
|
4
|
+
import { Client } from "@deno/sandbox";
|
|
5
|
+
let _client = null;
|
|
6
|
+
export function getClient() {
|
|
7
|
+
if (!_client) {
|
|
8
|
+
_client = new Client();
|
|
9
|
+
}
|
|
10
|
+
return _client;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Check if a snapshot exists
|
|
14
|
+
*/
|
|
15
|
+
export async function snapshotExists(slug) {
|
|
16
|
+
const client = getClient();
|
|
17
|
+
const snapshot = await client.snapshots.get(slug);
|
|
18
|
+
return snapshot !== null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate a unique temporary volume slug (max 32 chars)
|
|
22
|
+
*/
|
|
23
|
+
export function tempVolumeSlug() {
|
|
24
|
+
// Format: tmp-{timestamp-base36}-{random}
|
|
25
|
+
// Keep under 32 chars
|
|
26
|
+
const ts = Date.now().toString(36); // ~8 chars
|
|
27
|
+
const rand = Math.random().toString(36).slice(2, 8); // 6 chars
|
|
28
|
+
return `tmp-${ts}-${rand}`; // ~18 chars
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,IAAI,OAAO,GAAkB,IAAI,CAAC;AAElC,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,QAAQ,KAAK,IAAI,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,0CAA0C;IAC1C,sBAAsB;IACtB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU;IAC/D,OAAO,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,YAAY;AAC1C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File copy utilities for sandbox operations
|
|
3
|
+
*/
|
|
4
|
+
import type { Sandbox } from "@deno/sandbox";
|
|
5
|
+
export type { CopySpec } from "./parse.js";
|
|
6
|
+
/**
|
|
7
|
+
* Copy files from host to sandbox
|
|
8
|
+
*/
|
|
9
|
+
export declare function copyToSandbox(sandbox: Sandbox, specs: string[]): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Copy files from sandbox to host
|
|
12
|
+
*/
|
|
13
|
+
export declare function copyFromSandbox(sandbox: Sandbox, specs: string[]): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=copy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../src/lib/copy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|