@sandblocks/cli 0.4.0 → 0.5.0-b.1
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 +239 -146
- package/dist/cli.js.map +5 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11735,7 +11735,7 @@ var init_src = __esm(() => {
|
|
|
11735
11735
|
init_src();
|
|
11736
11736
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
11737
11737
|
import { lstat, readFile as readFile8, stat as stat3, writeFile as writeFile5 } from "fs/promises";
|
|
11738
|
-
import
|
|
11738
|
+
import path6 from "path";
|
|
11739
11739
|
|
|
11740
11740
|
// src/hooks.ts
|
|
11741
11741
|
import { chmod, copyFile, mkdir, readFile as readFile3, rm, writeFile } from "fs/promises";
|
|
@@ -11877,11 +11877,100 @@ async function text(file) {
|
|
|
11877
11877
|
}
|
|
11878
11878
|
}
|
|
11879
11879
|
|
|
11880
|
+
// src/runtime.ts
|
|
11881
|
+
import { chmod as chmod2, mkdir as mkdir2, readdir, rm as rm2 } from "fs/promises";
|
|
11882
|
+
import path4 from "path";
|
|
11883
|
+
var RUNTIME_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
|
|
11884
|
+
var NODE_ENV_RUNTIME = {
|
|
11885
|
+
production: "prod",
|
|
11886
|
+
development: "dev",
|
|
11887
|
+
test: "local"
|
|
11888
|
+
};
|
|
11889
|
+
async function runRuntimeCommand(args) {
|
|
11890
|
+
const [action, ...rest] = args;
|
|
11891
|
+
if (action !== "prepare")
|
|
11892
|
+
throw new Error("runtime action must be prepare");
|
|
11893
|
+
const options = parse(rest);
|
|
11894
|
+
const root = path4.resolve(options.positionals[0] ?? process.cwd());
|
|
11895
|
+
const runtime = normalizedRuntime(option(options, "runtime") ?? process.env.SANDBLOCKS_RUNTIME ?? "development" ?? "development");
|
|
11896
|
+
const targetRoot = contained(root, option(options, "target") ?? ".locker", "runtime target");
|
|
11897
|
+
const runtimeRoot = path4.join(targetRoot, runtime);
|
|
11898
|
+
const config = contained(root, option(options, "config") ?? "config/dotlocker.config.ts", "Dotlocker config");
|
|
11899
|
+
const dotlocker = path4.resolve(root, option(options, "dotlocker") ?? path4.join("node_modules", ".bin", "dotlocker"));
|
|
11900
|
+
if (!await Bun.file(config).exists())
|
|
11901
|
+
throw new Error(`Dotlocker config does not exist: ${config}`);
|
|
11902
|
+
if (!await Bun.file(dotlocker).exists()) {
|
|
11903
|
+
throw new Error("Dotlocker is not installed; add @dotlocker/dotlocker to the workspace");
|
|
11904
|
+
}
|
|
11905
|
+
await rm2(targetRoot, { recursive: true, force: true });
|
|
11906
|
+
await mkdir2(runtimeRoot, { recursive: true, mode: 493 });
|
|
11907
|
+
const child = Bun.spawn([dotlocker, "pull", "--config", config, "--runtime", runtime, "--out", runtimeRoot], { cwd: root, env: process.env, stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
11908
|
+
const exitCode = await child.exited;
|
|
11909
|
+
if (exitCode !== 0)
|
|
11910
|
+
throw new Error(`Dotlocker pull failed with exit code ${exitCode}`);
|
|
11911
|
+
const envFile = path4.join(runtimeRoot, ".env");
|
|
11912
|
+
if (!await Bun.file(envFile).exists()) {
|
|
11913
|
+
throw new Error(`Dotlocker did not materialize ${path4.relative(root, envFile)}`);
|
|
11914
|
+
}
|
|
11915
|
+
await normalizePermissions(targetRoot);
|
|
11916
|
+
console.log(`runtime ${runtime}
|
|
11917
|
+
environment ${path4.relative(root, envFile)}`);
|
|
11918
|
+
}
|
|
11919
|
+
function normalizedRuntime(value) {
|
|
11920
|
+
const runtime = NODE_ENV_RUNTIME[value.trim()] ?? value.trim();
|
|
11921
|
+
if (!RUNTIME_PATTERN.test(runtime))
|
|
11922
|
+
throw new Error(`invalid runtime '${value}'`);
|
|
11923
|
+
return runtime;
|
|
11924
|
+
}
|
|
11925
|
+
function contained(root, value, label) {
|
|
11926
|
+
const resolved = path4.resolve(root, value);
|
|
11927
|
+
const relative = path4.relative(root, resolved);
|
|
11928
|
+
if (!relative || !relative.startsWith("..") && !path4.isAbsolute(relative))
|
|
11929
|
+
return resolved;
|
|
11930
|
+
throw new Error(`${label} must remain within the repository`);
|
|
11931
|
+
}
|
|
11932
|
+
async function normalizePermissions(directory) {
|
|
11933
|
+
await chmod2(directory, 493);
|
|
11934
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
11935
|
+
const child = path4.join(directory, entry.name);
|
|
11936
|
+
if (entry.isSymbolicLink())
|
|
11937
|
+
throw new Error("Dotlocker output must not contain symbolic links");
|
|
11938
|
+
if (entry.isDirectory())
|
|
11939
|
+
await normalizePermissions(child);
|
|
11940
|
+
else if (entry.isFile())
|
|
11941
|
+
await chmod2(child, 420);
|
|
11942
|
+
else
|
|
11943
|
+
throw new Error("Dotlocker output contains an unsupported file type");
|
|
11944
|
+
}
|
|
11945
|
+
}
|
|
11946
|
+
function parse(args) {
|
|
11947
|
+
const options = { positionals: [] };
|
|
11948
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
11949
|
+
const value = args[index] ?? "";
|
|
11950
|
+
if (!value.startsWith("--")) {
|
|
11951
|
+
options.positionals.push(value);
|
|
11952
|
+
continue;
|
|
11953
|
+
}
|
|
11954
|
+
const key = value.slice(2);
|
|
11955
|
+
const next = args[index + 1];
|
|
11956
|
+
if (next && !next.startsWith("--")) {
|
|
11957
|
+
options[key] = next;
|
|
11958
|
+
index += 1;
|
|
11959
|
+
} else
|
|
11960
|
+
options[key] = true;
|
|
11961
|
+
}
|
|
11962
|
+
return options;
|
|
11963
|
+
}
|
|
11964
|
+
function option(options, key) {
|
|
11965
|
+
const value = options[key];
|
|
11966
|
+
return typeof value === "string" ? value : undefined;
|
|
11967
|
+
}
|
|
11968
|
+
|
|
11880
11969
|
// src/sandbox.ts
|
|
11881
11970
|
init_src();
|
|
11882
11971
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
11883
|
-
import { chmod as
|
|
11884
|
-
import
|
|
11972
|
+
import { chmod as chmod3, mkdir as mkdir3, readFile as readFile4, rename, rm as rm3 } from "fs/promises";
|
|
11973
|
+
import path5 from "path";
|
|
11885
11974
|
var COMMANDS = new Set([
|
|
11886
11975
|
"up",
|
|
11887
11976
|
"create",
|
|
@@ -11933,7 +12022,7 @@ async function up(args, dependencies) {
|
|
|
11933
12022
|
}
|
|
11934
12023
|
async function create(args, dependencies) {
|
|
11935
12024
|
const options = dependencies.parse(args);
|
|
11936
|
-
const root =
|
|
12025
|
+
const root = path5.resolve(options.positionals[0] ?? process.cwd());
|
|
11937
12026
|
await loadLocalEnvironment(root);
|
|
11938
12027
|
const environment = dependencies.option(options, "environment") ?? "preview";
|
|
11939
12028
|
const file = statePath(root, environment);
|
|
@@ -11942,7 +12031,7 @@ async function create(args, dependencies) {
|
|
|
11942
12031
|
const loaded = await dependencies.load(root, dependencies.option(options, "config") ?? dependencies.option(options, "manifest"));
|
|
11943
12032
|
const preview = selectPreview(loaded, environment);
|
|
11944
12033
|
const projectId = dependencies.option(options, "project") ?? process.env.SANDBLOCKS_PROJECT_ID ?? "default";
|
|
11945
|
-
const repository = loaded.manifest.name ??
|
|
12034
|
+
const repository = loaded.manifest.name ?? path5.basename(root).toLowerCase();
|
|
11946
12035
|
const sandboxId = randomUUID();
|
|
11947
12036
|
const workspaceId = `${sandboxId.replaceAll("-", "").slice(0, 8)}-${repository}`.slice(0, 63);
|
|
11948
12037
|
const { apiUrl, apiKey } = client(options, dependencies.option, projectId);
|
|
@@ -12010,7 +12099,7 @@ async function status2(args, dependencies) {
|
|
|
12010
12099
|
console.log(JSON.stringify({ state: redactedState(context.state), sandbox: sandbox.sandbox, session: session.session }, null, 2));
|
|
12011
12100
|
return;
|
|
12012
12101
|
}
|
|
12013
|
-
console.log(`state ${
|
|
12102
|
+
console.log(`state ${path5.relative(context.root, context.file)}`);
|
|
12014
12103
|
console.log(`sandbox ${context.state.sandboxId} (${sandbox.sandbox.status})`);
|
|
12015
12104
|
console.log(`workspace ${context.state.workspaceId}`);
|
|
12016
12105
|
console.log(`host ${context.state.hostId}`);
|
|
@@ -12023,12 +12112,12 @@ async function deploy(args, dependencies) {
|
|
|
12023
12112
|
}
|
|
12024
12113
|
async function withDeploymentLock(args, dependencies, action) {
|
|
12025
12114
|
const options = dependencies.parse(args);
|
|
12026
|
-
const root =
|
|
12115
|
+
const root = path5.resolve(options.positionals[0] ?? process.cwd());
|
|
12027
12116
|
const environment = dependencies.option(options, "environment") ?? "preview";
|
|
12028
12117
|
const lock = `${statePath(root, environment)}.deploy.lock`;
|
|
12029
|
-
await
|
|
12118
|
+
await mkdir3(path5.dirname(lock), { recursive: true, mode: 448 });
|
|
12030
12119
|
try {
|
|
12031
|
-
await
|
|
12120
|
+
await mkdir3(lock);
|
|
12032
12121
|
} catch (error) {
|
|
12033
12122
|
if (error.code === "EEXIST") {
|
|
12034
12123
|
throw new Error(`sandbox '${environment}' deployment is already running`);
|
|
@@ -12038,7 +12127,7 @@ async function withDeploymentLock(args, dependencies, action) {
|
|
|
12038
12127
|
try {
|
|
12039
12128
|
await action();
|
|
12040
12129
|
} finally {
|
|
12041
|
-
await
|
|
12130
|
+
await rm3(lock, { recursive: true, force: true });
|
|
12042
12131
|
}
|
|
12043
12132
|
}
|
|
12044
12133
|
async function deployUnlocked(args, dependencies) {
|
|
@@ -12271,7 +12360,7 @@ async function down(args, dependencies) {
|
|
|
12271
12360
|
await request(context.apiUrl, context.apiKey, `/v1/projects/${encodeURIComponent(context.state.projectId)}/sandboxes/${context.state.sandboxId}`, { method: "DELETE" }).catch(() => {
|
|
12272
12361
|
return;
|
|
12273
12362
|
});
|
|
12274
|
-
await
|
|
12363
|
+
await rm3(context.file, { force: true });
|
|
12275
12364
|
console.log(`sandbox ${context.state.sandboxId} destroyed`);
|
|
12276
12365
|
}
|
|
12277
12366
|
async function resolveAssignedTargetHost(context, fallback) {
|
|
@@ -12406,7 +12495,7 @@ async function dataOperation(context, action, body) {
|
|
|
12406
12495
|
}
|
|
12407
12496
|
async function getContext(args, dependencies) {
|
|
12408
12497
|
const options = dependencies.parse(args);
|
|
12409
|
-
const root =
|
|
12498
|
+
const root = path5.resolve(options.positionals[0] ?? process.cwd());
|
|
12410
12499
|
await loadLocalEnvironment(root);
|
|
12411
12500
|
const environment = dependencies.option(options, "environment") ?? "preview";
|
|
12412
12501
|
const file = statePath(root, environment);
|
|
@@ -12422,9 +12511,9 @@ async function getContext(args, dependencies) {
|
|
|
12422
12511
|
function selectPreview(loaded, id2) {
|
|
12423
12512
|
return resolveEnvironment(loaded.manifest, id2);
|
|
12424
12513
|
}
|
|
12425
|
-
function client(options,
|
|
12426
|
-
const apiUrl = (
|
|
12427
|
-
const apiKey =
|
|
12514
|
+
function client(options, option2, projectId) {
|
|
12515
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
12516
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY ?? "";
|
|
12428
12517
|
if (!apiUrl || !apiKey)
|
|
12429
12518
|
throw new Error("SANDBLOCKS_API_URL and SANDBLOCKS_API_KEY are required");
|
|
12430
12519
|
return { apiUrl, apiKey, projectId };
|
|
@@ -12525,7 +12614,7 @@ async function waitLease(context, operationId) {
|
|
|
12525
12614
|
throw new Error(`sandbox operation ${operationId} timed out`);
|
|
12526
12615
|
}
|
|
12527
12616
|
async function loadLocalEnvironment(root) {
|
|
12528
|
-
const file =
|
|
12617
|
+
const file = path5.join(root, ".sandblocks", "config.env");
|
|
12529
12618
|
let contents;
|
|
12530
12619
|
try {
|
|
12531
12620
|
contents = await readFile4(file, "utf8");
|
|
@@ -12589,15 +12678,15 @@ function resolvedServiceDomain(sandboxId, deploymentId, service, preview) {
|
|
|
12589
12678
|
function statePath(root, environment) {
|
|
12590
12679
|
if (!/^[a-z][a-z0-9-]{0,62}$/.test(environment))
|
|
12591
12680
|
throw new Error("sandbox environment is invalid");
|
|
12592
|
-
return
|
|
12681
|
+
return path5.join(root, ".sandblocks", `sandbox-${environment}.json`);
|
|
12593
12682
|
}
|
|
12594
12683
|
async function writeState(file, state) {
|
|
12595
|
-
await
|
|
12596
|
-
await
|
|
12684
|
+
await mkdir3(path5.dirname(file), { recursive: true, mode: 448 });
|
|
12685
|
+
await chmod3(path5.dirname(file), 448);
|
|
12597
12686
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
12598
12687
|
await Bun.write(temporary, `${JSON.stringify(state, null, 2)}
|
|
12599
12688
|
`, { mode: 384 });
|
|
12600
|
-
await
|
|
12689
|
+
await chmod3(temporary, 384);
|
|
12601
12690
|
await rename(temporary, file);
|
|
12602
12691
|
}
|
|
12603
12692
|
function redactedState(state) {
|
|
@@ -12613,12 +12702,12 @@ function printOutput(operation) {
|
|
|
12613
12702
|
}
|
|
12614
12703
|
|
|
12615
12704
|
// src/sdk.ts
|
|
12616
|
-
import { mkdir as
|
|
12705
|
+
import { mkdir as mkdir5, readFile as readFile7, rm as rm6, writeFile as writeFile4 } from "fs/promises";
|
|
12617
12706
|
import { dirname as dirname2, resolve as resolve7 } from "path";
|
|
12618
12707
|
|
|
12619
12708
|
// ../sdk/dist/index.js
|
|
12620
12709
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
12621
|
-
import { appendFile, cp, mkdir as
|
|
12710
|
+
import { appendFile, cp, mkdir as mkdir4, readFile as readFile5, rm as rm4 } from "fs/promises";
|
|
12622
12711
|
import { dirname, resolve } from "path";
|
|
12623
12712
|
import { spawn } from "child_process";
|
|
12624
12713
|
import { access, mkdir as mkdir22, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -13062,7 +13151,7 @@ class SandblocksSandbox {
|
|
|
13062
13151
|
"--force",
|
|
13063
13152
|
this.git.worktree
|
|
13064
13153
|
]);
|
|
13065
|
-
await
|
|
13154
|
+
await rm4(this.git.worktree, { recursive: true, force: true });
|
|
13066
13155
|
}
|
|
13067
13156
|
}
|
|
13068
13157
|
async[Symbol.asyncDispose]() {
|
|
@@ -13092,7 +13181,7 @@ async function createSandbox(options) {
|
|
|
13092
13181
|
for (const file of options.copyToWorktree ?? []) {
|
|
13093
13182
|
const source = resolve(hostCwd, file);
|
|
13094
13183
|
const destination = resolve(git3.runtimeCwd, file);
|
|
13095
|
-
await
|
|
13184
|
+
await mkdir4(dirname(destination), { recursive: true });
|
|
13096
13185
|
await withTimeout(cp(source, destination, { recursive: true }), options.timeouts?.copyToWorktreeMs ?? 60000, `copying '${file}'`);
|
|
13097
13186
|
}
|
|
13098
13187
|
const id2 = options.id ?? randomUUID2();
|
|
@@ -13161,7 +13250,7 @@ async function prepareGit(hostCwd, strategy, timeoutMs = 30000) {
|
|
|
13161
13250
|
}
|
|
13162
13251
|
const branch = strategy.name ?? `sandblocks/${randomUUID2().slice(0, 8)}`;
|
|
13163
13252
|
const worktree = resolve(hostCwd, ".sandblocks", "worktrees", branch.replace(/[^A-Za-z0-9_.-]/g, "-"));
|
|
13164
|
-
await
|
|
13253
|
+
await mkdir4(dirname(worktree), { recursive: true });
|
|
13165
13254
|
await checked(["git", "-C", hostCwd, "worktree", "add", "-B", branch, worktree, "HEAD"], timeoutMs);
|
|
13166
13255
|
return {
|
|
13167
13256
|
hostCwd,
|
|
@@ -13244,7 +13333,7 @@ function createLogger(options, cwd) {
|
|
|
13244
13333
|
process.stdout.write(event.type === "text" ? event.text : event.type === "raw" ? `${event.line}
|
|
13245
13334
|
` : line);
|
|
13246
13335
|
if (config.type !== "stdout" && config.type !== "silent") {
|
|
13247
|
-
await
|
|
13336
|
+
await mkdir4(dirname(file), { recursive: true });
|
|
13248
13337
|
await appendFile(file, line);
|
|
13249
13338
|
}
|
|
13250
13339
|
try {
|
|
@@ -13315,12 +13404,12 @@ SANDBLOCKS_API_KEY=
|
|
|
13315
13404
|
};
|
|
13316
13405
|
const written = [];
|
|
13317
13406
|
for (const [relative, content] of Object.entries(files)) {
|
|
13318
|
-
const
|
|
13319
|
-
await mkdir22(resolve2(
|
|
13320
|
-
if (!options.force && await access(
|
|
13407
|
+
const path6 = resolve2(root, relative);
|
|
13408
|
+
await mkdir22(resolve2(path6, ".."), { recursive: true });
|
|
13409
|
+
if (!options.force && await access(path6).then(() => true).catch(() => false))
|
|
13321
13410
|
continue;
|
|
13322
|
-
await writeFile2(
|
|
13323
|
-
written.push(
|
|
13411
|
+
await writeFile2(path6, content, { mode: relative.includes(".env") ? 384 : 420 });
|
|
13412
|
+
written.push(path6);
|
|
13324
13413
|
}
|
|
13325
13414
|
return written;
|
|
13326
13415
|
}
|
|
@@ -13390,12 +13479,12 @@ async function executeProcess2(command, options = {}) {
|
|
|
13390
13479
|
options.signal?.removeEventListener("abort", stop);
|
|
13391
13480
|
}
|
|
13392
13481
|
}
|
|
13393
|
-
function expandHome(
|
|
13394
|
-
if (
|
|
13395
|
-
return process.env.HOME ??
|
|
13396
|
-
if (
|
|
13397
|
-
return `${process.env.HOME ?? "~"}/${
|
|
13398
|
-
return
|
|
13482
|
+
function expandHome(path6) {
|
|
13483
|
+
if (path6 === "~")
|
|
13484
|
+
return process.env.HOME ?? path6;
|
|
13485
|
+
if (path6.startsWith("~/"))
|
|
13486
|
+
return `${process.env.HOME ?? "~"}/${path6.slice(2)}`;
|
|
13487
|
+
return path6;
|
|
13399
13488
|
}
|
|
13400
13489
|
var capabilities = {
|
|
13401
13490
|
bindMounts: true,
|
|
@@ -13636,12 +13725,12 @@ async function executeProcess3(command, options = {}) {
|
|
|
13636
13725
|
options.signal?.removeEventListener("abort", stop);
|
|
13637
13726
|
}
|
|
13638
13727
|
}
|
|
13639
|
-
function expandHome2(
|
|
13640
|
-
if (
|
|
13641
|
-
return process.env.HOME ??
|
|
13642
|
-
if (
|
|
13643
|
-
return `${process.env.HOME ?? "~"}/${
|
|
13644
|
-
return
|
|
13728
|
+
function expandHome2(path6) {
|
|
13729
|
+
if (path6 === "~")
|
|
13730
|
+
return process.env.HOME ?? path6;
|
|
13731
|
+
if (path6.startsWith("~/"))
|
|
13732
|
+
return `${process.env.HOME ?? "~"}/${path6.slice(2)}`;
|
|
13733
|
+
return path6;
|
|
13645
13734
|
}
|
|
13646
13735
|
var capabilities2 = {
|
|
13647
13736
|
bindMounts: true,
|
|
@@ -13819,7 +13908,7 @@ class PodmanProvider extends OciProvider2 {
|
|
|
13819
13908
|
|
|
13820
13909
|
// ../sdk/dist/providers/remote.js
|
|
13821
13910
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
13822
|
-
import { mkdtemp, readFile as readFile6, rm as
|
|
13911
|
+
import { mkdtemp, readFile as readFile6, rm as rm5, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
13823
13912
|
import { tmpdir } from "os";
|
|
13824
13913
|
import { join } from "path";
|
|
13825
13914
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -14097,12 +14186,12 @@ class RemoteSandblocksProvider {
|
|
|
14097
14186
|
if (result.exitCode !== 0)
|
|
14098
14187
|
throw new Error(result.stderr || "remote operation failed");
|
|
14099
14188
|
}
|
|
14100
|
-
async request(
|
|
14189
|
+
async request(path6, init = {}) {
|
|
14101
14190
|
const headers = new Headers(init.headers);
|
|
14102
14191
|
headers.set("x-sandblocks-api-key", this.options.apiKey);
|
|
14103
14192
|
if (init.body)
|
|
14104
14193
|
headers.set("content-type", "application/json");
|
|
14105
|
-
const response = await this.fetchImpl(`${this.baseUrl}${
|
|
14194
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path6}`, { ...init, headers });
|
|
14106
14195
|
const body = await response.json().catch(() => ({}));
|
|
14107
14196
|
if (!response.ok)
|
|
14108
14197
|
throw new Error(body.error ?? `Sandblocks request failed (${response.status})`);
|
|
@@ -14126,7 +14215,7 @@ async function sourceBundle(cwd) {
|
|
|
14126
14215
|
]);
|
|
14127
14216
|
if (listed.exitCode !== 0)
|
|
14128
14217
|
throw new Error("remote source directory must be a Git worktree");
|
|
14129
|
-
const files = listed.stdout.split("\x00").filter((
|
|
14218
|
+
const files = listed.stdout.split("\x00").filter((path6) => path6 && path6 !== ".sandblocks" && !path6.startsWith(".sandblocks/") && path6 !== ".git");
|
|
14130
14219
|
if (!files.length || files.length > 50000)
|
|
14131
14220
|
throw new Error("remote source bundle file count is invalid");
|
|
14132
14221
|
await writeFile3(list, `${files.join("\x00")}\x00`);
|
|
@@ -14137,7 +14226,7 @@ async function sourceBundle(cwd) {
|
|
|
14137
14226
|
throw new Error("remote source bundle exceeds 512 MiB");
|
|
14138
14227
|
return new Uint8Array(await readFile6(file));
|
|
14139
14228
|
} finally {
|
|
14140
|
-
await
|
|
14229
|
+
await rm5(temporary, { recursive: true, force: true });
|
|
14141
14230
|
}
|
|
14142
14231
|
}
|
|
14143
14232
|
|
|
@@ -14317,7 +14406,7 @@ async function sdkSandbox(args, helpers, defaultCwd) {
|
|
|
14317
14406
|
hooks: hooksFrom(options),
|
|
14318
14407
|
copyToWorktree: multi(options["copy-to-worktree"])
|
|
14319
14408
|
});
|
|
14320
|
-
await
|
|
14409
|
+
await mkdir5(dirname2(stateFile), { recursive: true });
|
|
14321
14410
|
const strategy = branchFrom(options, helpers);
|
|
14322
14411
|
const worktree = strategy.type === "worktree" || strategy.type === "merge-to-head" ? sandbox2.cwd : undefined;
|
|
14323
14412
|
await writeFile4(stateFile, JSON.stringify({
|
|
@@ -14392,7 +14481,7 @@ async function sdkSandbox(args, helpers, defaultCwd) {
|
|
|
14392
14481
|
const child = Bun.spawn(["git", "-C", state.cwd, "worktree", "remove", "--force", state.worktree]);
|
|
14393
14482
|
await child.exited;
|
|
14394
14483
|
}
|
|
14395
|
-
await
|
|
14484
|
+
await rm6(stateFile, { force: true });
|
|
14396
14485
|
console.log(`sandbox ${state.id} destroyed`);
|
|
14397
14486
|
} else
|
|
14398
14487
|
throw new Error("unsupported sdk sandbox command");
|
|
@@ -14504,10 +14593,10 @@ function hooksFrom(options) {
|
|
|
14504
14593
|
};
|
|
14505
14594
|
}
|
|
14506
14595
|
async function withSdkConfig(options, cwd, file) {
|
|
14507
|
-
const
|
|
14508
|
-
if (!await Bun.file(
|
|
14596
|
+
const path6 = resolve7(cwd, file ?? ".sandblocks/config.json");
|
|
14597
|
+
if (!await Bun.file(path6).exists())
|
|
14509
14598
|
return options;
|
|
14510
|
-
const config = JSON.parse(await readFile7(
|
|
14599
|
+
const config = JSON.parse(await readFile7(path6, "utf8"));
|
|
14511
14600
|
const mapped = {};
|
|
14512
14601
|
if (typeof config.provider === "string")
|
|
14513
14602
|
mapped.provider = config.provider;
|
|
@@ -14575,6 +14664,8 @@ var HELP = `Sandblocks CLI
|
|
|
14575
14664
|
sandblocks whoami [directory] [--api-url <url>] [--api-key <key>] [--json]
|
|
14576
14665
|
sandblocks hooks <install|status|uninstall> [directory]
|
|
14577
14666
|
sandblocks hooks run post-commit [directory]
|
|
14667
|
+
sandblocks runtime prepare [directory] [--runtime <id>]
|
|
14668
|
+
[--config <path>] [--target <path>]
|
|
14578
14669
|
sandblocks stack <list|get|settings|settings-set> --project <id> [--stack <id>] [--file <json>] [--json]
|
|
14579
14670
|
sandblocks app <list|get|settings|settings-set> --project <id> [--stack <id>] [--app <id>] [--file <json>] [--json]
|
|
14580
14671
|
sandblocks secret <list|set|delete> --project <id> --stack <id> --app <id>
|
|
@@ -14634,8 +14725,8 @@ async function run2(argv = process.argv.slice(2)) {
|
|
|
14634
14725
|
await runHooksCommand(args, {
|
|
14635
14726
|
load,
|
|
14636
14727
|
runSandbox: (hookCommand2, hookArgs) => runStatefulSandboxCommand(hookCommand2, hookArgs, {
|
|
14637
|
-
parse,
|
|
14638
|
-
option,
|
|
14728
|
+
parse: parse2,
|
|
14729
|
+
option: option2,
|
|
14639
14730
|
load,
|
|
14640
14731
|
source: async (root) => {
|
|
14641
14732
|
const bundle = await createSourceTar(root);
|
|
@@ -14643,6 +14734,8 @@ async function run2(argv = process.argv.slice(2)) {
|
|
|
14643
14734
|
}
|
|
14644
14735
|
})
|
|
14645
14736
|
});
|
|
14737
|
+
else if (command === "runtime")
|
|
14738
|
+
await runRuntimeCommand(args);
|
|
14646
14739
|
else if (command === "stack" || command === "app")
|
|
14647
14740
|
await resource(command, args);
|
|
14648
14741
|
else if (command === "secret")
|
|
@@ -14650,7 +14743,7 @@ async function run2(argv = process.argv.slice(2)) {
|
|
|
14650
14743
|
else if (command === "sandbox")
|
|
14651
14744
|
await sandbox(args);
|
|
14652
14745
|
else if (command === "sdk")
|
|
14653
|
-
await runSdkCommand(args, { parse, option });
|
|
14746
|
+
await runSdkCommand(args, { parse: parse2, option: option2 });
|
|
14654
14747
|
else {
|
|
14655
14748
|
console.log(HELP);
|
|
14656
14749
|
if (!["help", "--help", "-h"].includes(command))
|
|
@@ -14658,9 +14751,9 @@ async function run2(argv = process.argv.slice(2)) {
|
|
|
14658
14751
|
}
|
|
14659
14752
|
}
|
|
14660
14753
|
async function configure(args) {
|
|
14661
|
-
const options =
|
|
14662
|
-
const root =
|
|
14663
|
-
const manifestPath =
|
|
14754
|
+
const options = parse2(args);
|
|
14755
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
14756
|
+
const manifestPath = path6.join(root, "sandblocks.yml");
|
|
14664
14757
|
if (await Bun.file(manifestPath).exists() && !options.force) {
|
|
14665
14758
|
const loaded2 = await load(root, manifestPathOption(options));
|
|
14666
14759
|
console.log(`Sandblocks already configured
|
|
@@ -14668,8 +14761,8 @@ async function configure(args) {
|
|
|
14668
14761
|
digest: ${loaded2.digest}`);
|
|
14669
14762
|
return;
|
|
14670
14763
|
}
|
|
14671
|
-
const packageJson = await json(
|
|
14672
|
-
const name = slug(
|
|
14764
|
+
const packageJson = await json(path6.join(root, "package.json"));
|
|
14765
|
+
const name = slug(option2(options, "name") ?? path6.basename(root));
|
|
14673
14766
|
const checks = ["build", "typecheck", "test"].filter((script) => packageJson?.scripts?.[script]);
|
|
14674
14767
|
const manager = packageJson?.packageManager?.split("@")[0] ?? "npm";
|
|
14675
14768
|
const command = (script) => `[${manager}, ${manager === "npm" ? "run, " : ""}${script}]`;
|
|
@@ -14690,8 +14783,8 @@ async function configure(args) {
|
|
|
14690
14783
|
digest: ${loaded.digest}`);
|
|
14691
14784
|
}
|
|
14692
14785
|
async function validate(args) {
|
|
14693
|
-
const options =
|
|
14694
|
-
const root =
|
|
14786
|
+
const options = parse2(args);
|
|
14787
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
14695
14788
|
const loaded = await load(root, manifestPathOption(options));
|
|
14696
14789
|
if (options.json) {
|
|
14697
14790
|
console.log(JSON.stringify({ manifest: loaded.entrypoint, digest: loaded.digest, sources: loaded.sources }, null, 2));
|
|
@@ -14701,11 +14794,11 @@ async function validate(args) {
|
|
|
14701
14794
|
digest ${loaded.digest}`);
|
|
14702
14795
|
}
|
|
14703
14796
|
async function register(args) {
|
|
14704
|
-
const options =
|
|
14705
|
-
const root =
|
|
14706
|
-
const projectId =
|
|
14707
|
-
const apiUrl = (
|
|
14708
|
-
const apiKey =
|
|
14797
|
+
const options = parse2(args);
|
|
14798
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
14799
|
+
const projectId = option2(options, "project");
|
|
14800
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
14801
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14709
14802
|
if (!projectId)
|
|
14710
14803
|
throw new Error("register requires --project");
|
|
14711
14804
|
if (!apiUrl)
|
|
@@ -14713,7 +14806,7 @@ async function register(args) {
|
|
|
14713
14806
|
if (!apiKey)
|
|
14714
14807
|
throw new Error("register requires --api-key or SANDBLOCKS_API_KEY");
|
|
14715
14808
|
const loaded = await load(root, manifestPathOption(options));
|
|
14716
|
-
const repositoryUrl =
|
|
14809
|
+
const repositoryUrl = option2(options, "repository-url") ?? await git3(root, ["config", "--get", "remote.origin.url"]);
|
|
14717
14810
|
if (!repositoryUrl)
|
|
14718
14811
|
throw new Error("register requires --repository-url or git remote.origin.url");
|
|
14719
14812
|
const branch = await git3(root, ["branch", "--show-current"]) || "main";
|
|
@@ -14721,7 +14814,7 @@ async function register(args) {
|
|
|
14721
14814
|
method: "POST",
|
|
14722
14815
|
headers: { "content-type": "application/json", "x-sandblocks-api-key": apiKey },
|
|
14723
14816
|
body: JSON.stringify({
|
|
14724
|
-
slug: loaded.manifest.name ?? slug(
|
|
14817
|
+
slug: loaded.manifest.name ?? slug(path6.basename(root)),
|
|
14725
14818
|
name: loaded.manifest.name,
|
|
14726
14819
|
url: repositoryUrl,
|
|
14727
14820
|
defaultBranch: branch,
|
|
@@ -14737,24 +14830,24 @@ async function register(args) {
|
|
|
14737
14830
|
console.log(response.status === 409 ? "repository already registered" : "repository registered");
|
|
14738
14831
|
}
|
|
14739
14832
|
async function doctor(args) {
|
|
14740
|
-
const options =
|
|
14741
|
-
const root =
|
|
14833
|
+
const options = parse2(args);
|
|
14834
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
14742
14835
|
const loaded = await load(root, manifestPathOption(options));
|
|
14743
14836
|
console.log(`manifest ok (${loaded.entrypoint})`);
|
|
14744
|
-
const apiUrl = (
|
|
14837
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
14745
14838
|
if (apiUrl) {
|
|
14746
14839
|
const response = await fetch(`${apiUrl}/v1/health`);
|
|
14747
14840
|
if (!response.ok)
|
|
14748
14841
|
throw new Error(`API health failed (${response.status})`);
|
|
14749
14842
|
console.log(`api ok (${apiUrl})`);
|
|
14750
14843
|
}
|
|
14751
|
-
const gitDetected = await stat3(
|
|
14844
|
+
const gitDetected = await stat3(path6.join(root, ".git")).then(() => true).catch(() => false);
|
|
14752
14845
|
console.log(`git ${gitDetected ? "ok" : "not detected"}`);
|
|
14753
14846
|
}
|
|
14754
14847
|
async function whoami(args) {
|
|
14755
|
-
const options =
|
|
14756
|
-
const apiUrl = (
|
|
14757
|
-
const apiKey =
|
|
14848
|
+
const options = parse2(args);
|
|
14849
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
14850
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14758
14851
|
if (!apiUrl)
|
|
14759
14852
|
throw new Error("whoami requires --api-url or SANDBLOCKS_API_URL");
|
|
14760
14853
|
if (!apiKey)
|
|
@@ -14777,12 +14870,12 @@ async function whoami(args) {
|
|
|
14777
14870
|
}
|
|
14778
14871
|
async function resource(kind, args) {
|
|
14779
14872
|
const [action = "list", ...rest] = args;
|
|
14780
|
-
const options =
|
|
14781
|
-
const projectId =
|
|
14782
|
-
const stack =
|
|
14783
|
-
const app =
|
|
14784
|
-
const apiUrl = (
|
|
14785
|
-
const apiKey =
|
|
14873
|
+
const options = parse2(rest);
|
|
14874
|
+
const projectId = option2(options, "project");
|
|
14875
|
+
const stack = option2(options, "stack");
|
|
14876
|
+
const app = option2(options, "app");
|
|
14877
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
14878
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14786
14879
|
if (!projectId)
|
|
14787
14880
|
throw new Error(`${kind} ${action} requires --project`);
|
|
14788
14881
|
if (!apiUrl)
|
|
@@ -14802,10 +14895,10 @@ async function resource(kind, args) {
|
|
|
14802
14895
|
throw new Error(`unsupported ${kind} command`);
|
|
14803
14896
|
let body;
|
|
14804
14897
|
if (action === "settings-set") {
|
|
14805
|
-
const file =
|
|
14898
|
+
const file = option2(options, "file");
|
|
14806
14899
|
if (!file)
|
|
14807
14900
|
throw new Error(`${kind} settings-set requires --file <json>`);
|
|
14808
|
-
const values = JSON.parse(await readFile8(
|
|
14901
|
+
const values = JSON.parse(await readFile8(path6.resolve(file), "utf8"));
|
|
14809
14902
|
const current = await sandblocksRequest(apiUrl, apiKey, requestPath);
|
|
14810
14903
|
body = await sandblocksRequest(apiUrl, apiKey, requestPath, {
|
|
14811
14904
|
method: "PUT",
|
|
@@ -14820,12 +14913,12 @@ async function resource(kind, args) {
|
|
|
14820
14913
|
}
|
|
14821
14914
|
async function secret(args) {
|
|
14822
14915
|
const [action = "list", ...rest] = args;
|
|
14823
|
-
const options =
|
|
14824
|
-
const projectId =
|
|
14825
|
-
const stack =
|
|
14826
|
-
const app =
|
|
14827
|
-
const apiUrl = (
|
|
14828
|
-
const apiKey =
|
|
14916
|
+
const options = parse2(rest);
|
|
14917
|
+
const projectId = option2(options, "project");
|
|
14918
|
+
const stack = option2(options, "stack");
|
|
14919
|
+
const app = option2(options, "app");
|
|
14920
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
14921
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14829
14922
|
if (!projectId || !stack || !app)
|
|
14830
14923
|
throw new Error("secret commands require --project, --stack, and --app");
|
|
14831
14924
|
if (!apiUrl || !apiKey)
|
|
@@ -14835,9 +14928,9 @@ async function secret(args) {
|
|
|
14835
14928
|
if (action === "list")
|
|
14836
14929
|
body = await sandblocksRequest(apiUrl, apiKey, root);
|
|
14837
14930
|
else if (action === "set") {
|
|
14838
|
-
const environment =
|
|
14839
|
-
const name =
|
|
14840
|
-
const value =
|
|
14931
|
+
const environment = option2(options, "environment");
|
|
14932
|
+
const name = option2(options, "name");
|
|
14933
|
+
const value = option2(options, "value");
|
|
14841
14934
|
if (!environment || !name || value === undefined)
|
|
14842
14935
|
throw new Error("secret set requires --environment, --name, and --value");
|
|
14843
14936
|
body = await sandblocksRequest(apiUrl, apiKey, root, {
|
|
@@ -14845,7 +14938,7 @@ async function secret(args) {
|
|
|
14845
14938
|
body: JSON.stringify({ environment, name, value })
|
|
14846
14939
|
});
|
|
14847
14940
|
} else if (action === "delete") {
|
|
14848
|
-
const id2 =
|
|
14941
|
+
const id2 = option2(options, "id");
|
|
14849
14942
|
if (!id2)
|
|
14850
14943
|
throw new Error("secret delete requires --id");
|
|
14851
14944
|
await sandblocksRequest(apiUrl, apiKey, `${root}/${encodeURIComponent(id2)}`, { method: "DELETE" });
|
|
@@ -14885,8 +14978,8 @@ async function sandbox(args) {
|
|
|
14885
14978
|
const [subcommand, ...rest] = args;
|
|
14886
14979
|
if (isStatefulSandboxCommand(subcommand) && !(subcommand === "deploy" && rest.includes("--workspace"))) {
|
|
14887
14980
|
return runStatefulSandboxCommand(subcommand, rest, {
|
|
14888
|
-
parse,
|
|
14889
|
-
option,
|
|
14981
|
+
parse: parse2,
|
|
14982
|
+
option: option2,
|
|
14890
14983
|
load,
|
|
14891
14984
|
source: async (root2) => {
|
|
14892
14985
|
const bundle2 = await createSourceTar(root2);
|
|
@@ -14902,12 +14995,12 @@ async function sandbox(args) {
|
|
|
14902
14995
|
return sandboxAgent(rest);
|
|
14903
14996
|
if (subcommand !== "import")
|
|
14904
14997
|
throw new Error("unsupported sandbox command");
|
|
14905
|
-
const options =
|
|
14906
|
-
const root =
|
|
14907
|
-
const projectId =
|
|
14908
|
-
const workspaceId =
|
|
14909
|
-
const apiUrl = (
|
|
14910
|
-
const apiKey =
|
|
14998
|
+
const options = parse2(rest);
|
|
14999
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
15000
|
+
const projectId = option2(options, "project");
|
|
15001
|
+
const workspaceId = option2(options, "workspace");
|
|
15002
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
15003
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14911
15004
|
if (!projectId)
|
|
14912
15005
|
throw new Error("sandbox import requires --project");
|
|
14913
15006
|
if (!workspaceId)
|
|
@@ -14924,8 +15017,8 @@ async function sandbox(args) {
|
|
|
14924
15017
|
"content-length": String(bundle.byteLength),
|
|
14925
15018
|
"x-sandblocks-api-key": apiKey,
|
|
14926
15019
|
"x-sandblocks-workspace-id": workspaceId,
|
|
14927
|
-
"x-sandblocks-worker-pool":
|
|
14928
|
-
"idempotency-key":
|
|
15020
|
+
"x-sandblocks-worker-pool": option2(options, "pool") ?? "sandbox-development",
|
|
15021
|
+
"idempotency-key": option2(options, "idempotency-key") ?? `local-import:${workspaceId}`
|
|
14929
15022
|
},
|
|
14930
15023
|
body: new Blob([Uint8Array.from(bundle)])
|
|
14931
15024
|
});
|
|
@@ -14945,12 +15038,12 @@ async function sandbox(args) {
|
|
|
14945
15038
|
}
|
|
14946
15039
|
}
|
|
14947
15040
|
async function sandboxAgent(args) {
|
|
14948
|
-
const options =
|
|
15041
|
+
const options = parse2(args);
|
|
14949
15042
|
const action = options.positionals[0] ?? "list";
|
|
14950
|
-
const projectId =
|
|
14951
|
-
const sandboxId =
|
|
14952
|
-
const apiUrl = (
|
|
14953
|
-
const apiKey =
|
|
15043
|
+
const projectId = option2(options, "project");
|
|
15044
|
+
const sandboxId = option2(options, "sandbox");
|
|
15045
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
15046
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
14954
15047
|
if (!projectId || !sandboxId)
|
|
14955
15048
|
throw new Error("sandbox agent requires --project and --sandbox");
|
|
14956
15049
|
if (!apiUrl || !apiKey)
|
|
@@ -14958,25 +15051,25 @@ async function sandboxAgent(args) {
|
|
|
14958
15051
|
const collection = `/v1/projects/${encodeURIComponent(projectId)}/sandboxes/${encodeURIComponent(sandboxId)}/agent-sessions`;
|
|
14959
15052
|
let result;
|
|
14960
15053
|
if (action === "run") {
|
|
14961
|
-
const promptFile =
|
|
14962
|
-
const prompt =
|
|
15054
|
+
const promptFile = option2(options, "prompt-file");
|
|
15055
|
+
const prompt = option2(options, "prompt") ?? (promptFile ? await readFile8(path6.resolve(promptFile), "utf8") : undefined);
|
|
14963
15056
|
if (!prompt)
|
|
14964
15057
|
throw new Error("sandbox agent run requires --prompt or --prompt-file");
|
|
14965
|
-
const repository =
|
|
14966
|
-
const provider =
|
|
14967
|
-
const model =
|
|
15058
|
+
const repository = option2(options, "repository");
|
|
15059
|
+
const provider = option2(options, "provider") ?? "codex";
|
|
15060
|
+
const model = option2(options, "model");
|
|
14968
15061
|
if (!repository || !model)
|
|
14969
15062
|
throw new Error("sandbox agent run requires --repository and --model");
|
|
14970
15063
|
result = await sandblocksRequest(apiUrl, apiKey, collection, {
|
|
14971
15064
|
method: "POST",
|
|
14972
|
-
headers: { "idempotency-key":
|
|
15065
|
+
headers: { "idempotency-key": option2(options, "idempotency-key") ?? randomUUID4() },
|
|
14973
15066
|
body: JSON.stringify({
|
|
14974
15067
|
repository,
|
|
14975
15068
|
provider,
|
|
14976
15069
|
model,
|
|
14977
15070
|
prompt,
|
|
14978
|
-
credentialEnvironment:
|
|
14979
|
-
timeoutSeconds:
|
|
15071
|
+
credentialEnvironment: option2(options, "credential-env"),
|
|
15072
|
+
timeoutSeconds: option2(options, "timeout-seconds") ? Number(option2(options, "timeout-seconds")) : undefined
|
|
14980
15073
|
})
|
|
14981
15074
|
});
|
|
14982
15075
|
if (options.wait)
|
|
@@ -14984,12 +15077,12 @@ async function sandboxAgent(args) {
|
|
|
14984
15077
|
} else if (action === "list") {
|
|
14985
15078
|
result = await sandblocksRequest(apiUrl, apiKey, collection);
|
|
14986
15079
|
} else if (action === "get" || action === "cancel" || action === "redeploy") {
|
|
14987
|
-
const sessionId =
|
|
15080
|
+
const sessionId = option2(options, "session");
|
|
14988
15081
|
if (!sessionId)
|
|
14989
15082
|
throw new Error(`sandbox agent ${action} requires --session`);
|
|
14990
15083
|
result = await sandblocksRequest(apiUrl, apiKey, `${collection}/${encodeURIComponent(sessionId)}${action === "cancel" ? "/cancel" : action === "redeploy" ? "/redeploy" : ""}`, action === "cancel" || action === "redeploy" ? {
|
|
14991
15084
|
method: "POST",
|
|
14992
|
-
headers: action === "redeploy" ? { "idempotency-key":
|
|
15085
|
+
headers: action === "redeploy" ? { "idempotency-key": option2(options, "idempotency-key") ?? randomUUID4() } : undefined
|
|
14993
15086
|
} : undefined);
|
|
14994
15087
|
} else
|
|
14995
15088
|
throw new Error("sandbox agent action must be run, list, get, cancel, or redeploy");
|
|
@@ -15002,20 +15095,20 @@ async function sandboxAgent(args) {
|
|
|
15002
15095
|
}
|
|
15003
15096
|
}
|
|
15004
15097
|
async function waitForAgentSession(apiUrl, apiKey, projectId, sandboxId, sessionId) {
|
|
15005
|
-
const
|
|
15098
|
+
const path7 = `/v1/projects/${encodeURIComponent(projectId)}/sandboxes/${encodeURIComponent(sandboxId)}/agent-sessions/${encodeURIComponent(sessionId)}`;
|
|
15006
15099
|
for (;; ) {
|
|
15007
|
-
const result = await sandblocksRequest(apiUrl, apiKey,
|
|
15100
|
+
const result = await sandblocksRequest(apiUrl, apiKey, path7);
|
|
15008
15101
|
if (!["queued", "running"].includes(String(result.session?.state)))
|
|
15009
15102
|
return result.session;
|
|
15010
15103
|
await Bun.sleep(1000);
|
|
15011
15104
|
}
|
|
15012
15105
|
}
|
|
15013
15106
|
async function sandboxPromote(args) {
|
|
15014
|
-
const options =
|
|
15015
|
-
const projectId =
|
|
15016
|
-
const sandboxId =
|
|
15017
|
-
const apiUrl = (
|
|
15018
|
-
const apiKey =
|
|
15107
|
+
const options = parse2(args);
|
|
15108
|
+
const projectId = option2(options, "project");
|
|
15109
|
+
const sandboxId = option2(options, "sandbox");
|
|
15110
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
15111
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
15019
15112
|
if (!projectId)
|
|
15020
15113
|
throw new Error("sandbox promote requires --project");
|
|
15021
15114
|
if (!sandboxId)
|
|
@@ -15057,7 +15150,7 @@ async function createSourceTar(root) {
|
|
|
15057
15150
|
if (name.includes("\x00") || name.includes("\\") || name.startsWith("/") || name.split("/").includes("..")) {
|
|
15058
15151
|
throw new Error(`local source path is unsafe: ${name}`);
|
|
15059
15152
|
}
|
|
15060
|
-
const file =
|
|
15153
|
+
const file = path6.join(root, name);
|
|
15061
15154
|
let info;
|
|
15062
15155
|
try {
|
|
15063
15156
|
info = await lstat(file);
|
|
@@ -15150,25 +15243,25 @@ function tarOctal(header, offset, length, value) {
|
|
|
15150
15243
|
header[offset + length - 1] = 0;
|
|
15151
15244
|
}
|
|
15152
15245
|
async function sandboxDeploy(args) {
|
|
15153
|
-
const options =
|
|
15154
|
-
const root =
|
|
15155
|
-
const projectId =
|
|
15156
|
-
const workspaceId =
|
|
15157
|
-
const sandboxId =
|
|
15158
|
-
const hostId =
|
|
15159
|
-
const targetHost =
|
|
15160
|
-
const specPath =
|
|
15161
|
-
const apiUrl = (
|
|
15162
|
-
const apiKey =
|
|
15246
|
+
const options = parse2(args);
|
|
15247
|
+
const root = path6.resolve(options.positionals[0] ?? process.cwd());
|
|
15248
|
+
const projectId = option2(options, "project");
|
|
15249
|
+
const workspaceId = option2(options, "workspace");
|
|
15250
|
+
const sandboxId = option2(options, "sandbox");
|
|
15251
|
+
const hostId = option2(options, "host");
|
|
15252
|
+
const targetHost = option2(options, "target-host");
|
|
15253
|
+
const specPath = option2(options, "spec");
|
|
15254
|
+
const apiUrl = (option2(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
|
|
15255
|
+
const apiKey = option2(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
|
|
15163
15256
|
if (!projectId || !workspaceId || !sandboxId || !hostId || !targetHost || !specPath) {
|
|
15164
15257
|
throw new Error("sandbox deploy requires --project, --workspace, --sandbox, --host, --target-host, and --spec");
|
|
15165
15258
|
}
|
|
15166
15259
|
if (!apiUrl || !apiKey)
|
|
15167
15260
|
throw new Error("sandbox deploy requires Sandblocks API URL and key");
|
|
15168
|
-
const spec = JSON.parse(await readFile8(
|
|
15261
|
+
const spec = JSON.parse(await readFile8(path6.resolve(root, specPath), "utf8"));
|
|
15169
15262
|
if (!Array.isArray(spec.services) || !spec.services.length)
|
|
15170
15263
|
throw new Error("preview spec requires services");
|
|
15171
|
-
const deploymentId =
|
|
15264
|
+
const deploymentId = option2(options, "deployment") ?? randomUUID4();
|
|
15172
15265
|
const submitted = await sandblocksRequest(apiUrl, apiKey, `/v1/projects/${encodeURIComponent(projectId)}/operations`, {
|
|
15173
15266
|
method: "POST",
|
|
15174
15267
|
headers: { "idempotency-key": `preview:${deploymentId}:deploy` },
|
|
@@ -15249,7 +15342,7 @@ async function waitForOperation(apiUrl, apiKey, id2) {
|
|
|
15249
15342
|
async function load(root, entrypoint) {
|
|
15250
15343
|
return loadSandblocksManifest({ repositoryRoot: root, ...entrypoint ? { entrypoint } : {} });
|
|
15251
15344
|
}
|
|
15252
|
-
function
|
|
15345
|
+
function parse2(args) {
|
|
15253
15346
|
const output = { positionals: [] };
|
|
15254
15347
|
for (let index = 0;index < args.length; index++) {
|
|
15255
15348
|
const value = args[index];
|
|
@@ -15287,11 +15380,11 @@ function parse(args) {
|
|
|
15287
15380
|
}
|
|
15288
15381
|
return output;
|
|
15289
15382
|
}
|
|
15290
|
-
function
|
|
15383
|
+
function option2(options, key) {
|
|
15291
15384
|
return typeof options[key] === "string" ? options[key] : undefined;
|
|
15292
15385
|
}
|
|
15293
15386
|
function manifestPathOption(options) {
|
|
15294
|
-
return
|
|
15387
|
+
return option2(options, "config") ?? option2(options, "manifest");
|
|
15295
15388
|
}
|
|
15296
15389
|
function slug(value) {
|
|
15297
15390
|
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -15314,7 +15407,7 @@ async function git3(cwd, args) {
|
|
|
15314
15407
|
async function loadCliEnvironment(root) {
|
|
15315
15408
|
let contents;
|
|
15316
15409
|
try {
|
|
15317
|
-
contents = await readFile8(
|
|
15410
|
+
contents = await readFile8(path6.join(root, ".sandblocks", "config.env"), "utf8");
|
|
15318
15411
|
} catch {
|
|
15319
15412
|
return;
|
|
15320
15413
|
}
|
|
@@ -15338,7 +15431,7 @@ if (import.meta.main) {
|
|
|
15338
15431
|
}
|
|
15339
15432
|
export {
|
|
15340
15433
|
run2 as run,
|
|
15341
|
-
parse
|
|
15434
|
+
parse2 as parse
|
|
15342
15435
|
};
|
|
15343
15436
|
|
|
15344
|
-
//# debugId=
|
|
15437
|
+
//# debugId=EB337A76C8A48F0564756E2164756E21
|