openship 0.1.10 → 0.1.11

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command24 } from "commander";
4
+ import { Command as Command25 } from "commander";
5
5
 
6
6
  // src/lib/output.ts
7
7
  import chalk from "chalk";
@@ -2367,9 +2367,9 @@ import chalk5 from "chalk";
2367
2367
  import ora from "ora";
2368
2368
  import { spawn } from "child_process";
2369
2369
  import { randomBytes } from "crypto";
2370
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
2371
- import { homedir as homedir3 } from "os";
2372
- import { dirname as dirname2, join as join4 } from "path";
2370
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync4 } from "fs";
2371
+ import { homedir as homedir4 } from "os";
2372
+ import { dirname as dirname2, join as join5 } from "path";
2373
2373
  import { fileURLToPath } from "url";
2374
2374
 
2375
2375
  // src/lib/dashboard.ts
@@ -2419,8 +2419,8 @@ async function downloadToFile(url, dest, onProgress) {
2419
2419
  } finally {
2420
2420
  file.end();
2421
2421
  }
2422
- await new Promise((resolve, reject2) => {
2423
- file.on("finish", () => resolve());
2422
+ await new Promise((resolve2, reject2) => {
2423
+ file.on("finish", () => resolve2());
2424
2424
  file.on("error", reject2);
2425
2425
  });
2426
2426
  return { sha256: hash.digest("hex"), size: received };
@@ -2510,29 +2510,233 @@ async function ensureDashboard(opts = {}) {
2510
2510
  return { tag, entry, cwd };
2511
2511
  }
2512
2512
 
2513
+ // src/lib/service.ts
2514
+ import { spawnSync as spawnSync2 } from "child_process";
2515
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "fs";
2516
+ import { homedir as homedir3 } from "os";
2517
+ import { join as join4, resolve } from "path";
2518
+ var HOME = homedir3();
2519
+ var OS_DIR = join4(HOME, ".openship");
2520
+ var LOG_DIR = join4(OS_DIR, "logs");
2521
+ var MAC_LABEL = "io.openship.up";
2522
+ var MAC_PLIST = join4(HOME, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2523
+ var SYSTEMD_NAME = "openship";
2524
+ var WIN_TASK = "Openship";
2525
+ function selfInvocation() {
2526
+ const runtime = process.execPath;
2527
+ const entry = resolve(process.argv[1] ?? "");
2528
+ return { runtime, args: [entry] };
2529
+ }
2530
+ function upArgs(flags) {
2531
+ const a = ["up", "--foreground"];
2532
+ if (flags.port) a.push("--port", flags.port);
2533
+ if (flags.dataDir) a.push("--data-dir", flags.dataDir);
2534
+ if (flags.dashboardPort) a.push("--dashboard-port", flags.dashboardPort);
2535
+ if (flags.ui === false) a.push("--no-ui");
2536
+ if (flags.uiVersion) a.push("--ui-version", flags.uiVersion);
2537
+ return a;
2538
+ }
2539
+ function runArgv(flags) {
2540
+ const { runtime, args } = selfInvocation();
2541
+ return [runtime, ...args, ...upArgs(flags)];
2542
+ }
2543
+ function run(cmd, args) {
2544
+ const r = spawnSync2(cmd, args, { encoding: "utf8" });
2545
+ return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}`.trim() };
2546
+ }
2547
+ function isRoot() {
2548
+ return typeof process.getuid === "function" && process.getuid() === 0;
2549
+ }
2550
+ function detectKind() {
2551
+ if (process.platform === "darwin") return "launchd";
2552
+ if (process.platform === "linux") {
2553
+ if (!hasSystemd()) return "unsupported";
2554
+ return isRoot() ? "systemd-system" : "systemd-user";
2555
+ }
2556
+ if (process.platform === "win32") return "schtasks";
2557
+ return "unsupported";
2558
+ }
2559
+ function hasSystemd() {
2560
+ return spawnSync2("sh", ["-c", "command -v systemctl"]).status === 0;
2561
+ }
2562
+ function xmlEscape(s) {
2563
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2564
+ }
2565
+ function plist(flags) {
2566
+ const argv = runArgv(flags);
2567
+ const items = argv.map((a) => ` <string>${xmlEscape(a)}</string>`).join("\n");
2568
+ const path2 = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", join4(HOME, ".bun/bin")].join(":");
2569
+ return `<?xml version="1.0" encoding="UTF-8"?>
2570
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2571
+ <plist version="1.0">
2572
+ <dict>
2573
+ <key>Label</key><string>${MAC_LABEL}</string>
2574
+ <key>ProgramArguments</key>
2575
+ <array>
2576
+ ${items}
2577
+ </array>
2578
+ <key>RunAtLoad</key><true/>
2579
+ <key>KeepAlive</key><true/>
2580
+ <key>StandardOutPath</key><string>${join4(LOG_DIR, "up.log")}</string>
2581
+ <key>StandardErrorPath</key><string>${join4(LOG_DIR, "up.err.log")}</string>
2582
+ <key>EnvironmentVariables</key>
2583
+ <dict><key>PATH</key><string>${path2}</string></dict>
2584
+ </dict>
2585
+ </plist>
2586
+ `;
2587
+ }
2588
+ function systemdUnit(flags) {
2589
+ const argv = runArgv(flags);
2590
+ const execStart = argv.map((a) => /\s/.test(a) ? `"${a}"` : a).join(" ");
2591
+ return `[Unit]
2592
+ Description=Openship control plane
2593
+ After=network-online.target
2594
+ Wants=network-online.target
2595
+
2596
+ [Service]
2597
+ Type=simple
2598
+ ExecStart=${execStart}
2599
+ Restart=always
2600
+ RestartSec=2
2601
+ Environment=NODE_ENV=production
2602
+
2603
+ [Install]
2604
+ WantedBy=default.target
2605
+ `;
2606
+ }
2607
+ function preview(flags) {
2608
+ const kind = detectKind();
2609
+ if (kind === "launchd") return { kind, path: MAC_PLIST, content: plist(flags) };
2610
+ if (kind === "systemd-user") return { kind, path: join4(HOME, ".config/systemd/user", `${SYSTEMD_NAME}.service`), content: systemdUnit(flags) };
2611
+ if (kind === "systemd-system") return { kind, path: `/etc/systemd/system/${SYSTEMD_NAME}.service`, content: systemdUnit(flags) };
2612
+ if (kind === "schtasks") return { kind, path: WIN_TASK, content: runArgv(flags).join(" ") };
2613
+ return { kind, path: "", content: "" };
2614
+ }
2615
+ function installAndStart(flags) {
2616
+ mkdirSync4(LOG_DIR, { recursive: true });
2617
+ const kind = detectKind();
2618
+ if (kind === "launchd") {
2619
+ mkdirSync4(join4(HOME, "Library", "LaunchAgents"), { recursive: true });
2620
+ run("launchctl", ["bootout", `gui/${process.getuid?.() ?? ""}/${MAC_LABEL}`]);
2621
+ writeFileSync3(MAC_PLIST, plist(flags));
2622
+ const uid = String(process.getuid?.() ?? "");
2623
+ let r = run("launchctl", ["bootstrap", `gui/${uid}`, MAC_PLIST]);
2624
+ if (!r.ok) r = run("launchctl", ["load", "-w", MAC_PLIST]);
2625
+ if (!r.ok) throw new Error(`launchctl failed to load the agent: ${r.out}`);
2626
+ return { kind, detail: `launchd agent ${MAC_LABEL} (logs: ${LOG_DIR})` };
2627
+ }
2628
+ if (kind === "systemd-user" || kind === "systemd-system") {
2629
+ const sysArgs = kind === "systemd-user" ? ["--user"] : [];
2630
+ const unitPath = kind === "systemd-user" ? join4(HOME, ".config/systemd/user", `${SYSTEMD_NAME}.service`) : `/etc/systemd/system/${SYSTEMD_NAME}.service`;
2631
+ mkdirSync4(unitPath.slice(0, unitPath.lastIndexOf("/")), { recursive: true });
2632
+ writeFileSync3(unitPath, systemdUnit(flags));
2633
+ run("systemctl", [...sysArgs, "daemon-reload"]);
2634
+ const r = run("systemctl", [...sysArgs, "enable", "--now", SYSTEMD_NAME]);
2635
+ if (!r.ok) throw new Error(`systemctl enable failed: ${r.out}`);
2636
+ if (kind === "systemd-user") {
2637
+ run("loginctl", ["enable-linger", process.env.USER ?? ""]);
2638
+ }
2639
+ return { kind, detail: `systemd unit ${SYSTEMD_NAME} (${kind === "systemd-user" ? "--user" : "system"})` };
2640
+ }
2641
+ if (kind === "schtasks") {
2642
+ const tr = runArgv(flags).map((a) => `\\"${a}\\"`).join(" ");
2643
+ const r = run("schtasks", ["/Create", "/TN", WIN_TASK, "/SC", "ONLOGON", "/RL", "HIGHEST", "/TR", tr, "/F"]);
2644
+ if (!r.ok) throw new Error(`schtasks create failed: ${r.out}`);
2645
+ run("schtasks", ["/Run", "/TN", WIN_TASK]);
2646
+ return { kind, detail: `Scheduled Task ${WIN_TASK} (best-effort; runs at logon)` };
2647
+ }
2648
+ throw new Error(
2649
+ "No supported service manager found (need systemd on Linux). Run `openship up --foreground` instead, or use docker compose for always-on."
2650
+ );
2651
+ }
2652
+ function stop() {
2653
+ const kind = detectKind();
2654
+ if (kind === "launchd") {
2655
+ run("launchctl", ["bootout", `gui/${process.getuid?.() ?? ""}/${MAC_LABEL}`]);
2656
+ if (existsSync3(MAC_PLIST)) rmSync2(MAC_PLIST, { force: true });
2657
+ return { kind, detail: `launchd agent ${MAC_LABEL} stopped + removed` };
2658
+ }
2659
+ if (kind === "systemd-user" || kind === "systemd-system") {
2660
+ const sysArgs = kind === "systemd-user" ? ["--user"] : [];
2661
+ run("systemctl", [...sysArgs, "disable", "--now", SYSTEMD_NAME]);
2662
+ const unitPath = kind === "systemd-user" ? join4(HOME, ".config/systemd/user", `${SYSTEMD_NAME}.service`) : `/etc/systemd/system/${SYSTEMD_NAME}.service`;
2663
+ if (existsSync3(unitPath)) rmSync2(unitPath, { force: true });
2664
+ run("systemctl", [...sysArgs, "daemon-reload"]);
2665
+ return { kind, detail: `systemd unit ${SYSTEMD_NAME} stopped + disabled` };
2666
+ }
2667
+ if (kind === "schtasks") {
2668
+ run("schtasks", ["/End", "/TN", WIN_TASK]);
2669
+ run("schtasks", ["/Delete", "/TN", WIN_TASK, "/F"]);
2670
+ return { kind, detail: `Scheduled Task ${WIN_TASK} stopped + removed` };
2671
+ }
2672
+ return { kind, detail: "no supported service manager \u2014 nothing to stop" };
2673
+ }
2674
+
2513
2675
  // src/commands/up.ts
2514
2676
  var DIST_DIR = dirname2(fileURLToPath(import.meta.url));
2515
- var SERVER_DIR = join4(DIST_DIR, "server");
2516
- var OS_DIR = join4(homedir3(), ".openship");
2677
+ var SERVER_DIR = join5(DIST_DIR, "server");
2678
+ var OS_DIR2 = join5(homedir4(), ".openship");
2517
2679
  function ensureAuthSecret() {
2518
- const path2 = join4(OS_DIR, "auth-secret");
2519
- if (existsSync3(path2)) return readFileSync2(path2, "utf8").trim();
2520
- mkdirSync4(OS_DIR, { recursive: true, mode: 448 });
2680
+ const path2 = join5(OS_DIR2, "auth-secret");
2681
+ if (existsSync4(path2)) return readFileSync2(path2, "utf8").trim();
2682
+ mkdirSync5(OS_DIR2, { recursive: true, mode: 448 });
2521
2683
  const secret = randomBytes(32).toString("hex");
2522
- writeFileSync3(path2, secret, { mode: 384 });
2684
+ writeFileSync4(path2, secret, { mode: 384 });
2523
2685
  return secret;
2524
2686
  }
2525
- var upCommand = new Command4("up").description("Run the Openship control plane locally (bundled API + embedded database)").option("--port <port>", "API port to listen on", "4000").option("--data-dir <dir>", "Directory for the embedded database").option("--dashboard-port <port>", "Dashboard port", "3001").option("--no-ui", "Run the API only \u2014 don't download/serve the dashboard").option("--ui-version <tag>", "Dashboard release tag to run (default: this CLI's version)").action(async (opts) => {
2526
- const serverEntry = join4(SERVER_DIR, "index.js");
2527
- if (!existsSync3(serverEntry)) {
2687
+ var upCommand = new Command4("up").description("Start Openship as a persistent service (boot + auto-restart); --foreground to run attached").option("--port <port>", "API port to listen on", "4000").option("--data-dir <dir>", "Directory for the embedded database").option("--dashboard-port <port>", "Dashboard port", "3001").option("--no-ui", "Run the API only \u2014 don't download/serve the dashboard").option("--ui-version <tag>", "Dashboard release tag to run (default: this CLI's version)").option("-f, --foreground", "Run attached in this terminal instead of as a background service").option("--dry-run", "Print the service definition that would be installed, then exit").action(async (opts) => {
2688
+ if (opts.foreground) return runForeground(opts);
2689
+ startService(opts);
2690
+ });
2691
+ function startService(opts) {
2692
+ const flags = {
2693
+ port: opts.port,
2694
+ dataDir: opts.dataDir,
2695
+ dashboardPort: opts.dashboardPort,
2696
+ ui: opts.ui,
2697
+ uiVersion: opts.uiVersion
2698
+ };
2699
+ if (opts.dryRun) {
2700
+ const p = preview(flags);
2701
+ console.log(
2702
+ chalk5.dim(`
2703
+ service manager: ${p.kind}
2704
+ path: ${p.path}
2705
+
2706
+ `) + p.content + "\n"
2707
+ );
2708
+ return;
2709
+ }
2710
+ try {
2711
+ const res = installAndStart(flags);
2712
+ const port = String(opts.port || "4000");
2713
+ const dashPort = String(opts.dashboardPort || "3001");
2714
+ console.log(
2715
+ chalk5.green("\n \u2714 Openship is running as a service.\n") + chalk5.dim(` API: http://localhost:${port}/api
2716
+ `) + (opts.ui !== false ? chalk5.dim(` Dashboard: http://localhost:${dashPort}
2717
+ `) : "") + chalk5.dim(` ${res.detail}
2718
+ `) + chalk5.dim(" Starts on boot and auto-restarts. Stop with `openship stop`.\n")
2719
+ );
2720
+ } catch (e) {
2721
+ console.error(
2722
+ chalk5.red(`
2723
+ Couldn't install the service: ${e.message}
2724
+ `) + chalk5.dim(" Run `openship up --foreground` to run it attached instead.\n")
2725
+ );
2726
+ process.exit(1);
2727
+ }
2728
+ }
2729
+ async function runForeground(opts) {
2730
+ const serverEntry = join5(SERVER_DIR, "index.js");
2731
+ if (!existsSync4(serverEntry)) {
2528
2732
  console.error(
2529
2733
  chalk5.red("\n Bundled server not found in this install.") + chalk5.dim("\n Reinstall with `openship update` (or `npm i -g openship`).\n")
2530
2734
  );
2531
2735
  process.exit(1);
2532
2736
  }
2533
2737
  const port = String(opts.port || "4000");
2534
- const dataDir = opts.dataDir || join4(OS_DIR, "data");
2535
- mkdirSync4(dataDir, { recursive: true });
2738
+ const dataDir = opts.dataDir || join5(OS_DIR2, "data");
2739
+ mkdirSync5(dataDir, { recursive: true });
2536
2740
  const env = {
2537
2741
  ...process.env,
2538
2742
  PORT: port,
@@ -2544,8 +2748,8 @@ var upCommand = new Command4("up").description("Run the Openship control plane l
2544
2748
  OPENSHIP_JOB_RUNNER: "in-process",
2545
2749
  OPENSHIP_ALLOW_ZERO_AUTH: "true",
2546
2750
  PGLITE_DATA_DIR: dataDir,
2547
- OPENSHIP_MIGRATIONS_DIR: join4(SERVER_DIR, "migrations"),
2548
- OPENSHIP_PGLITE_ASSETS_DIR: join4(SERVER_DIR, "pglite"),
2751
+ OPENSHIP_MIGRATIONS_DIR: join5(SERVER_DIR, "migrations"),
2752
+ OPENSHIP_PGLITE_ASSETS_DIR: join5(SERVER_DIR, "pglite"),
2549
2753
  BETTER_AUTH_SECRET: ensureAuthSecret()
2550
2754
  };
2551
2755
  delete env.DATABASE_URL;
@@ -2606,7 +2810,7 @@ var upCommand = new Command4("up").description("Run the Openship control plane l
2606
2810
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
2607
2811
  try {
2608
2812
  const bundle = await ensureDashboard({
2609
- tag: opts.uiVersion || `v${"0.1.10"}`,
2813
+ tag: opts.uiVersion || `v${"0.1.11"}`,
2610
2814
  onProgress: (received, total) => {
2611
2815
  if (total) {
2612
2816
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -2685,19 +2889,35 @@ var upCommand = new Command4("up").description("Run the Openship control plane l
2685
2889
  stopAll();
2686
2890
  process.exit(code ?? 0);
2687
2891
  });
2892
+ }
2893
+
2894
+ // src/commands/stop.ts
2895
+ import { Command as Command5 } from "commander";
2896
+ import chalk6 from "chalk";
2897
+ var stopCommand = new Command5("stop").description("Stop the Openship service (started by `openship up`) \u2014 it won't restart or return on reboot").action(() => {
2898
+ try {
2899
+ const res = stop();
2900
+ console.log(chalk6.green("\n \u2714 Openship stopped.\n") + chalk6.dim(` ${res.detail}
2901
+ `));
2902
+ } catch (e) {
2903
+ console.error(chalk6.red(`
2904
+ Couldn't stop the service: ${e.message}
2905
+ `));
2906
+ process.exit(1);
2907
+ }
2688
2908
  });
2689
2909
 
2690
2910
  // src/commands/init.ts
2691
- import { Command as Command5 } from "commander";
2692
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
2911
+ import { Command as Command6 } from "commander";
2912
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
2693
2913
  import { createInterface as createInterface2 } from "readline/promises";
2694
2914
  import { stdin as input2, stdout as output2 } from "process";
2695
- import { join as join5 } from "path";
2696
- var initCommand = new Command5("init").description("Link the current directory to an Openship project (.openship/project.json)").option("--project <id>", "Project id to link (skips the picker)").option("--environment <name>", "Default deploy environment", "production").option("--dir <path>", "Directory to initialize", process.cwd()).option("--force", "Overwrite an existing project link").option("-y, --yes", "Non-interactive: fail instead of prompting").action(async (opts) => {
2915
+ import { join as join6 } from "path";
2916
+ var initCommand = new Command6("init").description("Link the current directory to an Openship project (.openship/project.json)").option("--project <id>", "Project id to link (skips the picker)").option("--environment <name>", "Default deploy environment", "production").option("--dir <path>", "Directory to initialize", process.cwd()).option("--force", "Overwrite an existing project link").option("-y, --yes", "Non-interactive: fail instead of prompting").action(async (opts) => {
2697
2917
  const root = opts.dir || process.cwd();
2698
- const linkDir = join5(root, ".openship");
2699
- const linkPath = join5(linkDir, "project.json");
2700
- if (existsSync4(linkPath) && !opts.force) {
2918
+ const linkDir = join6(root, ".openship");
2919
+ const linkPath = join6(linkDir, "project.json");
2920
+ if (existsSync5(linkPath) && !opts.force) {
2701
2921
  err(`Already linked (${linkPath}). Re-run with --force to overwrite.`);
2702
2922
  process.exit(1);
2703
2923
  }
@@ -2750,8 +2970,8 @@ var initCommand = new Command5("init").description("Link the current directory t
2750
2970
  context: getActiveContext(),
2751
2971
  defaults: { environment: opts.environment || "production" }
2752
2972
  };
2753
- mkdirSync5(linkDir, { recursive: true });
2754
- writeFileSync4(linkPath, JSON.stringify(link, null, 2) + "\n");
2973
+ mkdirSync6(linkDir, { recursive: true });
2974
+ writeFileSync5(linkPath, JSON.stringify(link, null, 2) + "\n");
2755
2975
  if (isJsonMode()) {
2756
2976
  printJson({ path: linkPath, link });
2757
2977
  return;
@@ -2762,7 +2982,7 @@ var initCommand = new Command5("init").description("Link the current directory t
2762
2982
  });
2763
2983
 
2764
2984
  // src/commands/context.ts
2765
- import { Command as Command6 } from "commander";
2985
+ import { Command as Command7 } from "commander";
2766
2986
  function renderContexts() {
2767
2987
  const rows = listContexts().map((c) => ({
2768
2988
  current: c.current ? "*" : "",
@@ -2773,8 +2993,8 @@ function renderContexts() {
2773
2993
  }));
2774
2994
  printTable(rows, ["current", "name", "apiUrl", "dashboardUrl", "auth"]);
2775
2995
  }
2776
- var listCmd = new Command6("list").alias("ls").description("List configured contexts").action(renderContexts);
2777
- var useCmd = new Command6("use").description("Switch the active context").argument("<name>", "Context name").action((name) => {
2996
+ var listCmd = new Command7("list").alias("ls").description("List configured contexts").action(renderContexts);
2997
+ var useCmd = new Command7("use").description("Switch the active context").argument("<name>", "Context name").action((name) => {
2778
2998
  try {
2779
2999
  setActiveContext(name);
2780
3000
  ok(`
@@ -2785,7 +3005,7 @@ var useCmd = new Command6("use").description("Switch the active context").argume
2785
3005
  process.exit(1);
2786
3006
  }
2787
3007
  });
2788
- var addCmd = new Command6("add").description("Create or update a context's endpoints/token").argument("<name>", "Context name").option("--api-url <url>", "API base URL").option("--dashboard-url <url>", "Dashboard base URL").option("--token <token>", "Personal Access Token to store").option("--use", "Switch to this context after adding").action((name, opts) => {
3008
+ var addCmd = new Command7("add").description("Create or update a context's endpoints/token").argument("<name>", "Context name").option("--api-url <url>", "API base URL").option("--dashboard-url <url>", "Dashboard base URL").option("--token <token>", "Personal Access Token to store").option("--use", "Switch to this context after adding").action((name, opts) => {
2789
3009
  addContext(name, {
2790
3010
  apiUrl: opts.apiUrl,
2791
3011
  dashboardUrl: opts.dashboardUrl,
@@ -2796,7 +3016,7 @@ var addCmd = new Command6("add").description("Create or update a context's endpo
2796
3016
  Saved context "${name}"${opts.use ? " (now active)" : ""}.
2797
3017
  `);
2798
3018
  });
2799
- var rmCmd = new Command6("rm").alias("remove").description("Remove a context (cannot remove the active one)").argument("<name>", "Context name").action((name) => {
3019
+ var rmCmd = new Command7("rm").alias("remove").description("Remove a context (cannot remove the active one)").argument("<name>", "Context name").action((name) => {
2800
3020
  try {
2801
3021
  removeContext(name);
2802
3022
  ok(`
@@ -2807,15 +3027,15 @@ var rmCmd = new Command6("rm").alias("remove").description("Remove a context (ca
2807
3027
  process.exit(1);
2808
3028
  }
2809
3029
  });
2810
- var contextCommand = new Command6("context").alias("ctx").description("Manage connection contexts (list/use/add/rm)").action(() => {
3030
+ var contextCommand = new Command7("context").alias("ctx").description("Manage connection contexts (list/use/add/rm)").action(() => {
2811
3031
  ok(` Active context: ${getActiveContext()}`);
2812
3032
  renderContexts();
2813
3033
  }).addCommand(listCmd).addCommand(useCmd).addCommand(addCmd).addCommand(rmCmd);
2814
3034
 
2815
3035
  // src/commands/status.ts
2816
- import { Command as Command7 } from "commander";
2817
- import chalk6 from "chalk";
2818
- var statusCommand = new Command7("status").description("Show the active context's API health and deployment info").action(async () => {
3036
+ import { Command as Command8 } from "commander";
3037
+ import chalk7 from "chalk";
3038
+ var statusCommand = new Command8("status").description("Show the active context's API health and deployment info").action(async () => {
2819
3039
  const context = getActiveContext();
2820
3040
  const apiUrl = getApiUrl2();
2821
3041
  let health;
@@ -2838,18 +3058,18 @@ var statusCommand = new Command7("status").description("Show the active context'
2838
3058
  printJson({ context, apiUrl, reachable: true, health, env: envInfo });
2839
3059
  return;
2840
3060
  }
2841
- const row = (label, value) => ` ${chalk6.dim(label.padEnd(14))}${value ?? chalk6.dim("-")}
3061
+ const row = (label, value) => ` ${chalk7.dim(label.padEnd(14))}${value ?? chalk7.dim("-")}
2842
3062
  `;
2843
3063
  process.stdout.write(
2844
- chalk6.bold("\n Openship status\n\n") + row("Context", context) + row("API", apiUrl) + row("Health", chalk6.green(health.status ?? "ok")) + row("Mode", envInfo.selfHosted ? "self-hosted" : "cloud") + row("Deploy", envInfo.deployMode) + row("Auth", envInfo.authMode) + row("Team", envInfo.teamMode) + (envInfo.hostDomain ? row("Host domain", envInfo.hostDomain) : "") + (envInfo.machineName ? row("Machine", envInfo.machineName) : "") + "\n"
3064
+ chalk7.bold("\n Openship status\n\n") + row("Context", context) + row("API", apiUrl) + row("Health", chalk7.green(health.status ?? "ok")) + row("Mode", envInfo.selfHosted ? "self-hosted" : "cloud") + row("Deploy", envInfo.deployMode) + row("Auth", envInfo.authMode) + row("Team", envInfo.teamMode) + (envInfo.hostDomain ? row("Host domain", envInfo.hostDomain) : "") + (envInfo.machineName ? row("Machine", envInfo.machineName) : "") + "\n"
2845
3065
  );
2846
3066
  });
2847
3067
 
2848
3068
  // src/commands/doctor.ts
2849
- import { Command as Command8 } from "commander";
2850
- import { existsSync as existsSync5 } from "fs";
3069
+ import { Command as Command9 } from "commander";
3070
+ import { existsSync as existsSync6 } from "fs";
2851
3071
  import { execFileSync } from "child_process";
2852
- import chalk7 from "chalk";
3072
+ import chalk8 from "chalk";
2853
3073
  function bunVersion() {
2854
3074
  const embedded = process.versions.bun;
2855
3075
  if (embedded) return embedded;
@@ -2859,9 +3079,9 @@ function bunVersion() {
2859
3079
  return null;
2860
3080
  }
2861
3081
  }
2862
- var doctorCommand = new Command8("doctor").description("Diagnose the CLI setup (config, active context, runtime)").action(async () => {
3082
+ var doctorCommand = new Command9("doctor").description("Diagnose the CLI setup (config, active context, runtime)").action(async () => {
2863
3083
  const checks = [];
2864
- const hasConfig = existsSync5(CONFIG_PATH);
3084
+ const hasConfig = existsSync6(CONFIG_PATH);
2865
3085
  checks.push({
2866
3086
  name: "config",
2867
3087
  status: hasConfig ? "pass" : "warn",
@@ -2897,10 +3117,10 @@ var doctorCommand = new Command8("doctor").description("Diagnose the CLI setup (
2897
3117
  if (isJsonMode()) {
2898
3118
  printJson({ context, apiUrl, reachable, checks });
2899
3119
  } else {
2900
- const glyph = { pass: chalk7.green("\u2713"), warn: chalk7.yellow("!"), fail: chalk7.red("\u2717") };
2901
- process.stdout.write(chalk7.bold("\n Openship doctor\n\n"));
3120
+ const glyph = { pass: chalk8.green("\u2713"), warn: chalk8.yellow("!"), fail: chalk8.red("\u2717") };
3121
+ process.stdout.write(chalk8.bold("\n Openship doctor\n\n"));
2902
3122
  for (const c of checks) {
2903
- process.stdout.write(` ${glyph[c.status]} ${chalk7.bold(c.name.padEnd(8))} ${c.detail}
3123
+ process.stdout.write(` ${glyph[c.status]} ${chalk8.bold(c.name.padEnd(8))} ${c.detail}
2904
3124
  `);
2905
3125
  }
2906
3126
  process.stdout.write("\n");
@@ -2909,20 +3129,20 @@ var doctorCommand = new Command8("doctor").description("Diagnose the CLI setup (
2909
3129
  });
2910
3130
 
2911
3131
  // src/commands/deploy.ts
2912
- import { Command as Command9 } from "commander";
3132
+ import { Command as Command10 } from "commander";
2913
3133
  import { execFileSync as execFileSync3 } from "child_process";
2914
3134
  import ora2 from "ora";
2915
3135
 
2916
3136
  // src/lib/project-link.ts
2917
- import { readFileSync as readFileSync3, existsSync as existsSync6 } from "fs";
2918
- import { join as join6, dirname as dirname3, parse } from "path";
2919
- var LINK_REL = join6(".openship", "project.json");
3137
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
3138
+ import { join as join7, dirname as dirname3, parse } from "path";
3139
+ var LINK_REL = join7(".openship", "project.json");
2920
3140
  function findProjectLinkPath(from = process.cwd()) {
2921
3141
  let dir = from;
2922
3142
  const root = parse(dir).root;
2923
3143
  for (; ; ) {
2924
- const candidate = join6(dir, LINK_REL);
2925
- if (existsSync6(candidate)) return candidate;
3144
+ const candidate = join7(dir, LINK_REL);
3145
+ if (existsSync7(candidate)) return candidate;
2926
3146
  if (dir === root) return null;
2927
3147
  dir = dirname3(dir);
2928
3148
  }
@@ -2939,21 +3159,21 @@ function readProjectLink(from) {
2939
3159
 
2940
3160
  // src/lib/folder-deploy.ts
2941
3161
  import { execFileSync as execFileSync2 } from "child_process";
2942
- import { readFileSync as readFileSync4, existsSync as existsSync7, rmSync as rmSync2 } from "fs";
3162
+ import { readFileSync as readFileSync4, existsSync as existsSync8, rmSync as rmSync3 } from "fs";
2943
3163
  import { tmpdir } from "os";
2944
- import { join as join7, basename } from "path";
3164
+ import { join as join8, basename } from "path";
2945
3165
  function detectPackageManager(dir) {
2946
- if (existsSync7(join7(dir, "bun.lockb")) || existsSync7(join7(dir, "bun.lock"))) return "bun";
2947
- if (existsSync7(join7(dir, "pnpm-lock.yaml"))) return "pnpm";
2948
- if (existsSync7(join7(dir, "yarn.lock"))) return "yarn";
2949
- if (existsSync7(join7(dir, "package.json"))) return "npm";
3166
+ if (existsSync8(join8(dir, "bun.lockb")) || existsSync8(join8(dir, "bun.lock"))) return "bun";
3167
+ if (existsSync8(join8(dir, "pnpm-lock.yaml"))) return "pnpm";
3168
+ if (existsSync8(join8(dir, "yarn.lock"))) return "yarn";
3169
+ if (existsSync8(join8(dir, "package.json"))) return "npm";
2950
3170
  return void 0;
2951
3171
  }
2952
3172
  function detectStack(dir) {
2953
- if (existsSync7(join7(dir, "go.mod"))) return "go";
2954
- if (existsSync7(join7(dir, "Cargo.toml"))) return "rust";
2955
- if (existsSync7(join7(dir, "requirements.txt")) || existsSync7(join7(dir, "pyproject.toml"))) return "python";
2956
- if (existsSync7(join7(dir, "package.json"))) return "node";
3173
+ if (existsSync8(join8(dir, "go.mod"))) return "go";
3174
+ if (existsSync8(join8(dir, "Cargo.toml"))) return "rust";
3175
+ if (existsSync8(join8(dir, "requirements.txt")) || existsSync8(join8(dir, "pyproject.toml"))) return "python";
3176
+ if (existsSync8(join8(dir, "package.json"))) return "node";
2957
3177
  return void 0;
2958
3178
  }
2959
3179
  async function deployFolder(opts) {
@@ -2970,7 +3190,7 @@ async function deployFolder(opts) {
2970
3190
  throw new Error(session.error || "Failed to open upload session");
2971
3191
  }
2972
3192
  step("Packaging folder");
2973
- const tarball = join7(tmpdir(), `openship-upload-${session.sessionId}.tar.gz`);
3193
+ const tarball = join8(tmpdir(), `openship-upload-${session.sessionId}.tar.gz`);
2974
3194
  execFileSync2(
2975
3195
  "tar",
2976
3196
  [
@@ -2996,7 +3216,7 @@ async function deployFolder(opts) {
2996
3216
  if (!res.ok) throw new Error(`upload failed (HTTP ${res.status})`);
2997
3217
  } finally {
2998
3218
  try {
2999
- rmSync2(tarball, { force: true });
3219
+ rmSync3(tarball, { force: true });
3000
3220
  } catch {
3001
3221
  }
3002
3222
  }
@@ -3170,7 +3390,7 @@ function git(args) {
3170
3390
  return void 0;
3171
3391
  }
3172
3392
  }
3173
- var deployCommand = new Command9("deploy").description("Trigger a deployment for the current project").option("--project <id>", "Project ID (defaults to the linked project in .openship/project.json)").option("--branch <name>", "Git branch to deploy (defaults to the current branch)").option("--commit <sha>", "Specific commit SHA (defaults to the latest commit on the branch)").option("--env <environment>", "Target environment: production | preview", "production").option("--force-all", "Rebuild every enabled service (skip smart per-service routing)").option("--service-ids <ids>", "Comma-separated service IDs to deploy (smart routing)").option("--smart-route", "Rebuild only services changed since the active deploy").option("--refresh", "Re-apply current env to the active deploy (no git pull, no rebuild)").option("--name <name>", "Project name for a folder (non-git) deploy (defaults to the directory name)").option("--watch", "Stream the deployment logs until it finishes").action(async (opts) => {
3393
+ var deployCommand = new Command10("deploy").description("Trigger a deployment for the current project").option("--project <id>", "Project ID (defaults to the linked project in .openship/project.json)").option("--branch <name>", "Git branch to deploy (defaults to the current branch)").option("--commit <sha>", "Specific commit SHA (defaults to the latest commit on the branch)").option("--env <environment>", "Target environment: production | preview", "production").option("--force-all", "Rebuild every enabled service (skip smart per-service routing)").option("--service-ids <ids>", "Comma-separated service IDs to deploy (smart routing)").option("--smart-route", "Rebuild only services changed since the active deploy").option("--refresh", "Re-apply current env to the active deploy (no git pull, no rebuild)").option("--name <name>", "Project name for a folder (non-git) deploy (defaults to the directory name)").option("--watch", "Stream the deployment logs until it finishes").action(async (opts) => {
3174
3394
  const link = readProjectLink();
3175
3395
  const env = opts.env;
3176
3396
  if (env !== "production" && env !== "preview") {
@@ -3252,9 +3472,9 @@ var deployCommand = new Command9("deploy").description("Trigger a deployment for
3252
3472
  });
3253
3473
 
3254
3474
  // src/commands/deployment.ts
3255
- import { Command as Command10 } from "commander";
3475
+ import { Command as Command11 } from "commander";
3256
3476
  import { createInterface as createInterface3 } from "readline";
3257
- function run(fn) {
3477
+ function run2(fn) {
3258
3478
  return async (...args) => {
3259
3479
  try {
3260
3480
  await fn(...args);
@@ -3274,12 +3494,12 @@ function shortSha(v) {
3274
3494
  async function confirm(question) {
3275
3495
  if (!process.stdin.isTTY) return true;
3276
3496
  const rl = createInterface3({ input: process.stdin, output: process.stderr });
3277
- const answer = await new Promise((resolve) => rl.question(`${question} [y/N] `, resolve));
3497
+ const answer = await new Promise((resolve2) => rl.question(`${question} [y/N] `, resolve2));
3278
3498
  rl.close();
3279
3499
  return /^y(es)?$/i.test(answer.trim());
3280
3500
  }
3281
- var list = new Command10("list").description("List deployments (org-wide, or scoped to a project)").option("--project <id>", "Scope to a project (defaults to the linked project)").option("--env <environment>", "Filter by environment: production | preview").option("--limit <n>", "Max rows to fetch", "50").action(
3282
- run(async (opts) => {
3501
+ var list = new Command11("list").description("List deployments (org-wide, or scoped to a project)").option("--project <id>", "Scope to a project (defaults to the linked project)").option("--env <environment>", "Filter by environment: production | preview").option("--limit <n>", "Max rows to fetch", "50").action(
3502
+ run2(async (opts) => {
3283
3503
  const projectId = opts.project || readProjectLink()?.projectId;
3284
3504
  const params = new URLSearchParams();
3285
3505
  if (projectId) params.set("projectId", projectId);
@@ -3301,8 +3521,8 @@ var list = new Command10("list").description("List deployments (org-wide, or sco
3301
3521
  printTable(rows, ["id", "status", "env", "branch", "commit", "active", "created"]);
3302
3522
  })
3303
3523
  );
3304
- var get = new Command10("get").description("Show a single deployment").argument("<id>", "Deployment ID").action(
3305
- run(async (id) => {
3524
+ var get = new Command11("get").description("Show a single deployment").argument("<id>", "Deployment ID").action(
3525
+ run2(async (id) => {
3306
3526
  const res = await apiRequest(`/deployments/${id}`);
3307
3527
  const d = res.data ?? {};
3308
3528
  if (isJsonMode()) return printJson(d);
@@ -3322,20 +3542,20 @@ var get = new Command10("get").description("Show a single deployment").argument(
3322
3542
  );
3323
3543
  })
3324
3544
  );
3325
- var info2 = new Command10("info").description("Show container info for a deployment").argument("<id>", "Deployment ID").action(
3326
- run(async (id) => {
3545
+ var info2 = new Command11("info").description("Show container info for a deployment").argument("<id>", "Deployment ID").action(
3546
+ run2(async (id) => {
3327
3547
  const res = await apiRequest(`/deployments/${id}/info`);
3328
3548
  printJson(res.data ?? res);
3329
3549
  })
3330
3550
  );
3331
- var usage = new Command10("usage").description("Show container resource usage for a deployment").argument("<id>", "Deployment ID").action(
3332
- run(async (id) => {
3551
+ var usage = new Command11("usage").description("Show container resource usage for a deployment").argument("<id>", "Deployment ID").action(
3552
+ run2(async (id) => {
3333
3553
  const res = await apiRequest(`/deployments/${id}/usage`);
3334
3554
  printJson(res.data ?? res);
3335
3555
  })
3336
3556
  );
3337
- var redeploy = new Command10("redeploy").description("Redeploy from an existing deployment").argument("<id>", "Deployment ID").option("--use-existing-commit", "Rebuild the same commit instead of the latest on the branch").action(
3338
- run(async (id, opts) => {
3557
+ var redeploy = new Command11("redeploy").description("Redeploy from an existing deployment").argument("<id>", "Deployment ID").option("--use-existing-commit", "Rebuild the same commit instead of the latest on the branch").action(
3558
+ run2(async (id, opts) => {
3339
3559
  const res = await apiRequest(`/deployments/${id}/redeploy`, {
3340
3560
  method: "POST",
3341
3561
  body: JSON.stringify({ useExistingCommit: opts.useExistingCommit === true })
@@ -3343,14 +3563,14 @@ var redeploy = new Command10("redeploy").description("Redeploy from an existing
3343
3563
  report(res, `Redeploy triggered for ${id}`);
3344
3564
  })
3345
3565
  );
3346
- var rollback = new Command10("rollback").description("Roll back to a previous deployment").argument("<id>", "Deployment ID to roll back to").action(
3347
- run(async (id) => {
3566
+ var rollback = new Command11("rollback").description("Roll back to a previous deployment").argument("<id>", "Deployment ID to roll back to").action(
3567
+ run2(async (id) => {
3348
3568
  const res = await apiRequest(`/deployments/${id}/rollback`, { method: "POST" });
3349
3569
  report(res, `Rolled back to ${id}`);
3350
3570
  })
3351
3571
  );
3352
- var pin = new Command10("pin").description("Pin (or unpin) a deployment's rollback artifact").argument("<id>", "Deployment ID").option("--off", "Unpin instead of pin").action(
3353
- run(async (id, opts) => {
3572
+ var pin = new Command11("pin").description("Pin (or unpin) a deployment's rollback artifact").argument("<id>", "Deployment ID").option("--off", "Unpin instead of pin").action(
3573
+ run2(async (id, opts) => {
3354
3574
  const pinned = !opts.off;
3355
3575
  const res = await apiRequest(`/deployments/${id}/pin`, {
3356
3576
  method: "POST",
@@ -3359,32 +3579,32 @@ var pin = new Command10("pin").description("Pin (or unpin) a deployment's rollba
3359
3579
  report(res, `${pinned ? "Pinned" : "Unpinned"} ${id}`);
3360
3580
  })
3361
3581
  );
3362
- var cancel = new Command10("cancel").description("Cancel an in-progress deployment").argument("<id>", "Deployment ID").action(
3363
- run(async (id) => {
3582
+ var cancel = new Command11("cancel").description("Cancel an in-progress deployment").argument("<id>", "Deployment ID").action(
3583
+ run2(async (id) => {
3364
3584
  const res = await apiRequest(`/deployments/${id}/cancel`, { method: "POST" });
3365
3585
  report(res, `Cancelled ${id}`);
3366
3586
  })
3367
3587
  );
3368
- var restart = new Command10("restart").description("Restart a deployment's container").argument("<id>", "Deployment ID").action(
3369
- run(async (id) => {
3588
+ var restart = new Command11("restart").description("Restart a deployment's container").argument("<id>", "Deployment ID").action(
3589
+ run2(async (id) => {
3370
3590
  const res = await apiRequest(`/deployments/${id}/restart`, { method: "POST" });
3371
3591
  report(res, `Restarted ${id}`);
3372
3592
  })
3373
3593
  );
3374
- var reject = new Command10("reject").description("Reject a finished deployment awaiting a keep/reject decision").argument("<id>", "Deployment ID").action(
3375
- run(async (id) => {
3594
+ var reject = new Command11("reject").description("Reject a finished deployment awaiting a keep/reject decision").argument("<id>", "Deployment ID").action(
3595
+ run2(async (id) => {
3376
3596
  const res = await apiRequest(`/deployments/${id}/reject`, { method: "POST" });
3377
3597
  report(res, `Rejected ${id}`);
3378
3598
  })
3379
3599
  );
3380
- var keep = new Command10("keep").description("Keep a finished deployment awaiting a keep/reject decision").argument("<id>", "Deployment ID").action(
3381
- run(async (id) => {
3600
+ var keep = new Command11("keep").description("Keep a finished deployment awaiting a keep/reject decision").argument("<id>", "Deployment ID").action(
3601
+ run2(async (id) => {
3382
3602
  const res = await apiRequest(`/deployments/${id}/keep`, { method: "POST" });
3383
3603
  report(res, `Kept ${id}`);
3384
3604
  })
3385
3605
  );
3386
- var rm = new Command10("rm").description("Delete a deployment").argument("<id>", "Deployment ID").option("-y, --yes", "Skip the confirmation prompt").action(
3387
- run(async (id, opts) => {
3606
+ var rm = new Command11("rm").description("Delete a deployment").argument("<id>", "Deployment ID").option("-y, --yes", "Skip the confirmation prompt").action(
3607
+ run2(async (id, opts) => {
3388
3608
  if (!opts.yes && !isJsonMode() && !await confirm(`Delete deployment ${id}?`)) {
3389
3609
  err("Aborted.");
3390
3610
  process.exit(1);
@@ -3393,8 +3613,8 @@ var rm = new Command10("rm").description("Delete a deployment").argument("<id>",
3393
3613
  report(res, `Deleted ${id}`);
3394
3614
  })
3395
3615
  );
3396
- var sslStatus = new Command10("status").description("Check SSL certificate status for a domain").argument("<domain>", "Domain to probe").action(
3397
- run(async (domain) => {
3616
+ var sslStatus = new Command11("status").description("Check SSL certificate status for a domain").argument("<domain>", "Domain to probe").action(
3617
+ run2(async (domain) => {
3398
3618
  const res = await apiRequest("/deployments/ssl/status", {
3399
3619
  method: "POST",
3400
3620
  body: JSON.stringify({ domain })
@@ -3402,8 +3622,8 @@ var sslStatus = new Command10("status").description("Check SSL certificate statu
3402
3622
  printJson(res);
3403
3623
  })
3404
3624
  );
3405
- var sslRenew = new Command10("renew").description("Renew (issue) an SSL certificate for a domain").argument("<domain>", "Domain to renew").option("--www", "Also include the www subdomain").action(
3406
- run(async (domain, opts) => {
3625
+ var sslRenew = new Command11("renew").description("Renew (issue) an SSL certificate for a domain").argument("<domain>", "Domain to renew").option("--www", "Also include the www subdomain").action(
3626
+ run2(async (domain, opts) => {
3407
3627
  const res = await apiRequest("/deployments/ssl/renew", {
3408
3628
  method: "POST",
3409
3629
  body: JSON.stringify({ domain, includeWww: opts.www === true })
@@ -3411,12 +3631,12 @@ var sslRenew = new Command10("renew").description("Renew (issue) an SSL certific
3411
3631
  report(res, `SSL renewal requested for ${domain}`);
3412
3632
  })
3413
3633
  );
3414
- var ssl = new Command10("ssl").description("SSL certificate operations").addCommand(sslStatus).addCommand(sslRenew);
3415
- var deploymentCommand = new Command10("deployment").alias("deployments").description("Manage deployments (list, inspect, redeploy, rollback, \u2026)").addCommand(list).addCommand(get).addCommand(info2).addCommand(usage).addCommand(redeploy).addCommand(rollback).addCommand(pin).addCommand(cancel).addCommand(restart).addCommand(reject).addCommand(keep).addCommand(rm).addCommand(ssl);
3634
+ var ssl = new Command11("ssl").description("SSL certificate operations").addCommand(sslStatus).addCommand(sslRenew);
3635
+ var deploymentCommand = new Command11("deployment").alias("deployments").description("Manage deployments (list, inspect, redeploy, rollback, \u2026)").addCommand(list).addCommand(get).addCommand(info2).addCommand(usage).addCommand(redeploy).addCommand(rollback).addCommand(pin).addCommand(cancel).addCommand(restart).addCommand(reject).addCommand(keep).addCommand(rm).addCommand(ssl);
3416
3636
 
3417
3637
  // src/commands/logs.ts
3418
- import { Command as Command11 } from "commander";
3419
- var logsCommand = new Command11("logs").description("View or stream a deployment's logs").argument("<deploymentId>", "Deployment ID").option("-f, --follow", "Stream live logs via SSE until the deployment finishes").option("--tail <n>", "Show only the last N log lines (snapshot mode)").action(async (deploymentId, opts) => {
3638
+ import { Command as Command12 } from "commander";
3639
+ var logsCommand = new Command12("logs").description("View or stream a deployment's logs").argument("<deploymentId>", "Deployment ID").option("-f, --follow", "Stream live logs via SSE until the deployment finishes").option("--tail <n>", "Show only the last N log lines (snapshot mode)").action(async (deploymentId, opts) => {
3420
3640
  if (opts.follow) {
3421
3641
  try {
3422
3642
  const result = await streamDeploymentLogs(deploymentId);
@@ -3448,8 +3668,8 @@ var logsCommand = new Command11("logs").description("View or stream a deployment
3448
3668
  });
3449
3669
 
3450
3670
  // src/commands/project.ts
3451
- import { Command as Command12 } from "commander";
3452
- import chalk8 from "chalk";
3671
+ import { Command as Command13 } from "commander";
3672
+ import chalk9 from "chalk";
3453
3673
  import { createInterface as createInterface4 } from "readline/promises";
3454
3674
  import { stdin as input3, stdout as output3 } from "process";
3455
3675
  function action(fn) {
@@ -3480,12 +3700,12 @@ function printProject(project) {
3480
3700
  ];
3481
3701
  for (const [k, v] of fields) {
3482
3702
  if (v === null || v === void 0 || v === "") continue;
3483
- process.stdout.write(` ${chalk8.dim(k.padEnd(12))} ${String(v)}
3703
+ process.stdout.write(` ${chalk9.dim(k.padEnd(12))} ${String(v)}
3484
3704
  `);
3485
3705
  }
3486
3706
  }
3487
3707
  var ENVIRONMENTS = ["production", "preview", "development"];
3488
- var listCmd2 = new Command12("list").alias("ls").description("List projects in the active organization").action(
3708
+ var listCmd2 = new Command13("list").alias("ls").description("List projects in the active organization").action(
3489
3709
  action(async () => {
3490
3710
  const rows = [];
3491
3711
  for await (const p of paginate("/projects")) {
@@ -3500,7 +3720,7 @@ var listCmd2 = new Command12("list").alias("ls").description("List projects in t
3500
3720
  printTable(rows, ["id", "name", "slug", "repo", "source"]);
3501
3721
  })
3502
3722
  );
3503
- var getCmd = new Command12("get").description("Show a single project").argument("<id>", "Project ID").action(
3723
+ var getCmd = new Command13("get").description("Show a single project").argument("<id>", "Project ID").action(
3504
3724
  action(async (id) => {
3505
3725
  const { data } = await apiRequest(
3506
3726
  `/projects/${encodeURIComponent(id)}`
@@ -3508,7 +3728,7 @@ var getCmd = new Command12("get").description("Show a single project").argument(
3508
3728
  printProject(data);
3509
3729
  })
3510
3730
  );
3511
- var createCmd = new Command12("create").description("Create a project").requiredOption("--name <name>", "Project name").option("--slug <slug>", "Free-subdomain slug (slug.opsh.io)").option("--git-owner <owner>", "GitHub owner/org").option("--git-repo <repo>", "GitHub repository name").option("--git-branch <branch>", "Git branch to deploy").option("--framework <framework>", "Stack/framework id").option("--local-path <path>", "Local source path").option("--port <port>", "Container port", (v) => Number(v)).option(
3731
+ var createCmd = new Command13("create").description("Create a project").requiredOption("--name <name>", "Project name").option("--slug <slug>", "Free-subdomain slug (slug.opsh.io)").option("--git-owner <owner>", "GitHub owner/org").option("--git-repo <repo>", "GitHub repository name").option("--git-branch <branch>", "Git branch to deploy").option("--framework <framework>", "Stack/framework id").option("--local-path <path>", "Local source path").option("--port <port>", "Container port", (v) => Number(v)).option(
3512
3732
  "--type <type>",
3513
3733
  "Project type: app | docker | services | monorepo"
3514
3734
  ).action(
@@ -3532,12 +3752,12 @@ var createCmd = new Command12("create").description("Create a project").required
3532
3752
  printProject(data);
3533
3753
  })
3534
3754
  );
3535
- var deleteCmd = new Command12("delete").alias("rm").description("Delete a project (tears down all resources)").argument("<id>", "Project ID").option("--force", "Cancel active work and delete anyway").option("--force-orphan", "Orphan resources that won't destroy, then drop the row").option("--wipe-volumes", "Also destroy persistent volumes").option("-y, --yes", "Skip the confirmation prompt").action(
3755
+ var deleteCmd = new Command13("delete").alias("rm").description("Delete a project (tears down all resources)").argument("<id>", "Project ID").option("--force", "Cancel active work and delete anyway").option("--force-orphan", "Orphan resources that won't destroy, then drop the row").option("--wipe-volumes", "Also destroy persistent volumes").option("-y, --yes", "Skip the confirmation prompt").action(
3536
3756
  action(async (id, opts) => {
3537
3757
  if (!opts.yes) {
3538
3758
  const rl = createInterface4({ input: input3, output: output3 });
3539
3759
  const answer = await rl.question(
3540
- chalk8.yellow(` Delete project ${id}? This cannot be undone. `) + "(y/N) "
3760
+ chalk9.yellow(` Delete project ${id}? This cannot be undone. `) + "(y/N) "
3541
3761
  );
3542
3762
  rl.close();
3543
3763
  if (answer.trim().toLowerCase() !== "y") {
@@ -3563,7 +3783,7 @@ var deleteCmd = new Command12("delete").alias("rm").description("Delete a projec
3563
3783
  `);
3564
3784
  })
3565
3785
  );
3566
- var envCmd = new Command12("env").description("Manage project environment variables");
3786
+ var envCmd = new Command13("env").description("Manage project environment variables");
3567
3787
  envCmd.command("get").description("List env vars (secret values are masked by the API)").argument("<id>", "Project ID").option("--environment <env>", "Filter by environment (production|preview|development)").action(
3568
3788
  action(async (id, opts) => {
3569
3789
  const qs = opts.environment ? `?environment=${encodeURIComponent(opts.environment)}` : "";
@@ -3637,7 +3857,7 @@ envCmd.command("set").description("Merge env vars: upsert KEY=VALUE pairs and/or
3637
3857
  );
3638
3858
  })
3639
3859
  );
3640
- var gitCmd = new Command12("git").description("Manage git linkage and auto-deploy");
3860
+ var gitCmd = new Command13("git").description("Manage git linkage and auto-deploy");
3641
3861
  gitCmd.command("link").description("Link a GitHub repository to a project").argument("<id>", "Project ID").requiredOption("--owner <owner>", "GitHub owner/org").requiredOption("--repo <repo>", "Repository name").option("--branch <branch>", "Branch (defaults to the repo's default branch)").option("--installation-id <id>", "GitHub App installation id", (v) => Number(v)).action(
3642
3862
  action(async (id, opts) => {
3643
3863
  const result = await apiRequest(
@@ -3725,7 +3945,7 @@ gitCmd.command("webhook-domain").description("Set or clear the domain that recei
3725
3945
  `);
3726
3946
  })
3727
3947
  );
3728
- var connectCmd = new Command12("connect").description("Connect a custom domain to a project").argument("<id>", "Project ID").argument("<domain>", "Custom domain hostname").option("--include-www", "Also connect the www. variant").action(
3948
+ var connectCmd = new Command13("connect").description("Connect a custom domain to a project").argument("<id>", "Project ID").argument("<domain>", "Custom domain hostname").option("--include-www", "Also connect the www. variant").action(
3729
3949
  action(async (id, domain, opts) => {
3730
3950
  const result = await apiRequest(
3731
3951
  `/projects/${encodeURIComponent(id)}/connect`,
@@ -3745,7 +3965,7 @@ var connectCmd = new Command12("connect").description("Connect a custom domain t
3745
3965
  printJson(result.records);
3746
3966
  })
3747
3967
  );
3748
- var enableCmd = new Command12("enable").description("Start a stopped project").argument("<id>", "Project ID").action(
3968
+ var enableCmd = new Command13("enable").description("Start a stopped project").argument("<id>", "Project ID").action(
3749
3969
  action(async (id) => {
3750
3970
  const result = await apiRequest(
3751
3971
  `/projects/${encodeURIComponent(id)}/enable`,
@@ -3757,7 +3977,7 @@ var enableCmd = new Command12("enable").description("Start a stopped project").a
3757
3977
  `);
3758
3978
  })
3759
3979
  );
3760
- var disableCmd = new Command12("disable").description("Stop a running project").argument("<id>", "Project ID").action(
3980
+ var disableCmd = new Command13("disable").description("Stop a running project").argument("<id>", "Project ID").action(
3761
3981
  action(async (id) => {
3762
3982
  const result = await apiRequest(
3763
3983
  `/projects/${encodeURIComponent(id)}/disable`,
@@ -3770,7 +3990,7 @@ var disableCmd = new Command12("disable").description("Stop a running project").
3770
3990
  })
3771
3991
  );
3772
3992
  var SLEEP_MODES = ["auto_sleep", "always_on"];
3773
- var sleepModeCmd = new Command12("sleep-mode").description("Set the project sleep mode").argument("<id>", "Project ID").argument("<mode>", `One of: ${SLEEP_MODES.join(", ")}`).action(
3993
+ var sleepModeCmd = new Command13("sleep-mode").description("Set the project sleep mode").argument("<id>", "Project ID").argument("<mode>", `One of: ${SLEEP_MODES.join(", ")}`).action(
3774
3994
  action(async (id, mode) => {
3775
3995
  if (!SLEEP_MODES.includes(mode)) {
3776
3996
  err(` mode must be one of: ${SLEEP_MODES.join(", ")}`);
@@ -3788,7 +4008,7 @@ var sleepModeCmd = new Command12("sleep-mode").description("Set the project slee
3788
4008
  })
3789
4009
  );
3790
4010
  var TRANSFER_DIRS = ["to-cloud", "to-self-hosted"];
3791
- var transferCmd = new Command12("transfer").description("Promote a project to Openship Cloud, or bring it back (self-hosted only)").argument("<id>", "Project ID").argument("<direction>", `One of: ${TRANSFER_DIRS.join(", ")}`).action(
4011
+ var transferCmd = new Command13("transfer").description("Promote a project to Openship Cloud, or bring it back (self-hosted only)").argument("<id>", "Project ID").argument("<direction>", `One of: ${TRANSFER_DIRS.join(", ")}`).action(
3792
4012
  action(async (id, direction) => {
3793
4013
  if (!TRANSFER_DIRS.includes(direction)) {
3794
4014
  err(` direction must be one of: ${TRANSFER_DIRS.join(", ")}`);
@@ -3811,7 +4031,7 @@ var transferCmd = new Command12("transfer").description("Promote a project to Op
3811
4031
  );
3812
4032
  })
3813
4033
  );
3814
- var logsCmd = new Command12("logs").description("Show or stream runtime (container) logs").argument("<id>", "Project ID").option("--tail <n>", "Number of recent lines", (v) => Number(v)).option("-f, --follow", "Stream logs until interrupted").action(
4034
+ var logsCmd = new Command13("logs").description("Show or stream runtime (container) logs").argument("<id>", "Project ID").option("--tail <n>", "Number of recent lines", (v) => Number(v)).option("-f, --follow", "Stream logs until interrupted").action(
3815
4035
  action(async (id, opts) => {
3816
4036
  const tailQs = opts.tail ? `?tail=${opts.tail}` : "";
3817
4037
  if (!opts.follow) {
@@ -3839,7 +4059,7 @@ var logsCmd = new Command12("logs").description("Show or stream runtime (contain
3839
4059
  }
3840
4060
  })
3841
4061
  );
3842
- var serverLogsCmd = new Command12("server-logs").description("Show or stream HTTP request logs (edge/OpenResty)").argument("<id>", "Project ID").option("--limit <n>", "Number of recent entries (max 200)", (v) => Number(v)).option("--domain <domain>", "Restrict to a specific domain").option("-f, --follow", "Stream request logs until interrupted").action(
4062
+ var serverLogsCmd = new Command13("server-logs").description("Show or stream HTTP request logs (edge/OpenResty)").argument("<id>", "Project ID").option("--limit <n>", "Number of recent entries (max 200)", (v) => Number(v)).option("--domain <domain>", "Restrict to a specific domain").option("-f, --follow", "Stream request logs until interrupted").action(
3843
4063
  action(async (id, opts) => {
3844
4064
  const base = `/projects/${encodeURIComponent(id)}/server-logs`;
3845
4065
  const domainQs = opts.domain ? `domain=${encodeURIComponent(opts.domain)}` : "";
@@ -3880,14 +4100,14 @@ function safeParse(s) {
3880
4100
  }
3881
4101
  }
3882
4102
  function printLogEntry(entry) {
3883
- const ts = entry.timestamp ? chalk8.dim(String(entry.timestamp)) : "";
4103
+ const ts = entry.timestamp ? chalk9.dim(String(entry.timestamp)) : "";
3884
4104
  const level = String(entry.level ?? "info");
3885
- const color = level === "error" ? chalk8.red : level === "warn" ? chalk8.yellow : chalk8.dim;
4105
+ const color = level === "error" ? chalk9.red : level === "warn" ? chalk9.yellow : chalk9.dim;
3886
4106
  const msg = entry.message ?? entry.data ?? "";
3887
4107
  process.stdout.write(` ${ts} ${color(level.padEnd(5))} ${String(msg)}
3888
4108
  `);
3889
4109
  }
3890
- var projectCommand = new Command12("project").alias("projects").description("Manage Openship projects");
4110
+ var projectCommand = new Command13("project").alias("projects").description("Manage Openship projects");
3891
4111
  projectCommand.addCommand(listCmd2);
3892
4112
  projectCommand.addCommand(getCmd);
3893
4113
  projectCommand.addCommand(createCmd);
@@ -3903,9 +4123,9 @@ projectCommand.addCommand(logsCmd);
3903
4123
  projectCommand.addCommand(serverLogsCmd);
3904
4124
 
3905
4125
  // src/commands/service.ts
3906
- import { Command as Command13 } from "commander";
3907
- import chalk9 from "chalk";
3908
- import { spawnSync as spawnSync2 } from "child_process";
4126
+ import { Command as Command14 } from "commander";
4127
+ import chalk10 from "chalk";
4128
+ import { spawnSync as spawnSync3 } from "child_process";
3909
4129
  import path from "path";
3910
4130
  import { createInterface as createInterface5 } from "readline/promises";
3911
4131
  import { stdin as input4, stdout as output4 } from "process";
@@ -3917,14 +4137,14 @@ function requireAuth() {
3917
4137
  }
3918
4138
  function fail(e) {
3919
4139
  if (e instanceof ApiError) {
3920
- err(` ${e.message}` + (e.status ? chalk9.dim(` (HTTP ${e.status})`) : ""));
4140
+ err(` ${e.message}` + (e.status ? chalk10.dim(` (HTTP ${e.status})`) : ""));
3921
4141
  } else {
3922
4142
  err(` ${e instanceof Error ? e.message : String(e)}`);
3923
4143
  }
3924
4144
  process.exit(1);
3925
4145
  }
3926
4146
  function stackCommand(name) {
3927
- return new Command13(name).requiredOption(
4147
+ return new Command14(name).requiredOption(
3928
4148
  "-p, --project <id|slug|name>",
3929
4149
  "Stack (project) id, slug, or name"
3930
4150
  );
@@ -4009,7 +4229,7 @@ var listCmd3 = stackCommand("list").description("List the services in a stack").
4009
4229
  image: s.image ?? "\u2014",
4010
4230
  enabled: s.enabled ? "yes" : "no",
4011
4231
  exposed: s.exposed ? "yes" : "no",
4012
- drift: s.drift ? chalk9.yellow("pending") : "\u2014"
4232
+ drift: s.drift ? chalk10.yellow("pending") : "\u2014"
4013
4233
  })),
4014
4234
  ["name", "kind", "image", "enabled", "exposed", "drift"]
4015
4235
  );
@@ -4172,7 +4392,7 @@ function mapComposeService(name, def, baseDir) {
4172
4392
  var syncCmd = stackCommand("sync").description("Sync a stack's services from a docker-compose file (services not in the file are removed)").argument("<compose-file>", "Path to docker-compose.yml / compose.yaml").option("-y, --yes", "Skip the confirmation prompt").action(async (composeFile, opts) => {
4173
4393
  requireAuth();
4174
4394
  const abs = path.resolve(composeFile);
4175
- const proc = spawnSync2(
4395
+ const proc = spawnSync3(
4176
4396
  "docker",
4177
4397
  ["compose", "-f", abs, "config", "--format", "json"],
4178
4398
  { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
@@ -4266,7 +4486,7 @@ var containersCmd = stackCommand("containers").description("List the stack's act
4266
4486
  fail(e);
4267
4487
  }
4268
4488
  });
4269
- var driftCmd = new Command13("drift").description(
4489
+ var driftCmd = new Command14("drift").description(
4270
4490
  "Resolve compose drift on a service (upstream compose changed a value you edited)"
4271
4491
  );
4272
4492
  function driftActionCommand(action2) {
@@ -4294,7 +4514,7 @@ function driftActionCommand(action2) {
4294
4514
  }
4295
4515
  driftCmd.addCommand(driftActionCommand("accept"));
4296
4516
  driftCmd.addCommand(driftActionCommand("keep"));
4297
- var envCmd2 = new Command13("env").description("Read and write a service's environment variables");
4517
+ var envCmd2 = new Command14("env").description("Read and write a service's environment variables");
4298
4518
  var envGetCmd = stackCommand("get").description("List a service's environment variables (secrets masked)").argument("<service>", "Service name or id").option("-e, --env <environment>", "Environment: production | preview | development").action(async (service, opts) => {
4299
4519
  requireAuth();
4300
4520
  try {
@@ -4377,8 +4597,8 @@ function printLogEntry2(entry) {
4377
4597
  process.stdout.write(JSON.stringify(entry) + "\n");
4378
4598
  return;
4379
4599
  }
4380
- const ts = entry.timestamp ? chalk9.dim(entry.timestamp) : "";
4381
- const line = entry.level === "error" ? chalk9.red(msg) : entry.level === "warn" ? chalk9.yellow(msg) : msg;
4600
+ const ts = entry.timestamp ? chalk10.dim(entry.timestamp) : "";
4601
+ const line = entry.level === "error" ? chalk10.red(msg) : entry.level === "warn" ? chalk10.yellow(msg) : msg;
4382
4602
  process.stdout.write(`${ts ? ts + " " : ""}${line}
4383
4603
  `);
4384
4604
  }
@@ -4427,7 +4647,7 @@ var execCmd = stackCommand("exec").description("Open an interactive shell in a s
4427
4647
  );
4428
4648
  process.exit(1);
4429
4649
  });
4430
- var serviceCommand = new Command13("service").alias("services").description("Manage the services in a compose stack (a multi-service project)");
4650
+ var serviceCommand = new Command14("service").alias("services").description("Manage the services in a compose stack (a multi-service project)");
4431
4651
  serviceCommand.addCommand(listCmd3);
4432
4652
  serviceCommand.addCommand(getCmd2);
4433
4653
  serviceCommand.addCommand(createCmd2);
@@ -4443,15 +4663,15 @@ serviceCommand.addCommand(logsCmd2);
4443
4663
  serviceCommand.addCommand(execCmd);
4444
4664
 
4445
4665
  // src/commands/domain.ts
4446
- import { Command as Command14 } from "commander";
4447
- import chalk10 from "chalk";
4666
+ import { Command as Command15 } from "commander";
4667
+ import chalk11 from "chalk";
4448
4668
  import ora3 from "ora";
4449
4669
  function spin(text) {
4450
4670
  return isJsonMode() ? null : ora3(text).start();
4451
4671
  }
4452
4672
  function fail2(e) {
4453
4673
  if (e instanceof ApiError) {
4454
- err(` ${e.message}${e.status ? chalk10.dim(` (${e.status})`) : ""}`);
4674
+ err(` ${e.message}${e.status ? chalk11.dim(` (${e.status})`) : ""}`);
4455
4675
  } else {
4456
4676
  err(` ${e instanceof Error ? e.message : String(e)}`);
4457
4677
  }
@@ -4479,7 +4699,7 @@ function printRecords(result) {
4479
4699
  ["type", "host", "value"]
4480
4700
  );
4481
4701
  }
4482
- var listCmd4 = new Command14("list").description("List a project's custom domains").requiredOption("-p, --project <id>", "Project ID to list domains for").action(async (opts) => {
4702
+ var listCmd4 = new Command15("list").description("List a project's custom domains").requiredOption("-p, --project <id>", "Project ID to list domains for").action(async (opts) => {
4483
4703
  try {
4484
4704
  const res = await apiRequest(
4485
4705
  `/domains?projectId=${encodeURIComponent(opts.project)}`
@@ -4494,7 +4714,7 @@ var listCmd4 = new Command14("list").description("List a project's custom domain
4494
4714
  fail2(e);
4495
4715
  }
4496
4716
  });
4497
- var addCmd2 = new Command14("add").description("Add a custom domain to a project").argument("<hostname>", "Domain hostname (e.g. app.example.com)").requiredOption("-p, --project <id>", "Project ID to attach the domain to").option("--primary", "Mark this domain as the project's primary", false).action(async (hostname, opts) => {
4717
+ var addCmd2 = new Command15("add").description("Add a custom domain to a project").argument("<hostname>", "Domain hostname (e.g. app.example.com)").requiredOption("-p, --project <id>", "Project ID to attach the domain to").option("--primary", "Mark this domain as the project's primary", false).action(async (hostname, opts) => {
4498
4718
  const sp = spin(`Adding ${hostname}\u2026`);
4499
4719
  try {
4500
4720
  const res = await apiRequest("/domains", {
@@ -4513,7 +4733,7 @@ var addCmd2 = new Command14("add").description("Add a custom domain to a project
4513
4733
  fail2(e);
4514
4734
  }
4515
4735
  });
4516
- var previewCmd = new Command14("preview").description("Preview the DNS records a hostname would need (no changes saved)").argument("<hostname>", "Domain hostname to preview").action(async (hostname) => {
4736
+ var previewCmd = new Command15("preview").description("Preview the DNS records a hostname would need (no changes saved)").argument("<hostname>", "Domain hostname to preview").action(async (hostname) => {
4517
4737
  try {
4518
4738
  const res = await apiRequest("/domains/preview", {
4519
4739
  method: "POST",
@@ -4524,7 +4744,7 @@ var previewCmd = new Command14("preview").description("Preview the DNS records a
4524
4744
  fail2(e);
4525
4745
  }
4526
4746
  });
4527
- var verifyCmd = new Command14("verify").description("Run DNS verification for a domain").argument("<id>", "Domain ID").action(async (id) => {
4747
+ var verifyCmd = new Command15("verify").description("Run DNS verification for a domain").argument("<id>", "Domain ID").action(async (id) => {
4528
4748
  const sp = spin("Checking DNS records\u2026");
4529
4749
  try {
4530
4750
  const res = await apiRaw(`/domains/${encodeURIComponent(id)}/verify`, { method: "POST" });
@@ -4550,7 +4770,7 @@ var verifyCmd = new Command14("verify").description("Run DNS verification for a
4550
4770
  fail2(e);
4551
4771
  }
4552
4772
  });
4553
- var primaryCmd = new Command14("primary").description("Make a domain the project's primary hostname").argument("<id>", "Domain ID").action(async (id) => {
4773
+ var primaryCmd = new Command15("primary").description("Make a domain the project's primary hostname").argument("<id>", "Domain ID").action(async (id) => {
4554
4774
  const sp = spin("Setting primary\u2026");
4555
4775
  try {
4556
4776
  const res = await apiRequest(`/domains/${encodeURIComponent(id)}/primary`, {
@@ -4563,7 +4783,7 @@ var primaryCmd = new Command14("primary").description("Make a domain the project
4563
4783
  fail2(e);
4564
4784
  }
4565
4785
  });
4566
- var recordsCmd = new Command14("records").description("Show the DNS records for an existing domain").argument("<id>", "Domain ID").action(async (id) => {
4786
+ var recordsCmd = new Command15("records").description("Show the DNS records for an existing domain").argument("<id>", "Domain ID").action(async (id) => {
4567
4787
  try {
4568
4788
  const res = await apiRequest(`/domains/${encodeURIComponent(id)}/records`);
4569
4789
  printRecords(res.data);
@@ -4581,7 +4801,7 @@ function printSsl(data) {
4581
4801
  if (data.issuer) info(` issuer: ${data.issuer}`);
4582
4802
  if (data.expiresAt) info(` expires: ${data.expiresAt}`);
4583
4803
  }
4584
- var renewCmd = new Command14("renew").description("Renew the SSL certificate for a domain").argument("<id>", "Domain ID").action(async (id) => {
4804
+ var renewCmd = new Command15("renew").description("Renew the SSL certificate for a domain").argument("<id>", "Domain ID").action(async (id) => {
4585
4805
  const sp = spin("Renewing certificate\u2026");
4586
4806
  try {
4587
4807
  const res = await apiRequest(`/domains/${encodeURIComponent(id)}/renew`, {
@@ -4594,7 +4814,7 @@ var renewCmd = new Command14("renew").description("Renew the SSL certificate for
4594
4814
  fail2(e);
4595
4815
  }
4596
4816
  });
4597
- var verifySslCmd = new Command14("verify-ssl").description("Recheck that a domain's SSL certificate is issued and valid (no reissue)").argument("<id>", "Domain ID").action(async (id) => {
4817
+ var verifySslCmd = new Command15("verify-ssl").description("Recheck that a domain's SSL certificate is issued and valid (no reissue)").argument("<id>", "Domain ID").action(async (id) => {
4598
4818
  const sp = spin("Checking certificate\u2026");
4599
4819
  try {
4600
4820
  const res = await apiRequest(`/domains/${encodeURIComponent(id)}/verify-ssl`, {
@@ -4609,7 +4829,7 @@ var verifySslCmd = new Command14("verify-ssl").description("Recheck that a domai
4609
4829
  fail2(e);
4610
4830
  }
4611
4831
  });
4612
- var renewAllCmd = new Command14("renew-all").description("Renew SSL for every near-expiry domain in your organization").action(async () => {
4832
+ var renewAllCmd = new Command15("renew-all").description("Renew SSL for every near-expiry domain in your organization").action(async () => {
4613
4833
  const sp = spin("Renewing expiring certificates\u2026");
4614
4834
  try {
4615
4835
  const res = await apiRequest("/domains/renew-all", { method: "POST" });
@@ -4631,11 +4851,11 @@ var renewAllCmd = new Command14("renew-all").description("Renew SSL for every ne
4631
4851
  fail2(e);
4632
4852
  }
4633
4853
  });
4634
- var domainCommand = new Command14("domain").description("Manage custom domains, DNS verification, and SSL certificates").addCommand(listCmd4).addCommand(addCmd2).addCommand(previewCmd).addCommand(verifyCmd).addCommand(primaryCmd).addCommand(recordsCmd).addCommand(renewCmd).addCommand(verifySslCmd).addCommand(renewAllCmd);
4854
+ var domainCommand = new Command15("domain").description("Manage custom domains, DNS verification, and SSL certificates").addCommand(listCmd4).addCommand(addCmd2).addCommand(previewCmd).addCommand(verifyCmd).addCommand(primaryCmd).addCommand(recordsCmd).addCommand(renewCmd).addCommand(verifySslCmd).addCommand(renewAllCmd);
4635
4855
 
4636
4856
  // src/commands/server.ts
4637
- import { Command as Command15 } from "commander";
4638
- import chalk11 from "chalk";
4857
+ import { Command as Command16 } from "commander";
4858
+ import chalk12 from "chalk";
4639
4859
  import ora4 from "ora";
4640
4860
  var INSTALLABLE = ["docker", "git", "openresty", "certbot", "rsync"];
4641
4861
  function guard(fn) {
@@ -4667,7 +4887,7 @@ function connBody(o) {
4667
4887
  sshArgs: o.sshArgs
4668
4888
  };
4669
4889
  }
4670
- var server = new Command15("server").description("Manage self-hosted SSH servers");
4890
+ var server = new Command16("server").description("Manage self-hosted SSH servers");
4671
4891
  server.command("list").alias("ls").description("List servers in the active organization").action(
4672
4892
  guard(async () => {
4673
4893
  const servers = await apiRequest("/system/servers");
@@ -4766,8 +4986,8 @@ server.command("install <serverId>").description("Install components on a server
4766
4986
  printJson({ event: ev.event, ...payload });
4767
4987
  } else if (ev.event === "log") {
4768
4988
  const p = payload;
4769
- const line = ` ${chalk11.dim(`[${p.component}]`)} ${p.message ?? ""}`;
4770
- process.stderr.write((p.level === "error" ? chalk11.red(line) : line) + "\n");
4989
+ const line = ` ${chalk12.dim(`[${p.component}]`)} ${p.message ?? ""}`;
4990
+ process.stderr.write((p.level === "error" ? chalk12.red(line) : line) + "\n");
4771
4991
  } else if (ev.event === "progress") {
4772
4992
  const p = payload;
4773
4993
  if (p.component) info(` ${p.component}: ${p.status}`);
@@ -4885,9 +5105,9 @@ function fmtUptime(seconds) {
4885
5105
  var serverCommand = server;
4886
5106
 
4887
5107
  // src/commands/system.ts
4888
- import { Command as Command16 } from "commander";
5108
+ import { Command as Command17 } from "commander";
4889
5109
  import ora5 from "ora";
4890
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
5110
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
4891
5111
  import { createInterface as createInterface6 } from "readline/promises";
4892
5112
  import { stdin as input5, stdout as output5 } from "process";
4893
5113
  async function guarded(fn) {
@@ -4933,7 +5153,7 @@ async function promptHidden(query) {
4933
5153
  output5.write("\n");
4934
5154
  return answer;
4935
5155
  }
4936
- var settingsCommand = new Command16("settings").description("Read or update instance settings");
5156
+ var settingsCommand = new Command17("settings").description("Read or update instance settings");
4937
5157
  settingsCommand.command("get").description("Show current instance settings").action(async () => {
4938
5158
  await guarded(async () => {
4939
5159
  const s = await apiRequest("/system/settings");
@@ -4972,7 +5192,7 @@ settingsCommand.command("set").description("Update instance-level settings").opt
4972
5192
  `));
4973
5193
  });
4974
5194
  });
4975
- var onboardingCommand = new Command16("onboarding").description("First-run instance setup");
5195
+ var onboardingCommand = new Command17("onboarding").description("First-run instance setup");
4976
5196
  onboardingCommand.command("apply").description("Configure a fresh instance (fails once already configured)").option("--ssh-host <host>", "SSH host of the target server").option("--ssh-port <n>", "SSH port (default 22)").option("--ssh-user <user>", "SSH user (default root)").option("--ssh-auth-method <method>", "SSH auth method").option("--ssh-password <password>", "SSH password").option("--ssh-key-path <path>", "SSH private key path").option("--ssh-key-passphrase <pass>", "SSH key passphrase").option("--ssh-jump-host <host>", "SSH jump host").option("--ssh-args <args>", "Extra SSH args").option("--server-name <name>", "Display name for the server").option("--auth-mode <mode>", "Initial auth mode: none | local | cloud").option("--tunnel-provider <provider>", "Tunnel provider").option("--tunnel-token <token>", "Tunnel token").option("--default-build-mode <mode>", "Default build mode").option("--default-rollback-window <n>", "Default rollback window").action(async (opts) => {
4977
5197
  await guarded(async () => {
4978
5198
  const body = {
@@ -5006,7 +5226,7 @@ onboardingCommand.command("apply").description("Configure a fresh instance (fail
5006
5226
  }
5007
5227
  });
5008
5228
  });
5009
- var upgradeToAuthCommand = new Command16("upgrade-to-auth").description("Promote a zero-auth instance to email/password login").option("--name <name>", "Account display name").option("--email <email>", "Account email").option("--password <password>", "Account password (prompted if omitted)").option("--use-own-mail-server", "Warm the self-hosted mail server for auth emails").action(async (opts) => {
5229
+ var upgradeToAuthCommand = new Command17("upgrade-to-auth").description("Promote a zero-auth instance to email/password login").option("--name <name>", "Account display name").option("--email <email>", "Account email").option("--password <password>", "Account password (prompted if omitted)").option("--use-own-mail-server", "Warm the self-hosted mail server for auth emails").action(async (opts) => {
5010
5230
  await guarded(async () => {
5011
5231
  const name = opts.name;
5012
5232
  const email = opts.email;
@@ -5035,7 +5255,7 @@ var upgradeToAuthCommand = new Command16("upgrade-to-auth").description("Promote
5035
5255
  `));
5036
5256
  });
5037
5257
  });
5038
- var browseCommand = new Command16("browse").description("List directories on the instance host (defaults to home)").argument("[path]", "Directory to list").action(async (path2) => {
5258
+ var browseCommand = new Command17("browse").description("List directories on the instance host (defaults to home)").argument("[path]", "Directory to list").action(async (path2) => {
5039
5259
  await guarded(async () => {
5040
5260
  const qs = path2 ? `?path=${encodeURIComponent(path2)}` : "";
5041
5261
  const res = await apiRequest(`/system/browse${qs}`);
@@ -5064,7 +5284,7 @@ function buildDomain(opts) {
5064
5284
  err("\n A domain is required: pass --hostname <host> or --slug <slug>.\n");
5065
5285
  process.exit(1);
5066
5286
  }
5067
- var migrationCommand = new Command16("migration").description("Team-mode migration lifecycle");
5287
+ var migrationCommand = new Command17("migration").description("Team-mode migration lifecycle");
5068
5288
  migrationCommand.command("preflight").description("Read-only readiness check for the own-server migration").requiredOption("--server-id <id>", "Target server id").option("--hostname <host>", "Custom domain pointing at the server").option("--slug <slug>", "Free <slug>.opsh.io subdomain").action(async (opts) => {
5069
5289
  await guarded(async () => {
5070
5290
  const domain = buildDomain(opts);
@@ -5171,7 +5391,7 @@ migrationCommand.command("switch-back").description("Reverse migration back to s
5171
5391
  }
5172
5392
  });
5173
5393
  });
5174
- var dataTransferCommand = new Command16("data-transfer").description(
5394
+ var dataTransferCommand = new Command17("data-transfer").description(
5175
5395
  "Whole-instance export / import (owner-only)"
5176
5396
  );
5177
5397
  dataTransferCommand.command("export").description("Export the entire instance to a JSON file").option("--passphrase <passphrase>", "Seal secrets under this passphrase").option("--out <file>", "Write the export to this file instead of stdout").action(async (opts) => {
@@ -5187,7 +5407,7 @@ dataTransferCommand.command("export").description("Export the entire instance to
5187
5407
  );
5188
5408
  spin4?.succeed("Export ready.");
5189
5409
  if (opts.out) {
5190
- writeFileSync5(opts.out, JSON.stringify(file));
5410
+ writeFileSync6(opts.out, JSON.stringify(file));
5191
5411
  const tables = Object.keys(file.dump?.tables ?? {}).length;
5192
5412
  report2(
5193
5413
  { out: opts.out, tables },
@@ -5241,11 +5461,11 @@ dataTransferCommand.command("import").description("Import an instance export fil
5241
5461
  }
5242
5462
  });
5243
5463
  });
5244
- var systemCommand = new Command16("system").description("Instance settings, onboarding, migration, and data transfer").addCommand(settingsCommand).addCommand(onboardingCommand).addCommand(upgradeToAuthCommand).addCommand(browseCommand).addCommand(migrationCommand).addCommand(dataTransferCommand);
5464
+ var systemCommand = new Command17("system").description("Instance settings, onboarding, migration, and data transfer").addCommand(settingsCommand).addCommand(onboardingCommand).addCommand(upgradeToAuthCommand).addCommand(browseCommand).addCommand(migrationCommand).addCommand(dataTransferCommand);
5245
5465
 
5246
5466
  // src/commands/mail.ts
5247
- import { Command as Command17 } from "commander";
5248
- import chalk12 from "chalk";
5467
+ import { Command as Command18 } from "commander";
5468
+ import chalk13 from "chalk";
5249
5469
  import ora6 from "ora";
5250
5470
  import { createInterface as createInterface7 } from "readline/promises";
5251
5471
  import { stdin as input6, stdout as output6 } from "process";
@@ -5286,7 +5506,7 @@ function printRecordsObject(records) {
5286
5506
  if (rows.length === 0) return info(" (no DNS records)");
5287
5507
  printTable(rows, ["key", "type", "host", "value"]);
5288
5508
  }
5289
- var stepsCmd = new Command17("steps").description("List the mail setup steps").action(
5509
+ var stepsCmd = new Command18("steps").description("List the mail setup steps").action(
5290
5510
  guard2(async () => {
5291
5511
  const res = await apiRequest(
5292
5512
  "/mail/steps"
@@ -5299,7 +5519,7 @@ var stepsCmd = new Command17("steps").description("List the mail setup steps").a
5299
5519
  info(` ${res.total} steps total.`);
5300
5520
  })
5301
5521
  );
5302
- var statusCmd = new Command17("status").description("Show the setup progress for a mail server").argument("[serverId]", "Mail server ID (omit for the empty welcome shell)").action(
5522
+ var statusCmd = new Command18("status").description("Show the setup progress for a mail server").argument("[serverId]", "Mail server ID (omit for the empty welcome shell)").action(
5303
5523
  guard2(async (serverId) => {
5304
5524
  const q = serverId ? `?serverId=${encodeURIComponent(serverId)}` : "";
5305
5525
  const res = await apiRequest(`/mail/status${q}`);
@@ -5320,7 +5540,7 @@ var statusCmd = new Command17("status").description("Show the setup progress for
5320
5540
  }
5321
5541
  })
5322
5542
  );
5323
- var serversCmd = new Command17("servers").description("List every server the mail stack is installed on").action(
5543
+ var serversCmd = new Command18("servers").description("List every server the mail stack is installed on").action(
5324
5544
  guard2(async () => {
5325
5545
  const res = await apiRequest(
5326
5546
  "/mail/servers"
@@ -5341,7 +5561,7 @@ var serversCmd = new Command17("servers").description("List every server the mai
5341
5561
  );
5342
5562
  })
5343
5563
  );
5344
- var scanCmd = new Command17("scan").description("Probe a server for an existing mail install (read-only)").argument("<serverId>", "Server ID to scan").action(
5564
+ var scanCmd = new Command18("scan").description("Probe a server for an existing mail install (read-only)").argument("<serverId>", "Server ID to scan").action(
5345
5565
  guard2(async (serverId) => {
5346
5566
  const sp = spin2("Scanning server\u2026");
5347
5567
  const res = await apiRequest("/mail/scan", { method: "POST", body: JSON.stringify({ serverId }) });
@@ -5356,7 +5576,7 @@ var scanCmd = new Command17("scan").description("Probe a server for an existing
5356
5576
  else info(" Nothing to adopt on this server.");
5357
5577
  })
5358
5578
  );
5359
- var adoptCmd = new Command17("adopt").description("Re-adopt an existing mail install whose orchestrator state was lost").argument("<serverId>", "Server ID to adopt").action(
5579
+ var adoptCmd = new Command18("adopt").description("Re-adopt an existing mail install whose orchestrator state was lost").argument("<serverId>", "Server ID to adopt").action(
5360
5580
  guard2(async (serverId) => {
5361
5581
  const sp = spin2("Adopting mail server\u2026");
5362
5582
  const res = await apiRequest(
@@ -5369,7 +5589,7 @@ var adoptCmd = new Command17("adopt").description("Re-adopt an existing mail ins
5369
5589
  info(` completed: ${res.completed ? "yes" : "no"}`);
5370
5590
  })
5371
5591
  );
5372
- var setupCmd = new Command17("setup").description("Start or resume the mail setup wizard (streams over SSE)").argument("<serverId>", "Server ID to install the mail stack on").requiredOption("-d, --domain <domain>", "Mail domain (e.g. example.com)").option("--start-step <n>", "Resume from a specific step (1-13)").option("--config <json>", "iRedMail config overrides as a JSON object").action(
5592
+ var setupCmd = new Command18("setup").description("Start or resume the mail setup wizard (streams over SSE)").argument("<serverId>", "Server ID to install the mail stack on").requiredOption("-d, --domain <domain>", "Mail domain (e.g. example.com)").option("--start-step <n>", "Resume from a specific step (1-13)").option("--config <json>", "iRedMail config overrides as a JSON object").action(
5373
5593
  guard2(async (serverId, opts) => {
5374
5594
  let config;
5375
5595
  if (opts.config) {
@@ -5402,7 +5622,7 @@ var setupCmd = new Command17("setup").description("Start or resume the mail setu
5402
5622
  break;
5403
5623
  case "log": {
5404
5624
  const line = ` ${String(p.message ?? "")}`;
5405
- process.stderr.write((p.level === "error" ? chalk12.red(line) : chalk12.dim(line)) + "\n");
5625
+ process.stderr.write((p.level === "error" ? chalk13.red(line) : chalk13.dim(line)) + "\n");
5406
5626
  break;
5407
5627
  }
5408
5628
  case "step_done":
@@ -5438,7 +5658,7 @@ var setupCmd = new Command17("setup").description("Start or resume the mail setu
5438
5658
  if (failed) process.exit(1);
5439
5659
  })
5440
5660
  );
5441
- var cancelCmd = new Command17("cancel").description("Cancel the mail setup currently running").action(
5661
+ var cancelCmd = new Command18("cancel").description("Cancel the mail setup currently running").action(
5442
5662
  guard2(async () => {
5443
5663
  const res = await apiRequest("/mail/setup/cancel", {
5444
5664
  method: "POST"
@@ -5448,7 +5668,7 @@ var cancelCmd = new Command17("cancel").description("Cancel the mail setup curre
5448
5668
  })
5449
5669
  );
5450
5670
  function ackCommand(name, path2, description, successMsg) {
5451
- return new Command17(name).description(description).argument("<serverId>", "Mail server ID").action(
5671
+ return new Command18(name).description(description).argument("<serverId>", "Mail server ID").action(
5452
5672
  guard2(async (serverId) => {
5453
5673
  const res = await apiRequest(path2, {
5454
5674
  method: "POST",
@@ -5471,7 +5691,7 @@ var ptrAckCmd = ackCommand(
5471
5691
  "Acknowledge that reverse DNS (PTR) is configured",
5472
5692
  "PTR acknowledged. Re-run `mail setup` with --start-step to continue."
5473
5693
  );
5474
- var resetCmd = new Command17("reset").description("Wipe the on-server setup state file (does NOT touch installed daemons)").argument("<serverId>", "Mail server ID").option("-y, --yes", "Skip the confirmation prompt").action(
5694
+ var resetCmd = new Command18("reset").description("Wipe the on-server setup state file (does NOT touch installed daemons)").argument("<serverId>", "Mail server ID").option("-y, --yes", "Skip the confirmation prompt").action(
5475
5695
  guard2(async (serverId, opts) => {
5476
5696
  if (!opts.yes && !isJsonMode()) {
5477
5697
  const rl = createInterface7({ input: input6, output: output6 });
@@ -5487,7 +5707,7 @@ var resetCmd = new Command17("reset").description("Wipe the on-server setup stat
5487
5707
  ok(" Setup state reset.");
5488
5708
  })
5489
5709
  );
5490
- var forgetCmd = new Command17("forget").description("Stop managing a mail server (drops the DB row; leaves the stack + state intact)").argument("<serverId>", "Mail server ID").action(
5710
+ var forgetCmd = new Command18("forget").description("Stop managing a mail server (drops the DB row; leaves the stack + state intact)").argument("<serverId>", "Mail server ID").action(
5491
5711
  guard2(async (serverId) => {
5492
5712
  const res = await apiRequest(`/mail/servers/${encodeURIComponent(serverId)}`, {
5493
5713
  method: "DELETE"
@@ -5496,7 +5716,7 @@ var forgetCmd = new Command17("forget").description("Stop managing a mail server
5496
5716
  ok(` Forgot mail server ${serverId} (re-adopt with \`mail scan\` + \`mail adopt\`).`);
5497
5717
  })
5498
5718
  );
5499
- var healthCmd = new Command17("health").description("Show live status of every mail daemon").argument("<serverId>", "Mail server ID").action(
5719
+ var healthCmd = new Command18("health").description("Show live status of every mail daemon").argument("<serverId>", "Mail server ID").action(
5500
5720
  guard2(async (serverId) => {
5501
5721
  const sp = spin2("Checking mail daemons\u2026");
5502
5722
  const res = await apiRequest(`/mail/health/${encodeURIComponent(serverId)}`);
@@ -5513,7 +5733,7 @@ var healthCmd = new Command17("health").description("Show live status of every m
5513
5733
  );
5514
5734
  })
5515
5735
  );
5516
- var logsCmd3 = new Command17("logs").description("Tail a mail component's journal (snapshot)").argument("<serverId>", "Mail server ID").argument("<component>", "Component key (postfix|dovecot|amavis|clamav|iredapd|postgresql|\u2026)").option("-n, --lines <n>", "Number of lines (max 1000)", "200").action(
5736
+ var logsCmd3 = new Command18("logs").description("Tail a mail component's journal (snapshot)").argument("<serverId>", "Mail server ID").argument("<component>", "Component key (postfix|dovecot|amavis|clamav|iredapd|postgresql|\u2026)").option("-n, --lines <n>", "Number of lines (max 1000)", "200").action(
5517
5737
  guard2(async (serverId, component, opts) => {
5518
5738
  const res = await apiRequest(
5519
5739
  `/mail/admin/${encodeURIComponent(serverId)}/components/${encodeURIComponent(component)}/logs?lines=${encodeURIComponent(opts.lines)}`
@@ -5523,9 +5743,9 @@ var logsCmd3 = new Command17("logs").description("Tail a mail component's journa
5523
5743
  for (const line of res.lines) process.stdout.write(line + "\n");
5524
5744
  })
5525
5745
  );
5526
- var postmasterCmd = new Command17("postmaster").description("Manage the postmaster mailbox");
5746
+ var postmasterCmd = new Command18("postmaster").description("Manage the postmaster mailbox");
5527
5747
  postmasterCmd.addCommand(
5528
- new Command17("set-password").description("Rotate the postmaster password").argument("<serverId>", "Mail server ID").option("--password <password>", "New password (min 12 chars); prompted if omitted").action(
5748
+ new Command18("set-password").description("Rotate the postmaster password").argument("<serverId>", "Mail server ID").option("--password <password>", "New password (min 12 chars); prompted if omitted").action(
5529
5749
  guard2(async (serverId, opts) => {
5530
5750
  let password = opts.password;
5531
5751
  if (!password) {
@@ -5551,10 +5771,10 @@ postmasterCmd.addCommand(
5551
5771
  })
5552
5772
  )
5553
5773
  );
5554
- var mailCommand = new Command17("mail").description("Self-hosted mail server (iRedMail) setup and admin [self-host]").addCommand(stepsCmd).addCommand(statusCmd).addCommand(serversCmd).addCommand(scanCmd).addCommand(adoptCmd).addCommand(setupCmd).addCommand(cancelCmd).addCommand(dnsAckCmd).addCommand(ptrAckCmd).addCommand(resetCmd).addCommand(forgetCmd).addCommand(healthCmd).addCommand(logsCmd3).addCommand(postmasterCmd);
5774
+ var mailCommand = new Command18("mail").description("Self-hosted mail server (iRedMail) setup and admin [self-host]").addCommand(stepsCmd).addCommand(statusCmd).addCommand(serversCmd).addCommand(scanCmd).addCommand(adoptCmd).addCommand(setupCmd).addCommand(cancelCmd).addCommand(dnsAckCmd).addCommand(ptrAckCmd).addCommand(resetCmd).addCommand(forgetCmd).addCommand(healthCmd).addCommand(logsCmd3).addCommand(postmasterCmd);
5555
5775
 
5556
5776
  // src/commands/backup.ts
5557
- import { Command as Command18 } from "commander";
5777
+ import { Command as Command19 } from "commander";
5558
5778
  import ora7 from "ora";
5559
5779
  import { readFileSync as readFileSync6 } from "fs";
5560
5780
  async function guard3(fn) {
@@ -5639,7 +5859,7 @@ async function followStream(path2, label) {
5639
5859
  spinner2?.stop();
5640
5860
  return status;
5641
5861
  }
5642
- var policyCmd = new Command18("policy").description("Backup policies (schedules) for a project");
5862
+ var policyCmd = new Command19("policy").description("Backup policies (schedules) for a project");
5643
5863
  policyCmd.command("list").description("List backup policies for a project").requiredOption("--project <id>", "Project ID").action(
5644
5864
  (opts) => guard3(async () => {
5645
5865
  const { data } = await apiRequest(
@@ -5709,7 +5929,7 @@ policyCmd.command("run").description("Trigger a policy's backup now").argument("
5709
5929
  }
5710
5930
  })
5711
5931
  );
5712
- var runCmd = new Command18("run").description("Backup runs (executions)");
5932
+ var runCmd = new Command19("run").description("Backup runs (executions)");
5713
5933
  runCmd.command("list").description("List backup runs for a project").requiredOption("--project <id>", "Project ID").option("--service <id>", "Filter to a single service").option("--limit <n>", "Max rows (default 50)").action(
5714
5934
  (opts) => guard3(async () => {
5715
5935
  const qs = new URLSearchParams();
@@ -5785,7 +6005,7 @@ runCmd.command("restore").description("Prepare a restore from a run (stages it;
5785
6005
  }
5786
6006
  })
5787
6007
  );
5788
- var restoreCmd = new Command18("restore").description("Manage staged restores");
6008
+ var restoreCmd = new Command19("restore").description("Manage staged restores");
5789
6009
  restoreCmd.command("apply").description("Apply a staged restore (destructive)").argument("<restoreId>", "Restore ID from `backup run restore`").requiredOption("--token <token>", "Confirmation token from prepare").option("--follow", "Stream the restore to completion").action(
5790
6010
  (restoreId, opts) => guard3(async () => {
5791
6011
  await apiRequest(
@@ -5828,7 +6048,7 @@ restoreCmd.command("get").description("Show one restore (optionally stream it)")
5828
6048
  show(data);
5829
6049
  })
5830
6050
  );
5831
- var destinationCmd = new Command18("destination").description("Backup destinations (storage targets)");
6051
+ var destinationCmd = new Command19("destination").description("Backup destinations (storage targets)");
5832
6052
  destinationCmd.command("list").description("List backup destinations").action(
5833
6053
  () => guard3(async () => {
5834
6054
  const { data } = await apiRequest("/backup-destinations");
@@ -5900,21 +6120,21 @@ destinationCmd.command("preflight").description("Verify a destination (write + r
5900
6120
  }
5901
6121
  })
5902
6122
  );
5903
- var backupCommand = new Command18("backup").description("Manage backups: policies, runs, restores, destinations").addCommand(policyCmd).addCommand(runCmd).addCommand(restoreCmd).addCommand(destinationCmd);
6123
+ var backupCommand = new Command19("backup").description("Manage backups: policies, runs, restores, destinations").addCommand(policyCmd).addCommand(runCmd).addCommand(restoreCmd).addCommand(destinationCmd);
5904
6124
 
5905
6125
  // src/commands/token.ts
5906
- import { Command as Command19 } from "commander";
5907
- import chalk14 from "chalk";
6126
+ import { Command as Command20 } from "commander";
6127
+ import chalk15 from "chalk";
5908
6128
 
5909
6129
  // src/lib/cmd-helpers.ts
5910
- import chalk13 from "chalk";
6130
+ import chalk14 from "chalk";
5911
6131
  import ora8 from "ora";
5912
6132
  function spin3(text) {
5913
6133
  return isJsonMode() ? null : ora8(text).start();
5914
6134
  }
5915
6135
  function fail3(e) {
5916
6136
  if (e instanceof ApiError) {
5917
- err(` ${e.message}${e.status ? chalk13.dim(` (${e.status})`) : ""}`);
6137
+ err(` ${e.message}${e.status ? chalk14.dim(` (${e.status})`) : ""}`);
5918
6138
  } else {
5919
6139
  err(` ${e instanceof Error ? e.message : String(e)}`);
5920
6140
  }
@@ -5931,7 +6151,7 @@ function collectGrant(value, acc) {
5931
6151
  acc.push({ resourceType, resourceId, permissions });
5932
6152
  return acc;
5933
6153
  }
5934
- var listCmd5 = new Command19("list").description("List your personal access tokens").action(async () => {
6154
+ var listCmd5 = new Command20("list").description("List your personal access tokens").action(async () => {
5935
6155
  try {
5936
6156
  const res = await apiRequest("/tokens");
5937
6157
  const rows = res.data ?? [];
@@ -5956,7 +6176,7 @@ var listCmd5 = new Command19("list").description("List your personal access toke
5956
6176
  fail3(e);
5957
6177
  }
5958
6178
  });
5959
- var createCmd3 = new Command19("create").description("Mint a new personal access token (the secret is shown once)").argument("<name>", "Human-readable token name").option("--read-only", "Reject mutation methods (POST/PUT/PATCH/DELETE)", false).option("--expires <days>", "Expire after N days (1\u2013365); omit for non-expiring", (v) => parseInt(v, 10)).option(
6179
+ var createCmd3 = new Command20("create").description("Mint a new personal access token (the secret is shown once)").argument("<name>", "Human-readable token name").option("--read-only", "Reject mutation methods (POST/PUT/PATCH/DELETE)", false).option("--expires <days>", "Expire after N days (1\u2013365); omit for non-expiring", (v) => parseInt(v, 10)).option(
5960
6180
  "--grant <type:id:perms>",
5961
6181
  "Scope the token to a resource (repeatable), e.g. project:abc123:read,write",
5962
6182
  collectGrant,
@@ -5981,14 +6201,14 @@ var createCmd3 = new Command19("create").description("Mint a new personal access
5981
6201
  return;
5982
6202
  }
5983
6203
  info(" Copy this token now \u2014 it will not be shown again:");
5984
- process.stdout.write(chalk14.cyan(` ${res.data.token}
6204
+ process.stdout.write(chalk15.cyan(` ${res.data.token}
5985
6205
  `));
5986
6206
  } catch (e) {
5987
6207
  sp?.fail("Create failed");
5988
6208
  fail3(e);
5989
6209
  }
5990
6210
  });
5991
- var revokeCmd = new Command19("revoke").description("Revoke one of your tokens").argument("<id>", "Token ID").action(async (id) => {
6211
+ var revokeCmd = new Command20("revoke").description("Revoke one of your tokens").argument("<id>", "Token ID").action(async (id) => {
5992
6212
  const sp = spin3("Revoking token\u2026");
5993
6213
  try {
5994
6214
  await apiRequest(`/tokens/${encodeURIComponent(id)}`, { method: "DELETE" });
@@ -6000,11 +6220,11 @@ var revokeCmd = new Command19("revoke").description("Revoke one of your tokens")
6000
6220
  fail3(e);
6001
6221
  }
6002
6222
  });
6003
- var tokenCommand = new Command19("token").description("Manage personal access tokens").addCommand(listCmd5).addCommand(createCmd3).addCommand(revokeCmd);
6223
+ var tokenCommand = new Command20("token").description("Manage personal access tokens").addCommand(listCmd5).addCommand(createCmd3).addCommand(revokeCmd);
6004
6224
 
6005
6225
  // src/commands/api.ts
6006
- import { Command as Command20 } from "commander";
6007
- var apiCommand = new Command20("api").description("Make an authenticated request to any Openship API route (like `gh api`)").argument("<path>", "Path under /api, e.g. /projects or /deployments/<id>").option("-X, --method <method>", "HTTP method (defaults to GET, or POST when --data is given)").option("-d, --data <json>", "Request body as a JSON string").option("-q, --query <kv...>", "Query parameter key=value (repeatable)").action(async (path2, opts) => {
6226
+ import { Command as Command21 } from "commander";
6227
+ var apiCommand = new Command21("api").description("Make an authenticated request to any Openship API route (like `gh api`)").argument("<path>", "Path under /api, e.g. /projects or /deployments/<id>").option("-X, --method <method>", "HTTP method (defaults to GET, or POST when --data is given)").option("-d, --data <json>", "Request body as a JSON string").option("-q, --query <kv...>", "Query parameter key=value (repeatable)").action(async (path2, opts) => {
6008
6228
  const method = (opts.method || (opts.data ? "POST" : "GET")).toUpperCase();
6009
6229
  let url = path2.startsWith("/") ? path2 : `/${path2}`;
6010
6230
  if (opts.query?.length) {
@@ -6038,11 +6258,11 @@ var apiCommand = new Command20("api").description("Make an authenticated request
6038
6258
  });
6039
6259
 
6040
6260
  // src/commands/install.ts
6041
- import { Command as Command21 } from "commander";
6042
- import { chmodSync as chmodSync2, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
6043
- import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
6044
- import { homedir as homedir4 } from "os";
6045
- import { join as join8 } from "path";
6261
+ import { Command as Command22 } from "commander";
6262
+ import { chmodSync as chmodSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
6263
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
6264
+ import { homedir as homedir5 } from "os";
6265
+ import { join as join9 } from "path";
6046
6266
  import ora9 from "ora";
6047
6267
  function assetForPlatform() {
6048
6268
  const { platform, arch } = process;
@@ -6054,33 +6274,33 @@ function assetForPlatform() {
6054
6274
  throw new Error(`Unsupported platform: ${platform} (${arch})`);
6055
6275
  }
6056
6276
  function installDmg(dmg) {
6057
- const homeApps = join8(homedir4(), "Applications");
6277
+ const homeApps = join9(homedir5(), "Applications");
6058
6278
  let dest = homeApps;
6059
6279
  try {
6060
- mkdirSync6(homeApps, { recursive: true });
6280
+ mkdirSync7(homeApps, { recursive: true });
6061
6281
  } catch {
6062
6282
  dest = "/Applications";
6063
6283
  }
6064
- const attach = spawnSync3("hdiutil", ["attach", "-nobrowse", "-readonly", "-noverify", dmg], {
6284
+ const attach = spawnSync4("hdiutil", ["attach", "-nobrowse", "-readonly", "-noverify", dmg], {
6065
6285
  encoding: "utf8"
6066
6286
  });
6067
6287
  if (attach.status !== 0) throw new Error(`hdiutil attach failed: ${attach.stderr?.trim()}`);
6068
6288
  const mount = (attach.stdout.match(/\/Volumes\/[^\n]*/g) ?? []).pop()?.trim();
6069
6289
  if (!mount) throw new Error("Could not determine the mounted volume");
6070
- let target = join8(dest, "Openship.app");
6290
+ let target = join9(dest, "Openship.app");
6071
6291
  try {
6072
- const appInDmg = join8(mount, "Openship.app");
6073
- if (!existsSync8(appInDmg)) throw new Error("Openship.app not found in the disk image");
6074
- spawnSync3("rm", ["-rf", target]);
6075
- let copy = spawnSync3("ditto", [appInDmg, target], { encoding: "utf8" });
6292
+ const appInDmg = join9(mount, "Openship.app");
6293
+ if (!existsSync9(appInDmg)) throw new Error("Openship.app not found in the disk image");
6294
+ spawnSync4("rm", ["-rf", target]);
6295
+ let copy = spawnSync4("ditto", [appInDmg, target], { encoding: "utf8" });
6076
6296
  if (copy.status !== 0 && dest === homeApps) {
6077
- target = join8("/Applications", "Openship.app");
6078
- spawnSync3("rm", ["-rf", target]);
6079
- copy = spawnSync3("ditto", [appInDmg, target], { encoding: "utf8" });
6297
+ target = join9("/Applications", "Openship.app");
6298
+ spawnSync4("rm", ["-rf", target]);
6299
+ copy = spawnSync4("ditto", [appInDmg, target], { encoding: "utf8" });
6080
6300
  }
6081
6301
  if (copy.status !== 0) throw new Error(`ditto copy failed: ${copy.stderr?.trim()}`);
6082
6302
  } finally {
6083
- spawnSync3("hdiutil", ["detach", mount, "-quiet"]);
6303
+ spawnSync4("hdiutil", ["detach", mount, "-quiet"]);
6084
6304
  }
6085
6305
  return target;
6086
6306
  }
@@ -6089,10 +6309,10 @@ function installAppImage(appImage) {
6089
6309
  return appImage;
6090
6310
  }
6091
6311
  function installZip(zip) {
6092
- const localAppData = process.env.LOCALAPPDATA || join8(homedir4(), "AppData", "Local");
6093
- const target = join8(localAppData, "Programs", "Openship");
6094
- mkdirSync6(target, { recursive: true });
6095
- const expand = spawnSync3(
6312
+ const localAppData = process.env.LOCALAPPDATA || join9(homedir5(), "AppData", "Local");
6313
+ const target = join9(localAppData, "Programs", "Openship");
6314
+ mkdirSync7(target, { recursive: true });
6315
+ const expand = spawnSync4(
6096
6316
  "powershell",
6097
6317
  [
6098
6318
  "-NoProfile",
@@ -6107,7 +6327,7 @@ function installZip(zip) {
6107
6327
  }
6108
6328
  function launch(kind, target) {
6109
6329
  if (kind === "dmg") {
6110
- spawnSync3("open", [target]);
6330
+ spawnSync4("open", [target]);
6111
6331
  return;
6112
6332
  }
6113
6333
  if (kind === "appimage") {
@@ -6118,11 +6338,11 @@ function launch(kind, target) {
6118
6338
  child.unref();
6119
6339
  return;
6120
6340
  }
6121
- const exe = join8(target, "Openship.exe");
6122
- const path2 = existsSync8(exe) ? exe : target;
6123
- spawnSync3("cmd", ["/c", "start", "", path2]);
6341
+ const exe = join9(target, "Openship.exe");
6342
+ const path2 = existsSync9(exe) ? exe : target;
6343
+ spawnSync4("cmd", ["/c", "start", "", path2]);
6124
6344
  }
6125
- var installCommand = new Command21("install").description("Download and install the Openship desktop app for this OS").option("--version <tag>", "Release tag to install (e.g. v1.2.3)").option("--latest", "Install the latest release (default)").option("--force", "Re-download even if a verified copy is cached").option("--no-verify", "Skip SHA-256 verification (allowed when no sidecar exists)").option("--no-launch", "Install without launching the app").action(async (opts) => {
6345
+ var installCommand = new Command22("install").description("Download and install the Openship desktop app for this OS").option("--version <tag>", "Release tag to install (e.g. v1.2.3)").option("--latest", "Install the latest release (default)").option("--force", "Re-download even if a verified copy is cached").option("--no-verify", "Skip SHA-256 verification (allowed when no sidecar exists)").option("--no-launch", "Install without launching the app").action(async (opts) => {
6126
6346
  let asset;
6127
6347
  try {
6128
6348
  asset = assetForPlatform();
@@ -6145,13 +6365,13 @@ var installCommand = new Command21("install").description("Download and install
6145
6365
  process.exit(1);
6146
6366
  }
6147
6367
  const dir = releaseDir(tag);
6148
- const assetPath = join8(dir, asset.name);
6368
+ const assetPath = join9(dir, asset.name);
6149
6369
  const sidecarPath = `${assetPath}.sha256`;
6150
6370
  const assetUrl2 = `${RELEASES}/download/${tag}/${asset.name}`;
6151
6371
  const sidecarUrl = `${assetUrl2}.sha256`;
6152
6372
  let downloaded = false;
6153
6373
  let sha;
6154
- const cachedUsable = !opts.force && existsSync8(assetPath) && (existsSync8(sidecarPath) || opts.verify === false);
6374
+ const cachedUsable = !opts.force && existsSync9(assetPath) && (existsSync9(sidecarPath) || opts.verify === false);
6155
6375
  if (cachedUsable) {
6156
6376
  info(` Using cached ${asset.name} (${tag}).`);
6157
6377
  } else {
@@ -6173,7 +6393,7 @@ var installCommand = new Command21("install").description("Download and install
6173
6393
  const s2 = spin4("Verifying checksum\u2026");
6174
6394
  try {
6175
6395
  let sidecarBody;
6176
- if (existsSync8(sidecarPath) && !downloaded) {
6396
+ if (existsSync9(sidecarPath) && !downloaded) {
6177
6397
  sidecarBody = readFileSync7(sidecarPath, "utf8");
6178
6398
  } else {
6179
6399
  sidecarBody = await fetchSidecar(sidecarUrl);
@@ -6197,8 +6417,8 @@ var installCommand = new Command21("install").description("Download and install
6197
6417
  err(`Expected ${expected}, got ${actual}. The download may be corrupt or tampered with.`);
6198
6418
  process.exit(1);
6199
6419
  }
6200
- mkdirSync6(dir, { recursive: true });
6201
- writeFileSync6(sidecarPath, sidecarBody);
6420
+ mkdirSync7(dir, { recursive: true });
6421
+ writeFileSync7(sidecarPath, sidecarBody);
6202
6422
  s2?.succeed("Checksum verified");
6203
6423
  } catch (e) {
6204
6424
  s2?.fail("Verification failed");
@@ -6237,15 +6457,15 @@ var installCommand = new Command21("install").description("Download and install
6237
6457
  });
6238
6458
 
6239
6459
  // src/commands/update.ts
6240
- import { Command as Command22 } from "commander";
6241
- import { spawnSync as spawnSync4 } from "child_process";
6460
+ import { Command as Command23 } from "commander";
6461
+ import { spawnSync as spawnSync5 } from "child_process";
6242
6462
  function detectPackageManager2(override) {
6243
6463
  if (override === "bun" || override === "npm") return override;
6244
- const hasBun = spawnSync4("bun", ["--version"], { stdio: "ignore" }).status === 0;
6464
+ const hasBun = spawnSync5("bun", ["--version"], { stdio: "ignore" }).status === 0;
6245
6465
  return hasBun ? "bun" : "npm";
6246
6466
  }
6247
- var updateCommand = new Command22("update").description("Update the Openship CLI + bundled server to the latest release").option("--check", "Only report the current + latest version; don't install").option("--via <manager>", "Package manager to update with: bun | npm").action(async (opts) => {
6248
- const current = "0.1.10";
6467
+ var updateCommand = new Command23("update").description("Update the Openship CLI + bundled server to the latest release").option("--check", "Only report the current + latest version; don't install").option("--via <manager>", "Package manager to update with: bun | npm").action(async (opts) => {
6468
+ const current = "0.1.11";
6249
6469
  let latest;
6250
6470
  try {
6251
6471
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -6273,7 +6493,7 @@ var updateCommand = new Command22("update").description("Update the Openship CLI
6273
6493
  const ref = `openship@${latest}`;
6274
6494
  const argv = pm === "bun" ? ["add", "-g", ref] : ["install", "-g", ref];
6275
6495
  info(`Updating v${current} \u2192 v${latest} (${cliInstallCommand(pm, latest)})...`);
6276
- const res = spawnSync4(pm, argv, { stdio: "inherit" });
6496
+ const res = spawnSync5(pm, argv, { stdio: "inherit" });
6277
6497
  if (res.status !== 0) {
6278
6498
  err(`Update failed (${pm} exited ${res.status ?? "with a signal"}). Reinstall manually: ${cliInstallCommand(pm, latest)}`);
6279
6499
  process.exitCode = 1;
@@ -6287,18 +6507,18 @@ var updateCommand = new Command22("update").description("Update the Openship CLI
6287
6507
  });
6288
6508
 
6289
6509
  // src/commands/cache.ts
6290
- import { Command as Command23 } from "commander";
6291
- import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync8, rmSync as rmSync3, statSync } from "fs";
6292
- import { join as join9 } from "path";
6510
+ import { Command as Command24 } from "commander";
6511
+ import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync8, rmSync as rmSync4, statSync } from "fs";
6512
+ import { join as join10 } from "path";
6293
6513
  function listAssets() {
6294
- if (!existsSync9(RELEASES_DIR)) return [];
6514
+ if (!existsSync10(RELEASES_DIR)) return [];
6295
6515
  const out = [];
6296
6516
  for (const tag of readdirSync(RELEASES_DIR)) {
6297
6517
  const dir = releaseDir(tag);
6298
6518
  if (!statSync(dir).isDirectory()) continue;
6299
6519
  for (const name of readdirSync(dir)) {
6300
6520
  if (name.endsWith(".sha256")) continue;
6301
- const path2 = join9(dir, name);
6521
+ const path2 = join10(dir, name);
6302
6522
  const st = statSync(path2);
6303
6523
  if (!st.isFile()) continue;
6304
6524
  out.push({
@@ -6306,17 +6526,17 @@ function listAssets() {
6306
6526
  name,
6307
6527
  path: path2,
6308
6528
  size: st.size,
6309
- hasSidecar: existsSync9(`${path2}.sha256`)
6529
+ hasSidecar: existsSync10(`${path2}.sha256`)
6310
6530
  });
6311
6531
  }
6312
6532
  }
6313
6533
  return out;
6314
6534
  }
6315
- var pathCmd = new Command23("path").description("Print the cache directory path").action(() => {
6535
+ var pathCmd = new Command24("path").description("Print the cache directory path").action(() => {
6316
6536
  if (isJsonMode()) printJson({ path: CACHE_DIR });
6317
6537
  else process.stdout.write(CACHE_DIR + "\n");
6318
6538
  });
6319
- var listCmd6 = new Command23("list").alias("ls").description("List cached release assets").action(() => {
6539
+ var listCmd6 = new Command24("list").alias("ls").description("List cached release assets").action(() => {
6320
6540
  const assets = listAssets();
6321
6541
  printTable(
6322
6542
  assets.map((a) => ({
@@ -6328,7 +6548,7 @@ var listCmd6 = new Command23("list").alias("ls").description("List cached releas
6328
6548
  ["tag", "asset", "size", "sidecar"]
6329
6549
  );
6330
6550
  });
6331
- var verifyCmd2 = new Command23("verify").description("Re-hash cached assets and compare to their .sha256 sidecar").argument("[tag]", "Only verify assets under this release tag").action(async (tag) => {
6551
+ var verifyCmd2 = new Command24("verify").description("Re-hash cached assets and compare to their .sha256 sidecar").argument("[tag]", "Only verify assets under this release tag").action(async (tag) => {
6332
6552
  const assets = listAssets().filter((a) => !tag || a.tag === tag);
6333
6553
  const results = [];
6334
6554
  let bad = 0;
@@ -6351,30 +6571,31 @@ var verifyCmd2 = new Command23("verify").description("Re-hash cached assets and
6351
6571
  }
6352
6572
  if (bad > 0) process.exit(1);
6353
6573
  });
6354
- var cleanCmd = new Command23("clean").description("Delete cached release assets").argument("[tag]", "Only remove this release tag (default: all)").action((tag) => {
6574
+ var cleanCmd = new Command24("clean").description("Delete cached release assets").argument("[tag]", "Only remove this release tag (default: all)").action((tag) => {
6355
6575
  const target = tag ? releaseDir(tag) : RELEASES_DIR;
6356
- if (!existsSync9(target)) {
6576
+ if (!existsSync10(target)) {
6357
6577
  if (isJsonMode()) printJson({ removed: false, path: target });
6358
6578
  else info(` Nothing to clean (${target}).`);
6359
6579
  return;
6360
6580
  }
6361
- rmSync3(target, { recursive: true, force: true });
6581
+ rmSync4(target, { recursive: true, force: true });
6362
6582
  if (isJsonMode()) printJson({ removed: true, path: target });
6363
6583
  else ok(`
6364
6584
  Removed ${target}
6365
6585
  `);
6366
6586
  });
6367
- var cacheCommand = new Command23("cache").description("Manage the local download cache (list/verify/clean/path)").action(() => {
6587
+ var cacheCommand = new Command24("cache").description("Manage the local download cache (list/verify/clean/path)").action(() => {
6368
6588
  err("Specify a subcommand: path | list | verify | clean");
6369
6589
  process.exit(1);
6370
6590
  }).addCommand(pathCmd).addCommand(listCmd6).addCommand(verifyCmd2).addCommand(cleanCmd);
6371
6591
 
6372
6592
  // src/index.ts
6373
- var program = new Command24();
6374
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.1.10").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
6593
+ var program = new Command25();
6594
+ program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.1.11").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
6375
6595
  if (thisCommand.opts().json) setJsonMode(true);
6376
6596
  });
6377
6597
  program.addCommand(upCommand);
6598
+ program.addCommand(stopCommand);
6378
6599
  program.addCommand(installCommand);
6379
6600
  program.addCommand(updateCommand);
6380
6601
  program.addCommand(openCommand);