@sandblocks/cli 0.5.0-b.0 → 0.5.0-b.2
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/dist/cli.js +280 -162
- package/dist/cli.js.map +5 -4
- package/dist/runtime.js +118 -0
- package/package.json +4 -3
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/runtime.ts
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { access, chmod, mkdir, readdir, rm } from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
var RUNTIME_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
|
|
9
|
+
var NODE_ENV_RUNTIME = {
|
|
10
|
+
production: "prod",
|
|
11
|
+
development: "dev",
|
|
12
|
+
test: "local"
|
|
13
|
+
};
|
|
14
|
+
async function runRuntimeCommand(args) {
|
|
15
|
+
const [action, ...rest] = args;
|
|
16
|
+
if (action !== "prepare")
|
|
17
|
+
throw new Error("runtime action must be prepare");
|
|
18
|
+
const options = parse(rest);
|
|
19
|
+
const root = path.resolve(options.positionals[0] ?? process.cwd());
|
|
20
|
+
const runtime = normalizedRuntime(option(options, "runtime") ?? process.env.SANDBLOCKS_RUNTIME ?? "development" ?? "development");
|
|
21
|
+
const targetRoot = contained(root, option(options, "target") ?? ".locker", "runtime target");
|
|
22
|
+
const runtimeRoot = path.join(targetRoot, runtime);
|
|
23
|
+
const config = contained(root, option(options, "config") ?? "config/dotlocker.config.ts", "Dotlocker config");
|
|
24
|
+
const dotlocker = path.resolve(root, option(options, "dotlocker") ?? path.join("node_modules", ".bin", "dotlocker"));
|
|
25
|
+
if (!await exists(config))
|
|
26
|
+
throw new Error(`Dotlocker config does not exist: ${config}`);
|
|
27
|
+
if (!await exists(dotlocker)) {
|
|
28
|
+
throw new Error("Dotlocker is not installed; add @dotlocker/dotlocker to the workspace");
|
|
29
|
+
}
|
|
30
|
+
await rm(targetRoot, { recursive: true, force: true });
|
|
31
|
+
await mkdir(runtimeRoot, { recursive: true, mode: 493 });
|
|
32
|
+
await run(dotlocker, ["pull", "--config", config, "--runtime", runtime, "--out", runtimeRoot], root);
|
|
33
|
+
const envFile = path.join(runtimeRoot, ".env");
|
|
34
|
+
if (!await exists(envFile)) {
|
|
35
|
+
throw new Error(`Dotlocker did not materialize ${path.relative(root, envFile)}`);
|
|
36
|
+
}
|
|
37
|
+
await normalizePermissions(targetRoot);
|
|
38
|
+
console.log(`runtime ${runtime}
|
|
39
|
+
environment ${path.relative(root, envFile)}`);
|
|
40
|
+
}
|
|
41
|
+
function run(executable, args, cwd) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const child = spawn(executable, args, { cwd, env: process.env, stdio: "inherit" });
|
|
44
|
+
child.once("error", reject);
|
|
45
|
+
child.once("exit", (code, signal) => {
|
|
46
|
+
if (code === 0)
|
|
47
|
+
resolve();
|
|
48
|
+
else
|
|
49
|
+
reject(new Error(`Dotlocker pull failed (${signal ? `signal ${signal}` : `exit ${code ?? "unknown"}`})`));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async function exists(file) {
|
|
54
|
+
try {
|
|
55
|
+
await access(file);
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function normalizedRuntime(value) {
|
|
62
|
+
const runtime = NODE_ENV_RUNTIME[value.trim()] ?? value.trim();
|
|
63
|
+
if (!RUNTIME_PATTERN.test(runtime))
|
|
64
|
+
throw new Error(`invalid runtime '${value}'`);
|
|
65
|
+
return runtime;
|
|
66
|
+
}
|
|
67
|
+
function contained(root, value, label) {
|
|
68
|
+
const resolved = path.resolve(root, value);
|
|
69
|
+
const relative = path.relative(root, resolved);
|
|
70
|
+
if (!relative || !relative.startsWith("..") && !path.isAbsolute(relative))
|
|
71
|
+
return resolved;
|
|
72
|
+
throw new Error(`${label} must remain within the repository`);
|
|
73
|
+
}
|
|
74
|
+
async function normalizePermissions(directory) {
|
|
75
|
+
await chmod(directory, 493);
|
|
76
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
77
|
+
const child = path.join(directory, entry.name);
|
|
78
|
+
if (entry.isSymbolicLink())
|
|
79
|
+
throw new Error("Dotlocker output must not contain symbolic links");
|
|
80
|
+
if (entry.isDirectory())
|
|
81
|
+
await normalizePermissions(child);
|
|
82
|
+
else if (entry.isFile())
|
|
83
|
+
await chmod(child, 420);
|
|
84
|
+
else
|
|
85
|
+
throw new Error("Dotlocker output contains an unsupported file type");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function parse(args) {
|
|
89
|
+
const options = { positionals: [] };
|
|
90
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
91
|
+
const value = args[index] ?? "";
|
|
92
|
+
if (!value.startsWith("--")) {
|
|
93
|
+
options.positionals.push(value);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const key = value.slice(2);
|
|
97
|
+
const next = args[index + 1];
|
|
98
|
+
if (next && !next.startsWith("--")) {
|
|
99
|
+
options[key] = next;
|
|
100
|
+
index += 1;
|
|
101
|
+
} else
|
|
102
|
+
options[key] = true;
|
|
103
|
+
}
|
|
104
|
+
return options;
|
|
105
|
+
}
|
|
106
|
+
function option(options, key) {
|
|
107
|
+
const value = options[key];
|
|
108
|
+
return typeof value === "string" ? value : undefined;
|
|
109
|
+
}
|
|
110
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
111
|
+
runRuntimeCommand(process.argv.slice(2)).catch((error) => {
|
|
112
|
+
console.error(`sandblocks-runtime: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
runRuntimeCommand
|
|
118
|
+
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sandblocks/cli",
|
|
3
|
-
"version": "0.5.0-b.
|
|
3
|
+
"version": "0.5.0-b.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Sandblocks command-line interface for workspaces, deployments, and agent sandboxes.",
|
|
6
6
|
"bin": {
|
|
7
|
-
"sandblocks": "dist/cli.js"
|
|
7
|
+
"sandblocks": "dist/cli.js",
|
|
8
|
+
"sandblocks-runtime": "dist/runtime.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"dist"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"start": "bun src/cli.ts",
|
|
14
|
-
"build": "rm -rf dist && bun build src/cli.ts --outdir dist --target=bun --format=esm --sourcemap=external",
|
|
15
|
+
"build": "rm -rf dist && bun build src/cli.ts --outdir dist --target=bun --format=esm --sourcemap=external && bun build src/runtime.ts --outfile dist/runtime.js --target=node --format=esm",
|
|
15
16
|
"test": "bun test",
|
|
16
17
|
"prepack": "bun run build && bun run test"
|
|
17
18
|
},
|