@sandblocks/cli 0.5.0-b.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 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 path5 from "path";
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 chmod2, mkdir as mkdir2, readFile as readFile4, rename, rm as rm2 } from "fs/promises";
11884
- import path4 from "path";
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 = path4.resolve(options.positionals[0] ?? process.cwd());
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 ?? path4.basename(root).toLowerCase();
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 ${path4.relative(context.root, context.file)}`);
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 = path4.resolve(options.positionals[0] ?? process.cwd());
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 mkdir2(path4.dirname(lock), { recursive: true, mode: 448 });
12118
+ await mkdir3(path5.dirname(lock), { recursive: true, mode: 448 });
12030
12119
  try {
12031
- await mkdir2(lock);
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 rm2(lock, { recursive: true, force: true });
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 rm2(context.file, { force: true });
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 = path4.resolve(options.positionals[0] ?? process.cwd());
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, option, projectId) {
12426
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
12427
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY ?? "";
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 = path4.join(root, ".sandblocks", "config.env");
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 path4.join(root, ".sandblocks", `sandbox-${environment}.json`);
12681
+ return path5.join(root, ".sandblocks", `sandbox-${environment}.json`);
12593
12682
  }
12594
12683
  async function writeState(file, state) {
12595
- await mkdir2(path4.dirname(file), { recursive: true, mode: 448 });
12596
- await chmod2(path4.dirname(file), 448);
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 chmod2(temporary, 384);
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 mkdir4, readFile as readFile7, rm as rm5, writeFile as writeFile4 } from "fs/promises";
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 mkdir3, readFile as readFile5, rm as rm3 } from "fs/promises";
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 rm3(this.git.worktree, { recursive: true, force: true });
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 mkdir3(dirname(destination), { recursive: true });
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 mkdir3(dirname(worktree), { recursive: true });
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 mkdir3(dirname(file), { recursive: true });
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 path5 = resolve2(root, relative);
13319
- await mkdir22(resolve2(path5, ".."), { recursive: true });
13320
- if (!options.force && await access(path5).then(() => true).catch(() => false))
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(path5, content, { mode: relative.includes(".env") ? 384 : 420 });
13323
- written.push(path5);
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(path5) {
13394
- if (path5 === "~")
13395
- return process.env.HOME ?? path5;
13396
- if (path5.startsWith("~/"))
13397
- return `${process.env.HOME ?? "~"}/${path5.slice(2)}`;
13398
- return path5;
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(path5) {
13640
- if (path5 === "~")
13641
- return process.env.HOME ?? path5;
13642
- if (path5.startsWith("~/"))
13643
- return `${process.env.HOME ?? "~"}/${path5.slice(2)}`;
13644
- return path5;
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 rm4, stat as stat2, writeFile as writeFile3 } from "fs/promises";
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(path5, init = {}) {
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}${path5}`, { ...init, headers });
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((path5) => path5 && path5 !== ".sandblocks" && !path5.startsWith(".sandblocks/") && path5 !== ".git");
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 rm4(temporary, { recursive: true, force: true });
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 mkdir4(dirname2(stateFile), { recursive: true });
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 rm5(stateFile, { force: true });
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 path5 = resolve7(cwd, file ?? ".sandblocks/config.json");
14508
- if (!await Bun.file(path5).exists())
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(path5, "utf8"));
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 = parse(args);
14662
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
14663
- const manifestPath = path5.join(root, "sandblocks.yml");
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(path5.join(root, "package.json"));
14672
- const name = slug(option(options, "name") ?? path5.basename(root));
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 = parse(args);
14694
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
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 = parse(args);
14705
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
14706
- const projectId = option(options, "project");
14707
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14708
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = option(options, "repository-url") ?? await git3(root, ["config", "--get", "remote.origin.url"]);
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(path5.basename(root)),
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 = parse(args);
14741
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
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 = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
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(path5.join(root, ".git")).then(() => true).catch(() => false);
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 = parse(args);
14756
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14757
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = parse(rest);
14781
- const projectId = option(options, "project");
14782
- const stack = option(options, "stack");
14783
- const app = option(options, "app");
14784
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14785
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = option(options, "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(path5.resolve(file), "utf8"));
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 = parse(rest);
14824
- const projectId = option(options, "project");
14825
- const stack = option(options, "stack");
14826
- const app = option(options, "app");
14827
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14828
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = option(options, "environment");
14839
- const name = option(options, "name");
14840
- const value = option(options, "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 = option(options, "id");
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 = parse(rest);
14906
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
14907
- const projectId = option(options, "project");
14908
- const workspaceId = option(options, "workspace");
14909
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14910
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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": option(options, "pool") ?? "sandbox-development",
14928
- "idempotency-key": option(options, "idempotency-key") ?? `local-import:${workspaceId}`
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 = parse(args);
15041
+ const options = parse2(args);
14949
15042
  const action = options.positionals[0] ?? "list";
14950
- const projectId = option(options, "project");
14951
- const sandboxId = option(options, "sandbox");
14952
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
14953
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = option(options, "prompt-file");
14962
- const prompt = option(options, "prompt") ?? (promptFile ? await readFile8(path5.resolve(promptFile), "utf8") : undefined);
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 = option(options, "repository");
14966
- const provider = option(options, "provider") ?? "codex";
14967
- const model = option(options, "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": option(options, "idempotency-key") ?? randomUUID4() },
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: option(options, "credential-env"),
14979
- timeoutSeconds: option(options, "timeout-seconds") ? Number(option(options, "timeout-seconds")) : undefined
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 = option(options, "session");
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": option(options, "idempotency-key") ?? randomUUID4() } : undefined
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 path6 = `/v1/projects/${encodeURIComponent(projectId)}/sandboxes/${encodeURIComponent(sandboxId)}/agent-sessions/${encodeURIComponent(sessionId)}`;
15098
+ const path7 = `/v1/projects/${encodeURIComponent(projectId)}/sandboxes/${encodeURIComponent(sandboxId)}/agent-sessions/${encodeURIComponent(sessionId)}`;
15006
15099
  for (;; ) {
15007
- const result = await sandblocksRequest(apiUrl, apiKey, path6);
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 = parse(args);
15015
- const projectId = option(options, "project");
15016
- const sandboxId = option(options, "sandbox");
15017
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
15018
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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 = path5.join(root, name);
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 = parse(args);
15154
- const root = path5.resolve(options.positionals[0] ?? process.cwd());
15155
- const projectId = option(options, "project");
15156
- const workspaceId = option(options, "workspace");
15157
- const sandboxId = option(options, "sandbox");
15158
- const hostId = option(options, "host");
15159
- const targetHost = option(options, "target-host");
15160
- const specPath = option(options, "spec");
15161
- const apiUrl = (option(options, "api-url") ?? process.env.SANDBLOCKS_API_URL ?? "").replace(/\/$/, "");
15162
- const apiKey = option(options, "api-key") ?? process.env.SANDBLOCKS_API_KEY;
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(path5.resolve(root, specPath), "utf8"));
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 = option(options, "deployment") ?? randomUUID4();
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 parse(args) {
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 option(options, key) {
15383
+ function option2(options, key) {
15291
15384
  return typeof options[key] === "string" ? options[key] : undefined;
15292
15385
  }
15293
15386
  function manifestPathOption(options) {
15294
- return option(options, "config") ?? option(options, "manifest");
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(path5.join(root, ".sandblocks", "config.env"), "utf8");
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=64EF599DFAEB049264756E2164756E21
15437
+ //# debugId=EB337A76C8A48F0564756E2164756E21