@launchsecure/launch-kit 0.0.34 → 0.0.35

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.
@@ -439,18 +439,18 @@ var init_mcp_client = __esm({
439
439
  var state_exports = {};
440
440
  __export(state_exports, {
441
441
  clearRoverState: () => clearRoverState,
442
- listRoverOrgs: () => listRoverOrgs,
442
+ listRoverIds: () => listRoverIds,
443
443
  loadRoverState: () => loadRoverState,
444
444
  saveRoverState: () => saveRoverState
445
445
  });
446
446
  function stateDir() {
447
447
  return (0, import_node_path2.join)((0, import_node_os.homedir)(), LAUNCHSECURE_DIR, "rover");
448
448
  }
449
- function statePath(orgSlug) {
450
- return (0, import_node_path2.join)(stateDir(), `${orgSlug}.json`);
449
+ function statePath(roverId) {
450
+ return (0, import_node_path2.join)(stateDir(), `${roverId}.json`);
451
451
  }
452
- function loadRoverState(orgSlug) {
453
- const p = statePath(orgSlug);
452
+ function loadRoverState(roverId) {
453
+ const p = statePath(roverId);
454
454
  if (!(0, import_node_fs2.existsSync)(p)) return null;
455
455
  try {
456
456
  const data = JSON.parse((0, import_node_fs2.readFileSync)(p, "utf-8"));
@@ -462,23 +462,23 @@ function loadRoverState(orgSlug) {
462
462
  }
463
463
  function saveRoverState(state) {
464
464
  (0, import_node_fs2.mkdirSync)(stateDir(), { recursive: true });
465
- const final = statePath(state.orgSlug);
465
+ const final = statePath(state.roverId);
466
466
  const tmp = `${final}.tmp`;
467
467
  (0, import_node_fs2.writeFileSync)(tmp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
468
468
  (0, import_node_fs2.renameSync)(tmp, final);
469
469
  }
470
- function clearRoverState(orgSlug) {
470
+ function clearRoverState(roverId) {
471
471
  try {
472
- (0, import_node_fs2.unlinkSync)(statePath(orgSlug));
472
+ (0, import_node_fs2.unlinkSync)(statePath(roverId));
473
473
  } catch {
474
474
  }
475
475
  }
476
- function listRoverOrgs() {
476
+ function listRoverIds() {
477
477
  const dir = stateDir();
478
478
  if (!(0, import_node_fs2.existsSync)(dir)) return [];
479
479
  try {
480
480
  const fs = require("node:fs");
481
- return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
481
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json") && !f.includes(".tunnel.")).map((f) => f.slice(0, -5));
482
482
  } catch {
483
483
  return [];
484
484
  }
@@ -514,7 +514,8 @@ async function registerOrRefresh(params) {
514
514
  secret,
515
515
  tunnelUrl: params.tunnelUrl,
516
516
  installedAt: params.existing?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
517
- daemonPort: params.daemonPort
517
+ daemonPort: params.daemonPort,
518
+ installPat: params.installPat
518
519
  };
519
520
  saveRoverState(state);
520
521
  return {
@@ -530,6 +531,139 @@ var init_registration = __esm({
530
531
  }
531
532
  });
532
533
 
534
+ // package.json
535
+ var require_package = __commonJS({
536
+ "package.json"(exports2, module2) {
537
+ module2.exports = {
538
+ name: "@launchsecure/launch-kit",
539
+ version: "0.0.35",
540
+ description: "LaunchSecure toolkit \u2014 launch-sequencer (pipeline runner + terminal bridge), launch-radar (feedback webhook receiver), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup). launch-pod is the container image these run inside.",
541
+ license: "MIT",
542
+ author: "LaunchSecure - AutomateWithUs",
543
+ homepage: "https://automatewith.us",
544
+ repository: {
545
+ type: "git",
546
+ url: "https://github.com/launchsecure/launchsecure-v2",
547
+ directory: "packages/cli"
548
+ },
549
+ keywords: [
550
+ "launchsecure",
551
+ "launch-kit",
552
+ "launch-chart",
553
+ "launch-pod",
554
+ "project-graph",
555
+ "launch-deck",
556
+ "launch-kit-beacon",
557
+ "feedback",
558
+ "bug-report",
559
+ "screenshot",
560
+ "web-component",
561
+ "launch-recall",
562
+ "playground",
563
+ "visual",
564
+ "mcp",
565
+ "pipeline",
566
+ "ai",
567
+ "cli",
568
+ "file-watcher",
569
+ "backup",
570
+ "recovery"
571
+ ],
572
+ exports: {
573
+ "./beacon": {
574
+ types: "./dist/beacon/types/index.d.ts",
575
+ import: "./dist/beacon/beacon.mjs",
576
+ require: "./dist/beacon/beacon.umd.js"
577
+ }
578
+ },
579
+ engines: {
580
+ node: ">=18.0.0"
581
+ },
582
+ publishConfig: {
583
+ access: "public"
584
+ },
585
+ bin: {
586
+ "launch-kit": "./dist/server/init-entry.js",
587
+ "launch-sequencer": "./dist/server/cli.js",
588
+ "launch-radar": "./dist/server/launch-radar-entry.js",
589
+ "launch-chart": "./dist/server/graph-mcp-entry.js",
590
+ "launch-deck": "./dist/server/deck-mcp-entry.js",
591
+ "launch-recall": "./dist/server/recall-entry.js",
592
+ "launch-orbit": "./dist/server/orbit-entry.js",
593
+ "launch-course": "./dist/server/course-entry.js",
594
+ "launch-beacon": "./dist/server/beacon-monitor-entry.js",
595
+ "launch-rover": "./dist/server/rover-entry.js",
596
+ "launch-bot": "./dist/server/launch-bot-entry.js"
597
+ },
598
+ scripts: {
599
+ build: "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
600
+ "build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
601
+ "test:beacon": "vitest run --config vite.beacon.config.ts",
602
+ "test:radar": "vitest run --config vite.radar.config.ts",
603
+ "test:chart": "vitest run --config vite.chart.test.config.ts",
604
+ "build:deck-client": "vite build --config vite.deck.config.ts",
605
+ "build:council-client": "vite build --config vite.council.config.ts",
606
+ "build:client": "vite build",
607
+ "build:chart-client": "vite build --config vite.chart.config.ts",
608
+ "build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts src/server/init-entry.ts src/server/orbit-entry.ts src/server/course-entry.ts src/server/beacon-monitor-entry.ts src/server/parse-worker-entry.ts src/server/radar-teardown-entry.ts src/server/radar-docker-init-entry.ts src/server/launch-radar-entry.ts src/server/rover-entry.ts src/server/launch-bot-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native --external:pgsql-parser --external:libpg-query && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
609
+ "dev:client": "vite",
610
+ "dev:deck-serve": "cd ../.. && tsx watch packages/cli/src/server/deck-mcp-entry.ts serve",
611
+ "dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
612
+ "dev:server": "pnpm build:server && node dist/server/cli.js",
613
+ dev: 'pnpm build:server && concurrently -k -n client,server -c cyan,magenta "vite" "node dist/server/cli.js"',
614
+ prepublishOnly: "pnpm build"
615
+ },
616
+ files: [
617
+ "dist",
618
+ "prompts",
619
+ "scaffolds"
620
+ ],
621
+ dependencies: {
622
+ "cacheable-lookup": "^7.0.0",
623
+ cloudflared: "^0.7.1",
624
+ "html-to-image": "^1.11.13",
625
+ "node-pty": "^1.2.0-beta.12",
626
+ pg: "^8.13.0",
627
+ "pgsql-parser": "^17.9.15",
628
+ "tree-sitter": "^0.21.1",
629
+ "tree-sitter-typescript": "^0.23.2",
630
+ typescript: "^5.5.0",
631
+ undici: "^6.25.0",
632
+ "web-tree-sitter": "^0.24.7",
633
+ ws: "^8.18.0"
634
+ },
635
+ devDependencies: {
636
+ "@launchsecure/claude-code-web": "workspace:*",
637
+ "@launchsecure/ui": "workspace:*",
638
+ "@types/node": "^20.0.0",
639
+ "@types/pg": "^8.11.10",
640
+ "@types/react": "^18.3.12",
641
+ "@types/react-dom": "^18.3.1",
642
+ "@types/ws": "^8.5.10",
643
+ "@vitejs/plugin-react": "^4.3.4",
644
+ "@xterm/addon-fit": "^0.10.0",
645
+ "@xterm/addon-web-links": "^0.11.0",
646
+ "@xterm/xterm": "^5.5.0",
647
+ "@xyflow/react": "^12.10.2",
648
+ autoprefixer: "^10.4.27",
649
+ concurrently: "^9.2.1",
650
+ esbuild: "^0.28.0",
651
+ "happy-dom": "^20.8.8",
652
+ marked: "^15.0.0",
653
+ mermaid: "^11.4.0",
654
+ postcss: "^8.5.8",
655
+ react: "^18.3.1",
656
+ "react-dom": "^18.3.1",
657
+ "react-force-graph-2d": "^1.25.5",
658
+ "react-router-dom": "^6.28.0",
659
+ tailwindcss: "^3.4.19",
660
+ vite: "^5.4.11",
661
+ vitest: "^1.6.0"
662
+ }
663
+ };
664
+ }
665
+ });
666
+
533
667
  // ../../node_modules/.pnpm/cacheable-lookup@7.0.0/node_modules/cacheable-lookup/source/index.js
534
668
  var import_node_dns, import_node_util, import_node_os2, AsyncResolver, kCacheableLookupCreateConnection, kCacheableLookupInstance, kExpires, supportsALL, verifyAgent, map4to6, getIfaceInfo, isIterable, ignoreNoResultErrors, ttl, all, all4, all6, CacheableLookup;
535
669
  var init_source = __esm({
@@ -11455,7 +11589,7 @@ var require_mock_interceptor = __commonJS({
11455
11589
  var require_mock_client = __commonJS({
11456
11590
  "../../node_modules/.pnpm/undici@6.25.0/node_modules/undici/lib/mock/mock-client.js"(exports2, module2) {
11457
11591
  "use strict";
11458
- var { promisify: promisify2 } = require("node:util");
11592
+ var { promisify: promisify3 } = require("node:util");
11459
11593
  var Client = require_client();
11460
11594
  var { buildMockDispatch } = require_mock_utils();
11461
11595
  var {
@@ -11495,7 +11629,7 @@ var require_mock_client = __commonJS({
11495
11629
  return new MockInterceptor(opts, this[kDispatches]);
11496
11630
  }
11497
11631
  async [kClose]() {
11498
- await promisify2(this[kOriginalClose])();
11632
+ await promisify3(this[kOriginalClose])();
11499
11633
  this[kConnected] = 0;
11500
11634
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
11501
11635
  }
@@ -11508,7 +11642,7 @@ var require_mock_client = __commonJS({
11508
11642
  var require_mock_pool = __commonJS({
11509
11643
  "../../node_modules/.pnpm/undici@6.25.0/node_modules/undici/lib/mock/mock-pool.js"(exports2, module2) {
11510
11644
  "use strict";
11511
- var { promisify: promisify2 } = require("node:util");
11645
+ var { promisify: promisify3 } = require("node:util");
11512
11646
  var Pool = require_pool();
11513
11647
  var { buildMockDispatch } = require_mock_utils();
11514
11648
  var {
@@ -11548,7 +11682,7 @@ var require_mock_pool = __commonJS({
11548
11682
  return new MockInterceptor(opts, this[kDispatches]);
11549
11683
  }
11550
11684
  async [kClose]() {
11551
- await promisify2(this[kOriginalClose])();
11685
+ await promisify3(this[kOriginalClose])();
11552
11686
  this[kConnected] = 0;
11553
11687
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
11554
11688
  }
@@ -19530,6 +19664,259 @@ var init_hmac = __esm({
19530
19664
  }
19531
19665
  });
19532
19666
 
19667
+ // src/server/rover/pods.ts
19668
+ async function podsUp(state, body) {
19669
+ if (!state.installPat) {
19670
+ throw new PodError(
19671
+ "rover state is missing the install PAT \u2014 re-run `launch-rover setup` to refresh.",
19672
+ 500
19673
+ );
19674
+ }
19675
+ if (!body || typeof body !== "object") {
19676
+ throw new PodError("body must be a JSON object");
19677
+ }
19678
+ if (typeof body.projectSlug !== "string" || !DNS_SLUG_RE.test(body.projectSlug)) {
19679
+ throw new PodError("projectSlug must be a DNS-safe string (a-z, 0-9, -)");
19680
+ }
19681
+ if (typeof body.claudeCredentialsB64 !== "string" || body.claudeCredentialsB64.length < 16) {
19682
+ throw new PodError("claudeCredentialsB64 is required (base64-encoded Claude credentials JSON)");
19683
+ }
19684
+ const image = body.image ?? DEFAULT_IMAGE;
19685
+ const containerName = POD_NAME_PREFIX + body.projectSlug;
19686
+ const workspaceVol = `${VOLUME_PREFIX}${body.projectSlug}-workspace`;
19687
+ const claudeVol = `${VOLUME_PREFIX}${body.projectSlug}-claude`;
19688
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
19689
+ await stopAndRemove(containerName);
19690
+ const dockerConfigDir = body.skipPull ? null : setupDockerConfig();
19691
+ try {
19692
+ if (dockerConfigDir) {
19693
+ await dockerRun(["pull", image], { DOCKER_CONFIG: dockerConfigDir });
19694
+ }
19695
+ const runArgs = [
19696
+ "run",
19697
+ "-d",
19698
+ "--name",
19699
+ containerName,
19700
+ "--restart",
19701
+ "unless-stopped",
19702
+ // Lets the container reach the host's loopback. Built-in on Docker
19703
+ // Desktop (macOS/Windows); explicit on Linux. Required when LS runs
19704
+ // on the host as a dev server (LS_SERVER_URL=http://localhost:…).
19705
+ "--add-host",
19706
+ "host.docker.internal:host-gateway",
19707
+ "--label",
19708
+ `${MANAGED_LABEL}=true`,
19709
+ "--label",
19710
+ `launch-rover.projectSlug=${body.projectSlug}`,
19711
+ "--label",
19712
+ `launch-rover.createdAt=${createdAt}`,
19713
+ "-v",
19714
+ `${workspaceVol}:/workspace`,
19715
+ "-v",
19716
+ `${claudeVol}:/home/launchpod/.claude`
19717
+ ];
19718
+ if (body.podId) runArgs.push("--label", `launch-rover.podId=${body.podId}`);
19719
+ if (body.name) runArgs.push("--label", `launch-rover.name=${body.name}`);
19720
+ const envVars = {
19721
+ CLAUDE_CREDENTIALS_B64: body.claudeCredentialsB64,
19722
+ LS_PAT: state.installPat,
19723
+ LS_ORG_SLUG: state.orgSlug,
19724
+ LS_PROJECT_SLUG: body.projectSlug,
19725
+ LS_SERVER_URL: rewriteLocalhostForContainer(state.serverUrl)
19726
+ };
19727
+ if (body.services && Array.isArray(body.services)) {
19728
+ envVars.LAUNCHKIT_SERVICES = JSON.stringify(body.services);
19729
+ }
19730
+ if (body.cfBaseDomain) envVars.LAUNCHKIT_CF_BASE_DOMAIN = body.cfBaseDomain;
19731
+ if (body.env && typeof body.env === "object") {
19732
+ for (const [k, v] of Object.entries(body.env)) {
19733
+ if (typeof v === "string") envVars[k] = v;
19734
+ }
19735
+ }
19736
+ for (const [k, v] of Object.entries(envVars)) {
19737
+ runArgs.push("-e", `${k}=${v}`);
19738
+ }
19739
+ runArgs.push(image);
19740
+ const containerId = (await dockerRun(runArgs)).stdout.trim();
19741
+ return { podId: body.podId ?? null, projectSlug: body.projectSlug, containerId, image, createdAt };
19742
+ } finally {
19743
+ if (dockerConfigDir) (0, import_node_fs4.rmSync)(dockerConfigDir, { recursive: true, force: true });
19744
+ }
19745
+ }
19746
+ async function podsDown(_state, body) {
19747
+ if (!body || typeof body !== "object") {
19748
+ throw new PodError("body must be a JSON object");
19749
+ }
19750
+ if (typeof body.projectSlug !== "string" || !DNS_SLUG_RE.test(body.projectSlug)) {
19751
+ throw new PodError("projectSlug must be a DNS-safe string (a-z, 0-9, -)");
19752
+ }
19753
+ const mode = body.mode ?? "stop";
19754
+ if (mode !== "stop" && mode !== "remove" && mode !== "purge") {
19755
+ throw new PodError(`mode must be "stop" | "remove" | "purge" (got "${mode}")`);
19756
+ }
19757
+ const containerName = POD_NAME_PREFIX + body.projectSlug;
19758
+ const stopped = await dockerStopIfExists(containerName);
19759
+ let removed = false;
19760
+ let volumesRemoved = false;
19761
+ if (mode === "remove" || mode === "purge") {
19762
+ removed = await dockerRmIfExists(containerName);
19763
+ }
19764
+ if (mode === "purge") {
19765
+ const workspaceVol = `${VOLUME_PREFIX}${body.projectSlug}-workspace`;
19766
+ const claudeVol = `${VOLUME_PREFIX}${body.projectSlug}-claude`;
19767
+ const a = await dockerVolumeRmIfExists(workspaceVol);
19768
+ const b = await dockerVolumeRmIfExists(claudeVol);
19769
+ volumesRemoved = a || b;
19770
+ }
19771
+ return { projectSlug: body.projectSlug, mode, stopped, removed, volumesRemoved };
19772
+ }
19773
+ async function podsList(_state) {
19774
+ const { stdout: stdout2 } = await dockerRun([
19775
+ "ps",
19776
+ "-a",
19777
+ "--filter",
19778
+ `label=${MANAGED_LABEL}=true`,
19779
+ "--format",
19780
+ "{{json .}}"
19781
+ ]);
19782
+ const pods = [];
19783
+ for (const line of stdout2.split("\n")) {
19784
+ const trimmed = line.trim();
19785
+ if (!trimmed) continue;
19786
+ let row;
19787
+ try {
19788
+ row = JSON.parse(trimmed);
19789
+ } catch {
19790
+ continue;
19791
+ }
19792
+ pods.push(parsePsRow(row));
19793
+ }
19794
+ return { pods };
19795
+ }
19796
+ async function stopAndRemove(containerName) {
19797
+ await dockerStopIfExists(containerName);
19798
+ await dockerRmIfExists(containerName);
19799
+ }
19800
+ async function dockerStopIfExists(containerName) {
19801
+ try {
19802
+ await dockerRun(["stop", containerName]);
19803
+ return true;
19804
+ } catch (err) {
19805
+ if (isNoSuchContainer(err)) return false;
19806
+ throw err;
19807
+ }
19808
+ }
19809
+ async function dockerRmIfExists(containerName) {
19810
+ try {
19811
+ await dockerRun(["rm", containerName]);
19812
+ return true;
19813
+ } catch (err) {
19814
+ if (isNoSuchContainer(err)) return false;
19815
+ throw err;
19816
+ }
19817
+ }
19818
+ async function dockerVolumeRmIfExists(volumeName) {
19819
+ try {
19820
+ await dockerRun(["volume", "rm", volumeName]);
19821
+ return true;
19822
+ } catch (err) {
19823
+ if (isNoSuchVolume(err)) return false;
19824
+ throw err;
19825
+ }
19826
+ }
19827
+ function isNoSuchContainer(err) {
19828
+ const e = err;
19829
+ const text = `${e?.stderr ?? ""}${e?.message ?? ""}`.toLowerCase();
19830
+ return text.includes("no such container");
19831
+ }
19832
+ function isNoSuchVolume(err) {
19833
+ const e = err;
19834
+ const text = `${e?.stderr ?? ""}${e?.message ?? ""}`.toLowerCase();
19835
+ return text.includes("no such volume") || text.includes("get no such volume");
19836
+ }
19837
+ async function dockerRun(args, envOverlay = {}) {
19838
+ try {
19839
+ const { stdout: stdout2, stderr } = await execFileAsync("docker", args, {
19840
+ env: { ...process.env, ...envOverlay },
19841
+ maxBuffer: 32 * 1024 * 1024
19842
+ });
19843
+ return { stdout: stdout2, stderr };
19844
+ } catch (err) {
19845
+ const e = err;
19846
+ throw new PodError(
19847
+ `docker ${args[0]} failed: ${(e.stderr || e.message || "").trim()}`,
19848
+ 502
19849
+ );
19850
+ }
19851
+ }
19852
+ function setupDockerConfig() {
19853
+ const token = process.env.GHCR_PULL_TOKEN;
19854
+ if (!token) {
19855
+ throw new PodError(
19856
+ "GHCR_PULL_TOKEN is not set in the rover's environment. Add it to .env.local and restart the rover daemon. (Will be replaced by per-call token from LS later.)",
19857
+ 500
19858
+ );
19859
+ }
19860
+ const dir = (0, import_node_fs4.mkdtempSync)((0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "launch-rover-docker-"));
19861
+ const auth = Buffer.from(`${GHCR_USERNAME}:${token}`, "utf-8").toString("base64");
19862
+ const config = { auths: { [GHCR_REGISTRY]: { auth } } };
19863
+ (0, import_node_fs4.writeFileSync)((0, import_node_path3.join)(dir, "config.json"), JSON.stringify(config), { mode: 384 });
19864
+ return dir;
19865
+ }
19866
+ function parsePsRow(row) {
19867
+ const labels = parseLabels(row.Labels);
19868
+ return {
19869
+ podId: labels["launch-rover.podId"] ?? null,
19870
+ projectSlug: labels["launch-rover.projectSlug"] ?? null,
19871
+ name: labels["launch-rover.name"] ?? null,
19872
+ containerName: row.Names ?? "",
19873
+ containerId: row.ID ?? "",
19874
+ image: row.Image ?? "",
19875
+ state: row.State ?? "",
19876
+ status: row.Status ?? "",
19877
+ createdAt: labels["launch-rover.createdAt"] ?? row.CreatedAt ?? null
19878
+ };
19879
+ }
19880
+ function rewriteLocalhostForContainer(url) {
19881
+ return url.replace(/^(https?:\/\/)(localhost|127\.0\.0\.1)(?=[:\/]|$)/i, "$1host.docker.internal");
19882
+ }
19883
+ function parseLabels(raw) {
19884
+ const out = {};
19885
+ if (!raw) return out;
19886
+ for (const pair of raw.split(",")) {
19887
+ const eq = pair.indexOf("=");
19888
+ if (eq <= 0) continue;
19889
+ out[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
19890
+ }
19891
+ return out;
19892
+ }
19893
+ var import_node_child_process2, import_node_fs4, import_node_os3, import_node_path3, import_node_util2, execFileAsync, DEFAULT_IMAGE, GHCR_REGISTRY, GHCR_USERNAME, MANAGED_LABEL, POD_NAME_PREFIX, VOLUME_PREFIX, DNS_SLUG_RE, PodError;
19894
+ var init_pods = __esm({
19895
+ "src/server/rover/pods.ts"() {
19896
+ "use strict";
19897
+ import_node_child_process2 = require("node:child_process");
19898
+ import_node_fs4 = require("node:fs");
19899
+ import_node_os3 = require("node:os");
19900
+ import_node_path3 = require("node:path");
19901
+ import_node_util2 = require("node:util");
19902
+ execFileAsync = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
19903
+ DEFAULT_IMAGE = "ghcr.io/launchsecure/launch-pod:latest";
19904
+ GHCR_REGISTRY = "ghcr.io";
19905
+ GHCR_USERNAME = process.env.GHCR_PULL_USERNAME ?? "ghcr-pull";
19906
+ MANAGED_LABEL = "launch-rover.managed";
19907
+ POD_NAME_PREFIX = "launch-pod-";
19908
+ VOLUME_PREFIX = "lp-";
19909
+ DNS_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
19910
+ PodError = class extends Error {
19911
+ constructor(message, httpStatus = 400) {
19912
+ super(message);
19913
+ this.name = "PodError";
19914
+ this.httpStatus = httpStatus;
19915
+ }
19916
+ };
19917
+ }
19918
+ });
19919
+
19533
19920
  // src/server/rover/daemon.ts
19534
19921
  var daemon_exports = {};
19535
19922
  __export(daemon_exports, {
@@ -19537,33 +19924,33 @@ __export(daemon_exports, {
19537
19924
  startDaemon: () => startDaemon
19538
19925
  });
19539
19926
  function lockPath() {
19540
- return (0, import_node_path3.join)((0, import_node_os3.homedir)(), LAUNCHSECURE_DIR, ROVER_LOCK_FILENAME);
19927
+ return (0, import_node_path4.join)((0, import_node_os4.homedir)(), LAUNCHSECURE_DIR, ROVER_LOCK_FILENAME);
19541
19928
  }
19542
19929
  function readRoverLock() {
19543
19930
  const p = lockPath();
19544
- if (!(0, import_node_fs4.existsSync)(p)) return null;
19931
+ if (!(0, import_node_fs5.existsSync)(p)) return null;
19545
19932
  try {
19546
- return JSON.parse((0, import_node_fs4.readFileSync)(p, "utf-8"));
19933
+ return JSON.parse((0, import_node_fs5.readFileSync)(p, "utf-8"));
19547
19934
  } catch {
19548
19935
  return null;
19549
19936
  }
19550
19937
  }
19551
19938
  function writeLock(lock) {
19552
- const dir = (0, import_node_path3.join)((0, import_node_os3.homedir)(), LAUNCHSECURE_DIR);
19553
- (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
19554
- (0, import_node_fs4.writeFileSync)(lockPath(), JSON.stringify(lock, null, 2) + "\n", "utf-8");
19939
+ const dir = (0, import_node_path4.join)((0, import_node_os4.homedir)(), LAUNCHSECURE_DIR);
19940
+ (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
19941
+ (0, import_node_fs5.writeFileSync)(lockPath(), JSON.stringify(lock, null, 2) + "\n", "utf-8");
19555
19942
  }
19556
19943
  function clearLock() {
19557
19944
  try {
19558
- (0, import_node_fs4.unlinkSync)(lockPath());
19945
+ (0, import_node_fs5.unlinkSync)(lockPath());
19559
19946
  } catch {
19560
19947
  }
19561
19948
  }
19562
19949
  async function startDaemon(opts) {
19563
- const state = loadRoverState(opts.orgSlug);
19950
+ const state = loadRoverState(opts.roverId);
19564
19951
  if (!state) {
19565
19952
  throw new Error(
19566
- `No rover state for org "${opts.orgSlug}". Run \`launch-rover setup\` first.`
19953
+ `No rover state for id "${opts.roverId}". Run \`launch-rover setup\` first.`
19567
19954
  );
19568
19955
  }
19569
19956
  const port = opts.port ?? state.daemonPort;
@@ -19588,7 +19975,8 @@ async function startDaemon(opts) {
19588
19975
  cwd: process.cwd(),
19589
19976
  url: state.tunnelUrl,
19590
19977
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
19591
- orgSlug: opts.orgSlug
19978
+ roverId: state.roverId,
19979
+ orgSlug: state.orgSlug
19592
19980
  };
19593
19981
  writeLock(lock);
19594
19982
  console.log(`[rover] listening on 0.0.0.0:${port} (tunnel: ${state.tunnelUrl})`);
@@ -19633,7 +20021,33 @@ async function handleRequest(req, res, state) {
19633
20021
  console.warn(`[rover] ${method} ${path} \u2192 401 (${failure})`);
19634
20022
  return json(res, 401, { error: "invalid_signature" });
19635
20023
  }
19636
- return json(res, 501, { error: "not_implemented", path });
20024
+ let parsed = {};
20025
+ if (rawBody.length > 0) {
20026
+ try {
20027
+ parsed = JSON.parse(rawBody);
20028
+ } catch {
20029
+ return json(res, 400, { error: "malformed_json" });
20030
+ }
20031
+ }
20032
+ try {
20033
+ if (path === "/pods/up") {
20034
+ const result2 = await podsUp(state, parsed);
20035
+ return json(res, 200, result2);
20036
+ }
20037
+ if (path === "/pods/down") {
20038
+ const result2 = await podsDown(state, parsed);
20039
+ return json(res, 200, result2);
20040
+ }
20041
+ const result = await podsList(state);
20042
+ return json(res, 200, result);
20043
+ } catch (err) {
20044
+ if (err instanceof PodError) {
20045
+ console.warn(`[rover] ${method} ${path} \u2192 ${err.httpStatus} (${err.message})`);
20046
+ return json(res, err.httpStatus, { error: err.message });
20047
+ }
20048
+ console.error(`[rover] ${method} ${path} crashed:`, err);
20049
+ return json(res, 500, { error: err instanceof Error ? err.message : "internal_error" });
20050
+ }
19637
20051
  }
19638
20052
  return json(res, 404, { error: "not_found", path });
19639
20053
  }
@@ -19651,7 +20065,7 @@ function readBody(req) {
19651
20065
  });
19652
20066
  }
19653
20067
  async function sendHeartbeat(state) {
19654
- const payload = JSON.stringify({ organizationId: state.organizationId });
20068
+ const payload = JSON.stringify({ roverId: state.roverId });
19655
20069
  const { header } = buildSignatureHeader(state.secret, payload);
19656
20070
  const url = new URL("/api/rover/heartbeat", state.serverUrl);
19657
20071
  await postJson(url, payload, { "X-LS-Signature": header });
@@ -19690,18 +20104,19 @@ function postJson(url, body, extra) {
19690
20104
  req.end();
19691
20105
  });
19692
20106
  }
19693
- var import_node_fs4, import_node_http2, import_node_http3, import_node_https2, import_node_os3, import_node_path3, ROVER_LOCK_FILENAME;
20107
+ var import_node_fs5, import_node_http2, import_node_http3, import_node_https2, import_node_os4, import_node_path4, ROVER_LOCK_FILENAME;
19694
20108
  var init_daemon = __esm({
19695
20109
  "src/server/rover/daemon.ts"() {
19696
20110
  "use strict";
19697
- import_node_fs4 = require("node:fs");
20111
+ import_node_fs5 = require("node:fs");
19698
20112
  import_node_http2 = require("node:http");
19699
20113
  import_node_http3 = require("node:http");
19700
20114
  import_node_https2 = require("node:https");
19701
- import_node_os3 = require("node:os");
19702
- import_node_path3 = require("node:path");
20115
+ import_node_os4 = require("node:os");
20116
+ import_node_path4 = require("node:path");
19703
20117
  init_launch_kit_paths();
19704
20118
  init_hmac();
20119
+ init_pods();
19705
20120
  init_state();
19706
20121
  ROVER_LOCK_FILENAME = "launch-rover.lock";
19707
20122
  }
@@ -19748,7 +20163,7 @@ function printUsage() {
19748
20163
  "Usage: launch-rover setup --pat=<PAT> --org=<orgSlug> [options]",
19749
20164
  "",
19750
20165
  "Required:",
19751
- " --pat=<ls_pat_...> Setup PAT issued by LS (mcp:rover:setup scope)",
20166
+ " --pat=<ls_pat_...> Setup PAT issued by LS (rover:setup scope)",
19752
20167
  " --org=<slug> Organization slug the rover belongs to",
19753
20168
  "",
19754
20169
  "Optional:",
@@ -19773,26 +20188,33 @@ async function runSetup(argv) {
19773
20188
  pat: args.pat,
19774
20189
  orgSlug: args.orgSlug
19775
20190
  });
19776
- console.log("[rover] fetching CF config from LS\u2026");
20191
+ console.log("[rover] fetching rover config from LS\u2026");
19777
20192
  const lsConfig = await mcp.call("rover_get_config", {});
19778
- if (!lsConfig.cfApiToken || !lsConfig.cfAccountId || !lsConfig.cfZoneId) {
20193
+ if (!lsConfig.id) {
19779
20194
  throw new Error(
19780
- "LS rover config is missing Cloudflare credentials. Edit the rover in LS Org Settings \u2192 Rover and fill in the CF fields."
20195
+ "LS rover config did not return an id. PAT may not be bound to a specific rover \u2014 create the rover in LS Organization \u2192 Rovers and re-issue the install command."
19781
20196
  );
19782
20197
  }
19783
- console.log(`[rover] LS rover "${lsConfig.name}" (status: ${lsConfig.status})`);
19784
- const zoneName = await fetchZoneName(lsConfig.cfApiToken, lsConfig.cfZoneId);
19785
- console.log(`[rover] resolved CF zone: ${zoneName}`);
19786
- const stateDir2 = (0, import_node_path4.join)((0, import_node_os4.homedir)(), LAUNCHSECURE_DIR, "rover");
19787
- (0, import_node_fs5.mkdirSync)(stateDir2, { recursive: true });
19788
- const tunnelStateFile = (0, import_node_path4.join)(stateDir2, `${args.orgSlug}.tunnel.json`);
19789
- const subdomain = `rover-${args.orgSlug.replace(/[^a-z0-9-]/gi, "-").toLowerCase().slice(0, 50)}`;
19790
- const tunnelName = `launch-rover-${args.orgSlug}`;
19791
- console.log(`[rover] provisioning CF tunnel ${tunnelName} \u2192 ${subdomain}.${zoneName}`);
20198
+ if (!lsConfig.cfApiToken || !lsConfig.cfDomainName) {
20199
+ throw new Error(
20200
+ "LS rover config is missing Cloudflare credentials. Connect a Cloudflare integration and set the rover's domain in LS Organization \u2192 Rovers."
20201
+ );
20202
+ }
20203
+ console.log(`[rover] LS rover "${lsConfig.name}" (id: ${lsConfig.id}, status: ${lsConfig.status})`);
20204
+ const roverId = lsConfig.id;
20205
+ const zone = await resolveZone(lsConfig.cfApiToken, lsConfig.cfDomainName);
20206
+ console.log(`[rover] resolved CF zone: ${zone.name} (account ${zone.accountId})`);
20207
+ const stateDir2 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), LAUNCHSECURE_DIR, "rover");
20208
+ (0, import_node_fs6.mkdirSync)(stateDir2, { recursive: true });
20209
+ const tunnelStateFile = (0, import_node_path5.join)(stateDir2, `${roverId}.tunnel.json`);
20210
+ const idSuffix = roverId.replace(/[^a-z0-9]/gi, "").toLowerCase().slice(-8);
20211
+ const subdomain = `rover-${args.orgSlug.replace(/[^a-z0-9-]/gi, "-").toLowerCase().slice(0, 30)}-${idSuffix}`;
20212
+ const tunnelName = `launch-rover-${roverId}`;
20213
+ console.log(`[rover] provisioning CF tunnel ${tunnelName} \u2192 ${subdomain}.${zone.name}`);
19792
20214
  const ingress = await provisionIngress({
19793
20215
  apiToken: lsConfig.cfApiToken,
19794
- accountId: lsConfig.cfAccountId,
19795
- zone: { id: lsConfig.cfZoneId, name: zoneName },
20216
+ accountId: zone.accountId,
20217
+ zone: { id: zone.id, name: zone.name },
19796
20218
  tunnelName,
19797
20219
  services: [{ name: subdomain, port: args.port }],
19798
20220
  stateFile: tunnelStateFile
@@ -19803,18 +20225,19 @@ async function runSetup(argv) {
19803
20225
  }
19804
20226
  const tunnelUrl = `https://${hostname}`;
19805
20227
  console.log(`[rover] tunnel public URL: ${tunnelUrl}`);
19806
- const existing = loadRoverState(args.orgSlug);
20228
+ const existing = loadRoverState(roverId);
19807
20229
  const outcome = await registerOrRefresh({
19808
20230
  mcp,
19809
20231
  orgSlug: args.orgSlug,
19810
20232
  serverUrl: args.serverUrl,
19811
20233
  tunnelUrl,
19812
20234
  daemonPort: args.port,
20235
+ installPat: args.pat,
19813
20236
  hostInfo: {
19814
- platform: (0, import_node_os4.platform)(),
19815
- hostname: args.name ?? (0, import_node_os4.hostname)(),
20237
+ platform: (0, import_node_os5.platform)(),
20238
+ hostname: args.name ?? (0, import_node_os5.hostname)(),
19816
20239
  dockerVersion: dockerInfo.version ?? void 0,
19817
- kitVersion: process.env.npm_package_version
20240
+ kitVersion: KIT_VERSION
19818
20241
  },
19819
20242
  existing
19820
20243
  });
@@ -19825,30 +20248,39 @@ async function runSetup(argv) {
19825
20248
  console.log("");
19826
20249
  if (outcome.approvalStatus === "pending_approval") {
19827
20250
  console.log("\u2713 Rover is live and registered, awaiting admin approval in LS.");
19828
- console.log(` ${args.serverUrl}/${args.orgSlug}/settings/rover`);
20251
+ console.log(` ${args.serverUrl}/${args.orgSlug}/rovers`);
19829
20252
  } else if (outcome.approvalStatus === "approved") {
19830
20253
  console.log("\u2713 Rover is live and already approved. Heartbeats will start within 60s.");
19831
20254
  } else {
19832
20255
  console.log(`! Rover state: ${outcome.approvalStatus}.`);
19833
20256
  }
19834
20257
  }
19835
- async function fetchZoneName(apiToken, zoneId) {
19836
- const res = await fetch(`https://api.cloudflare.com/client/v4/zones/${zoneId}`, {
19837
- headers: {
19838
- Authorization: `Bearer ${apiToken}`,
19839
- Accept: "application/json"
19840
- },
19841
- signal: AbortSignal.timeout(1e4)
19842
- });
20258
+ async function resolveZone(apiToken, domainName) {
20259
+ const res = await fetch(
20260
+ `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(domainName)}`,
20261
+ {
20262
+ headers: {
20263
+ Authorization: `Bearer ${apiToken}`,
20264
+ Accept: "application/json"
20265
+ },
20266
+ signal: AbortSignal.timeout(1e4)
20267
+ }
20268
+ );
19843
20269
  if (!res.ok) {
19844
20270
  throw new Error(`CF zone lookup failed: HTTP ${res.status}`);
19845
20271
  }
19846
20272
  const body = await res.json();
19847
- if (!body.success || !body.result?.name) {
20273
+ if (!body.success) {
19848
20274
  const reason = body.errors?.[0]?.message ?? "unknown";
19849
20275
  throw new Error(`CF zone lookup failed: ${reason}`);
19850
20276
  }
19851
- return body.result.name;
20277
+ const zone = body.result?.[0];
20278
+ if (!zone?.id || !zone.account?.id) {
20279
+ throw new Error(
20280
+ `No Cloudflare zone found for "${domainName}". The API token may lack Zone:Read, or the domain isn't on this Cloudflare account.`
20281
+ );
20282
+ }
20283
+ return { id: zone.id, name: zone.name, accountId: zone.account.id };
19852
20284
  }
19853
20285
  async function launchDaemon(args, ingress, state) {
19854
20286
  if (!args.detach) {
@@ -19863,11 +20295,11 @@ async function launchDaemon(args, ingress, state) {
19863
20295
  });
19864
20296
  void tunnel.start();
19865
20297
  const { startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
19866
- await startDaemon2({ orgSlug: args.orgSlug, port: args.port });
20298
+ await startDaemon2({ roverId: state.roverId, port: args.port });
19867
20299
  return;
19868
20300
  }
19869
20301
  const argv0 = process.argv[1] ?? "launch-rover";
19870
- const child = (0, import_node_child_process2.spawn)(process.execPath, [argv0, "serve", `--org=${args.orgSlug}`], {
20302
+ const child = (0, import_node_child_process3.spawn)(process.execPath, [argv0, "serve", `--rover=${state.roverId}`], {
19871
20303
  detached: true,
19872
20304
  stdio: "ignore",
19873
20305
  env: {
@@ -19879,23 +20311,24 @@ async function launchDaemon(args, ingress, state) {
19879
20311
  child.unref();
19880
20312
  console.log(`[rover] daemon spawned (pid ${child.pid}, detached)`);
19881
20313
  }
19882
- function runSetupReset(orgSlug) {
19883
- clearRoverState(orgSlug);
20314
+ function runSetupReset(roverId) {
20315
+ clearRoverState(roverId);
19884
20316
  }
19885
- var import_node_os4, import_node_child_process2, import_node_path4, import_node_fs5, DEFAULT_SERVER, DEFAULT_DAEMON_PORT;
20317
+ var import_node_os5, import_node_child_process3, import_node_path5, import_node_fs6, KIT_VERSION, DEFAULT_SERVER, DEFAULT_DAEMON_PORT;
19886
20318
  var init_setup = __esm({
19887
20319
  "src/server/rover/setup.ts"() {
19888
20320
  "use strict";
19889
- import_node_os4 = require("node:os");
19890
- import_node_child_process2 = require("node:child_process");
19891
- import_node_path4 = require("node:path");
19892
- import_node_fs5 = require("node:fs");
20321
+ import_node_os5 = require("node:os");
20322
+ import_node_child_process3 = require("node:child_process");
20323
+ import_node_path5 = require("node:path");
20324
+ import_node_fs6 = require("node:fs");
19893
20325
  init_cf_ingress();
19894
20326
  init_launch_kit_paths();
19895
20327
  init_docker_install();
19896
20328
  init_mcp_client();
19897
20329
  init_registration();
19898
20330
  init_state();
20331
+ KIT_VERSION = require_package().version;
19899
20332
  DEFAULT_SERVER = "https://launchsecure-v2.vercel.app";
19900
20333
  DEFAULT_DAEMON_PORT = 52749;
19901
20334
  }
@@ -19907,41 +20340,41 @@ __export(teardown_exports, {
19907
20340
  runStatus: () => runStatus,
19908
20341
  runTeardown: () => runTeardown
19909
20342
  });
19910
- function parseArgs2(argv, requireOrg = true) {
19911
- let orgSlug;
20343
+ function parseArgs2(argv, requireRover = true) {
20344
+ let roverId;
19912
20345
  let force = false;
19913
20346
  for (const arg of argv) {
19914
- if (arg.startsWith("--org=")) orgSlug = arg.slice("--org=".length);
20347
+ if (arg.startsWith("--rover=")) roverId = arg.slice("--rover=".length);
19915
20348
  else if (arg === "--force" || arg === "-f") force = true;
19916
20349
  else if (arg === "--help" || arg === "-h") return null;
19917
20350
  else throw new Error(`Unknown argument: ${arg}`);
19918
20351
  }
19919
- if (!orgSlug) {
19920
- const known = listRoverOrgs();
20352
+ if (!roverId) {
20353
+ const known = listRoverIds();
19921
20354
  if (known.length === 1) {
19922
- orgSlug = known[0];
19923
- } else if (requireOrg) {
20355
+ roverId = known[0];
20356
+ } else if (requireRover) {
19924
20357
  const hint = known.length === 0 ? "no rovers found" : `multiple rovers found: ${known.join(", ")}`;
19925
- throw new Error(`--org=<slug> is required (${hint})`);
20358
+ throw new Error(`--rover=<id> is required (${hint})`);
19926
20359
  } else {
19927
20360
  return null;
19928
20361
  }
19929
20362
  }
19930
- return { orgSlug, force };
20363
+ return { roverId, force };
19931
20364
  }
19932
20365
  async function runTeardown(argv) {
19933
20366
  const parsed = parseArgs2(argv);
19934
20367
  if (!parsed) {
19935
- console.log("Usage: launch-rover teardown --org=<slug> [--force]");
20368
+ console.log("Usage: launch-rover teardown --rover=<id> [--force]");
19936
20369
  return;
19937
20370
  }
19938
- const state = loadRoverState(parsed.orgSlug);
20371
+ const state = loadRoverState(parsed.roverId);
19939
20372
  if (!state) {
19940
- console.log(`[rover] no local state for org "${parsed.orgSlug}" \u2014 nothing to tear down.`);
20373
+ console.log(`[rover] no local state for rover "${parsed.roverId}" \u2014 nothing to tear down.`);
19941
20374
  return;
19942
20375
  }
19943
20376
  const lock = readRoverLock();
19944
- if (lock && lock.orgSlug === parsed.orgSlug) {
20377
+ if (lock && lock.roverId === parsed.roverId) {
19945
20378
  if (isPidAlive(lock.pid)) {
19946
20379
  console.log(`[rover] stopping daemon (pid ${lock.pid})\u2026`);
19947
20380
  try {
@@ -19961,44 +20394,43 @@ async function runTeardown(argv) {
19961
20394
  }
19962
20395
  }
19963
20396
  }
19964
- clearRoverState(parsed.orgSlug);
19965
- const tunnelStateFile = (0, import_node_path5.join)(
19966
- (0, import_node_os5.homedir)(),
20397
+ clearRoverState(parsed.roverId);
20398
+ const tunnelStateFile = (0, import_node_path6.join)(
20399
+ (0, import_node_os6.homedir)(),
19967
20400
  LAUNCHSECURE_DIR,
19968
20401
  "rover",
19969
- `${parsed.orgSlug}.tunnel.json`
20402
+ `${parsed.roverId}.tunnel.json`
19970
20403
  );
19971
- if ((0, import_node_fs6.existsSync)(tunnelStateFile)) {
20404
+ if ((0, import_node_fs7.existsSync)(tunnelStateFile)) {
19972
20405
  try {
19973
- (0, import_node_fs6.unlinkSync)(tunnelStateFile);
20406
+ (0, import_node_fs7.unlinkSync)(tunnelStateFile);
19974
20407
  } catch {
19975
20408
  }
19976
20409
  }
19977
- console.log(`[rover] teardown complete for org "${parsed.orgSlug}".`);
20410
+ console.log(`[rover] teardown complete for rover "${parsed.roverId}".`);
19978
20411
  console.log(" \u2192 To fully reset on the LS side (revoke install PAT, allow re-create), use the");
19979
- console.log(` "Reset" button at ${state.serverUrl}/${parsed.orgSlug}/settings/rover`);
20412
+ console.log(` "Reset" button at ${state.serverUrl}/${state.orgSlug}/rovers`);
19980
20413
  }
19981
20414
  function runStatus(argv) {
19982
20415
  const parsed = parseArgs2(
19983
20416
  argv,
19984
- /* requireOrg */
20417
+ /* requireRover */
19985
20418
  false
19986
20419
  );
19987
- const orgs = parsed ? [parsed.orgSlug] : listRoverOrgs();
19988
- if (orgs.length === 0) {
20420
+ const rovers = parsed ? [parsed.roverId] : listRoverIds();
20421
+ if (rovers.length === 0) {
19989
20422
  console.log("[rover] no rovers configured on this host.");
19990
20423
  return;
19991
20424
  }
19992
- for (const orgSlug of orgs) {
19993
- const state = loadRoverState(orgSlug);
20425
+ for (const roverId of rovers) {
20426
+ const state = loadRoverState(roverId);
19994
20427
  if (!state) {
19995
- console.log(`[rover] ${orgSlug}: no state file`);
20428
+ console.log(`[rover] ${roverId}: no state file`);
19996
20429
  continue;
19997
20430
  }
19998
20431
  const lock = readRoverLock();
19999
- const live = lock && lock.orgSlug === orgSlug && isPidAlive(lock.pid);
20000
- console.log(`\u2500\u2500 ${orgSlug} \u2500\u2500`);
20001
- console.log(` rover id: ${state.roverId}`);
20432
+ const live = lock && lock.roverId === roverId && isPidAlive(lock.pid);
20433
+ console.log(`\u2500\u2500 ${roverId} (${state.orgSlug}) \u2500\u2500`);
20002
20434
  console.log(` org id: ${state.organizationId}`);
20003
20435
  console.log(` server: ${state.serverUrl}`);
20004
20436
  console.log(` tunnel url: ${state.tunnelUrl}`);
@@ -20022,13 +20454,13 @@ function isPidAlive(pid) {
20022
20454
  function sleep(ms) {
20023
20455
  return new Promise((resolve) => setTimeout(resolve, ms));
20024
20456
  }
20025
- var import_node_fs6, import_node_os5, import_node_path5;
20457
+ var import_node_fs7, import_node_os6, import_node_path6;
20026
20458
  var init_teardown = __esm({
20027
20459
  "src/server/rover/teardown.ts"() {
20028
20460
  "use strict";
20029
- import_node_fs6 = require("node:fs");
20030
- import_node_os5 = require("node:os");
20031
- import_node_path5 = require("node:path");
20461
+ import_node_fs7 = require("node:fs");
20462
+ import_node_os6 = require("node:os");
20463
+ import_node_path6 = require("node:path");
20032
20464
  init_launch_kit_paths();
20033
20465
  init_daemon();
20034
20466
  init_state();
@@ -20049,9 +20481,10 @@ function printUsage2() {
20049
20481
  " setup --pat=<PAT> --org=<slug> [options]",
20050
20482
  " install Docker, provision CF tunnel,",
20051
20483
  " register with LaunchSecure, start daemon",
20052
- " serve [--org=<slug>] start the rover HTTP daemon (foreground)",
20053
- " status [--org=<slug>] print state + daemon liveness",
20054
- " teardown --org=<slug> stop daemon and clear local state",
20484
+ " serve [--rover=<id>] start the rover HTTP daemon (foreground).",
20485
+ " With one local rover, --rover is optional.",
20486
+ " status [--rover=<id>] print state + daemon liveness",
20487
+ " teardown --rover=<id> stop daemon and clear local state",
20055
20488
  " (does NOT delete the LS RoverInstance \u2014 use",
20056
20489
  " the Reset button in LS for that)",
20057
20490
  "",
@@ -20074,21 +20507,21 @@ async function main() {
20074
20507
  return;
20075
20508
  }
20076
20509
  case "serve": {
20077
- const orgSlug = argv.find((a) => a.startsWith("--org="))?.slice("--org=".length);
20078
- if (!orgSlug) {
20079
- const { listRoverOrgs: listRoverOrgs2 } = await Promise.resolve().then(() => (init_state(), state_exports));
20080
- const known = listRoverOrgs2();
20510
+ const roverId = argv.find((a) => a.startsWith("--rover="))?.slice("--rover=".length);
20511
+ if (!roverId) {
20512
+ const { listRoverIds: listRoverIds2 } = await Promise.resolve().then(() => (init_state(), state_exports));
20513
+ const known = listRoverIds2();
20081
20514
  if (known.length !== 1) {
20082
20515
  throw new Error(
20083
- `serve needs --org=<slug>${known.length ? ` (known: ${known.join(", ")})` : " (no rovers configured)"}`
20516
+ `serve needs --rover=<id>${known.length ? ` (known: ${known.join(", ")})` : " (no rovers configured)"}`
20084
20517
  );
20085
20518
  }
20086
20519
  const { startDaemon: startDaemon3 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
20087
- await startDaemon3({ orgSlug: known[0] });
20520
+ await startDaemon3({ roverId: known[0] });
20088
20521
  return;
20089
20522
  }
20090
20523
  const { startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
20091
- await startDaemon2({ orgSlug });
20524
+ await startDaemon2({ roverId });
20092
20525
  return;
20093
20526
  }
20094
20527
  case "status": {