@mesh-tech/mesh-cli 0.20.4 → 0.21.0

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/bin/mesh.js CHANGED
@@ -838,6 +838,7 @@ var init_seed = __esm({
838
838
 
839
839
  // libs/mesh-cli/src/commands/local/helpers.ts
840
840
  import * as net from "net";
841
+ import { execFileSync as execFileSync3 } from "child_process";
841
842
  async function upsertLocalSecret(secretId, value, client) {
842
843
  const { SecretsManagerClient: SecretsManagerClient10, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
843
844
  const sm = client ?? new SecretsManagerClient10(LOCAL_AWS_CONFIG);
@@ -867,10 +868,91 @@ function probeTcp(port, opts = {}) {
867
868
  });
868
869
  });
869
870
  }
871
+ function hostPortsOf(endpoints) {
872
+ const ports = /* @__PURE__ */ new Set();
873
+ const fromUrl = (raw) => {
874
+ try {
875
+ const u = new URL(raw.includes("://") ? raw : `tcp://${raw}`);
876
+ const port = Number(u.port || (u.protocol === "https:" ? 443 : u.protocol === "http:" ? 80 : NaN));
877
+ if (Number.isInteger(port) && port > 0) ports.add(port);
878
+ } catch {
879
+ }
880
+ };
881
+ for (const e of endpoints) {
882
+ if (e.probe.kind === "tcp") ports.add(e.probe.port);
883
+ else if (e.probe.kind === "http") fromUrl(e.probe.url);
884
+ fromUrl(e.url);
885
+ }
886
+ return [...ports].sort((a, b) => a - b);
887
+ }
888
+ function parseDockerPsPorts(output, ignorePrefix) {
889
+ const held = /* @__PURE__ */ new Map();
890
+ for (const line of output.split("\n")) {
891
+ const tab = line.indexOf(" ");
892
+ if (tab < 0) continue;
893
+ const name = line.slice(0, tab).trim();
894
+ if (!name || name.startsWith(ignorePrefix)) continue;
895
+ for (const m of line.slice(tab + 1).matchAll(/:(\d+)->\d+\/(?:tcp|udp)/g)) {
896
+ const port = Number(m[1]);
897
+ if (!held.has(port)) held.set(port, name);
898
+ }
899
+ }
900
+ return held;
901
+ }
902
+ async function findPortConflicts(ports, ignorePrefix, io = defaultPortConflictIo) {
903
+ const unique = [...new Set(ports)].sort((a, b) => a - b);
904
+ const bound = (await Promise.all(unique.map(async (port) => await io.listening(port) ? port : null))).filter(
905
+ (p) => p !== null
906
+ );
907
+ if (bound.length === 0) return [];
908
+ const containers = parseDockerPsPorts(io.dockerPs(), ignorePrefix);
909
+ return bound.map((port) => {
910
+ const container = containers.get(port);
911
+ if (container) return { port, holder: { kind: "container", name: container } };
912
+ const process2 = io.processOn(port);
913
+ return { port, holder: process2 ? { kind: "process", name: process2 } : null };
914
+ });
915
+ }
916
+ function describePortConflicts(conflicts, moveHints = /* @__PURE__ */ new Map()) {
917
+ const lines = conflicts.flatMap(({ port, holder }) => {
918
+ const remedy = holder?.kind === "container" ? `docker stop ${holder.name}` : `lsof -nP -iTCP:${port} -sTCP:LISTEN, then stop what it names`;
919
+ const what = holder ? `held by ${holder.kind} ${holder.name}` : "held by something lsof would not name";
920
+ const move = moveHints.get(port);
921
+ return move ? [` ${String(port).padEnd(5)} ${what.padEnd(38)} (${remedy})`, ` ${"".padEnd(5)} or: ${move}`] : [` ${String(port).padEnd(5)} ${what.padEnd(38)} (${remedy})`];
922
+ });
923
+ return `Cannot start the local platform \u2014 ${conflicts.length === 1 ? "a host port it needs is" : "host ports it needs are"} already in use:
924
+ ` + lines.join("\n") + '\nStop what holds the port (or move it), then run mesh start again. A port lost this way does not fail as "port in use": the container never joins the network and a later service reports an unrelated error.\nIf the port is held by something you cannot stop and the stack can live without it: mesh start --skip-port-check';
925
+ }
926
+ var defaultPortConflictIo;
870
927
  var init_helpers = __esm({
871
928
  "libs/mesh-cli/src/commands/local/helpers.ts"() {
872
929
  "use strict";
873
930
  init_seed();
931
+ defaultPortConflictIo = {
932
+ dockerPs: () => {
933
+ try {
934
+ return execFileSync3("docker", ["ps", "--format", "{{.Names}} {{.Ports}}"], {
935
+ encoding: "utf-8",
936
+ stdio: ["ignore", "pipe", "ignore"]
937
+ });
938
+ } catch {
939
+ return "";
940
+ }
941
+ },
942
+ processOn: (port) => {
943
+ try {
944
+ const out = execFileSync3("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fc"], {
945
+ encoding: "utf-8",
946
+ stdio: ["ignore", "pipe", "ignore"]
947
+ });
948
+ const cmd = out.split("\n").find((l) => l.startsWith("c"));
949
+ return cmd ? cmd.slice(1) : null;
950
+ } catch {
951
+ return null;
952
+ }
953
+ },
954
+ listening: (port) => probeTcp(port, { timeoutMs: 300 })
955
+ };
874
956
  }
875
957
  });
876
958
 
@@ -922,7 +1004,7 @@ __export(stack_exports, {
922
1004
  summarizeComposeFailure: () => summarizeComposeFailure,
923
1005
  writeAppServiceProbes: () => writeAppServiceProbes
924
1006
  });
925
- import { execFileSync as execFileSync3, spawn, spawnSync } from "child_process";
1007
+ import { execFileSync as execFileSync4, spawn, spawnSync } from "child_process";
926
1008
  import * as fs4 from "fs";
927
1009
  import * as path4 from "path";
928
1010
  import { fileURLToPath } from "url";
@@ -1028,7 +1110,7 @@ function stackOwnedElsewhere() {
1028
1110
  try {
1029
1111
  const first = compose(["ps", "-q"]).trim().split("\n").filter(Boolean)[0];
1030
1112
  if (!first) return void 0;
1031
- const label = execFileSync3(
1113
+ const label = execFileSync4(
1032
1114
  "docker",
1033
1115
  ["inspect", first, "--format", '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'],
1034
1116
  { encoding: "utf-8" }
@@ -1061,7 +1143,7 @@ function compose(args, opts = {}) {
1061
1143
  const files = ["-f", path4.join(dir, "docker-compose.yml")];
1062
1144
  if (opts.hub) files.push("-f", path4.join(dir, "docker-compose.hub.yml"));
1063
1145
  const fullArgs = ["compose", "-p", COMPOSE_PROJECT, ...files, ...args];
1064
- return execFileSync3("docker", fullArgs, {
1146
+ return execFileSync4("docker", fullArgs, {
1065
1147
  cwd: dir,
1066
1148
  encoding: "utf-8",
1067
1149
  // MESH_LOCAL_LOGS on every call: the compose file mounts it into the
@@ -1320,10 +1402,10 @@ var init_stack = __esm({
1320
1402
  // libs/mesh-cli/src/commands/skills.ts
1321
1403
  import * as fs5 from "fs";
1322
1404
  import * as path5 from "path";
1323
- import { execFileSync as execFileSync4 } from "child_process";
1405
+ import { execFileSync as execFileSync5 } from "child_process";
1324
1406
  function resolveTargetRoot(startDir = process.cwd()) {
1325
1407
  try {
1326
- return execFileSync4("git", ["rev-parse", "--show-toplevel"], {
1408
+ return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
1327
1409
  cwd: startDir,
1328
1410
  encoding: "utf-8",
1329
1411
  stdio: ["ignore", "pipe", "pipe"]
@@ -1686,7 +1768,7 @@ var init_pid = __esm({
1686
1768
  });
1687
1769
 
1688
1770
  // libs/mesh-cli/src/utils/tailscale-targets.ts
1689
- import { execFileSync as execFileSync5 } from "node:child_process";
1771
+ import { execFileSync as execFileSync6 } from "node:child_process";
1690
1772
  import * as fs6 from "node:fs";
1691
1773
  import * as path6 from "node:path";
1692
1774
  function tenantPortIndex(tenant, registryPath) {
@@ -1723,7 +1805,7 @@ function resolveTunnelTargets(services, resolveIp, portOffset = 0) {
1723
1805
  }
1724
1806
  function resolveElbIp(host) {
1725
1807
  try {
1726
- const out = execFileSync5("dig", ["+short", host, "@1.1.1.1"], {
1808
+ const out = execFileSync6("dig", ["+short", host, "@1.1.1.1"], {
1727
1809
  encoding: "utf8",
1728
1810
  stdio: ["ignore", "pipe", "ignore"]
1729
1811
  });
@@ -1830,7 +1912,7 @@ import * as crypto from "node:crypto";
1830
1912
  import * as fs7 from "node:fs";
1831
1913
  import * as net3 from "node:net";
1832
1914
  import * as path7 from "node:path";
1833
- import { execFileSync as execFileSync6 } from "node:child_process";
1915
+ import { execFileSync as execFileSync7 } from "node:child_process";
1834
1916
  function mintRunnerToken() {
1835
1917
  return crypto.randomBytes(16).toString("hex");
1836
1918
  }
@@ -1855,7 +1937,7 @@ function clearRunnerManifest(file, token) {
1855
1937
  }
1856
1938
  function getProcessGroupId(pid) {
1857
1939
  try {
1858
- const out = execFileSync6("ps", ["-o", "pgid=", "-p", String(pid)], {
1940
+ const out = execFileSync7("ps", ["-o", "pgid=", "-p", String(pid)], {
1859
1941
  encoding: "utf8",
1860
1942
  stdio: ["ignore", "pipe", "ignore"]
1861
1943
  }).trim();
@@ -1881,7 +1963,7 @@ function parseRunnerPidsFromPs(psOutput, tenant, exclude) {
1881
1963
  }
1882
1964
  function discoverRunnerPids(tenant, exclude) {
1883
1965
  try {
1884
- const out = execFileSync6("ps", ["ax", "-o", "pid=,command="], {
1966
+ const out = execFileSync7("ps", ["ax", "-o", "pid=,command="], {
1885
1967
  encoding: "utf8",
1886
1968
  stdio: ["ignore", "pipe", "ignore"]
1887
1969
  });
@@ -1973,7 +2055,7 @@ async function pollOwnership(check, deadlineMs) {
1973
2055
  }
1974
2056
  function describePortOwner(port) {
1975
2057
  try {
1976
- const out = execFileSync6("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fcp"], {
2058
+ const out = execFileSync7("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fcp"], {
1977
2059
  encoding: "utf8",
1978
2060
  stdio: ["ignore", "pipe", "ignore"]
1979
2061
  });
@@ -2022,7 +2104,7 @@ import * as fs8 from "node:fs";
2022
2104
  import * as net4 from "node:net";
2023
2105
  import * as os2 from "node:os";
2024
2106
  import * as path8 from "node:path";
2025
- import { spawn as spawn2, execFileSync as execFileSync7 } from "node:child_process";
2107
+ import { spawn as spawn2, execFileSync as execFileSync8 } from "node:child_process";
2026
2108
  function tenantStateDir(tenant) {
2027
2109
  return path8.join(CONFIG_DIR, "tailscale", tenant);
2028
2110
  }
@@ -2097,7 +2179,7 @@ function resolveBrewBin(name) {
2097
2179
  const brew = `/opt/homebrew/bin/${name}`;
2098
2180
  if (fs8.existsSync(brew)) return brew;
2099
2181
  try {
2100
- const p = execFileSync7("which", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
2182
+ const p = execFileSync8("which", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
2101
2183
  if (p) return p;
2102
2184
  } catch {
2103
2185
  }
@@ -2106,7 +2188,7 @@ function resolveBrewBin(name) {
2106
2188
  function daemonState(tenant) {
2107
2189
  if (!fs8.existsSync(socketPath(tenant))) return { backendState: "Down" };
2108
2190
  try {
2109
- const out = execFileSync7(
2191
+ const out = execFileSync8(
2110
2192
  tailscaleBinPath(),
2111
2193
  ["--socket", socketPath(tenant), "status", "--json"],
2112
2194
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
@@ -2126,7 +2208,7 @@ function findRunningDaemon(tenant) {
2126
2208
  const sock = socketPath(tenant);
2127
2209
  let out;
2128
2210
  try {
2129
- out = execFileSync7("ps", ["ax", "-o", "pid=,command="], {
2211
+ out = execFileSync8("ps", ["ax", "-o", "pid=,command="], {
2130
2212
  encoding: "utf8",
2131
2213
  stdio: ["ignore", "pipe", "ignore"]
2132
2214
  });
@@ -2290,7 +2372,7 @@ async function joinHeadscale(tenant, loginServer, opts = {}) {
2290
2372
  }
2291
2373
  function logout(tenant) {
2292
2374
  try {
2293
- execFileSync7(tailscaleBinPath(), ["--socket", socketPath(tenant), "logout"], { stdio: "ignore" });
2375
+ execFileSync8(tailscaleBinPath(), ["--socket", socketPath(tenant), "logout"], { stdio: "ignore" });
2294
2376
  } catch {
2295
2377
  }
2296
2378
  }
@@ -2531,8 +2613,8 @@ function guiDomain() {
2531
2613
  function launchdAvailable() {
2532
2614
  if (_launchdAvailable !== void 0) return _launchdAvailable;
2533
2615
  try {
2534
- execFileSync7("which", ["launchctl"], { stdio: "ignore" });
2535
- execFileSync7("launchctl", ["print", guiDomain()], { stdio: "ignore" });
2616
+ execFileSync8("which", ["launchctl"], { stdio: "ignore" });
2617
+ execFileSync8("launchctl", ["print", guiDomain()], { stdio: "ignore" });
2536
2618
  _launchdAvailable = true;
2537
2619
  } catch {
2538
2620
  _launchdAvailable = false;
@@ -2564,10 +2646,10 @@ function installLaunchAgent(tenant, meshBin) {
2564
2646
  })
2565
2647
  );
2566
2648
  try {
2567
- execFileSync7("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
2649
+ execFileSync8("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
2568
2650
  } catch {
2569
2651
  }
2570
- execFileSync7("launchctl", ["bootstrap", guiDomain(), plistPath], { stdio: "ignore" });
2652
+ execFileSync8("launchctl", ["bootstrap", guiDomain(), plistPath], { stdio: "ignore" });
2571
2653
  return true;
2572
2654
  } catch {
2573
2655
  return false;
@@ -2576,7 +2658,7 @@ function installLaunchAgent(tenant, meshBin) {
2576
2658
  function uninstallLaunchAgent(tenant) {
2577
2659
  const plistPath = launchAgentPath(tenant);
2578
2660
  try {
2579
- execFileSync7("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
2661
+ execFileSync8("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
2580
2662
  } catch {
2581
2663
  }
2582
2664
  try {
@@ -2816,7 +2898,7 @@ var init_tunnel = __esm({
2816
2898
  });
2817
2899
 
2818
2900
  // libs/mesh-cli/src/commands/vpn/index.ts
2819
- import { execFileSync as execFileSync8 } from "child_process";
2901
+ import { execFileSync as execFileSync9 } from "child_process";
2820
2902
  import * as net6 from "node:net";
2821
2903
  function defaultNamespace(tenant, env) {
2822
2904
  return `${tenant}-${env}-headscale`;
@@ -2834,7 +2916,7 @@ function headscaleExec(namespace, args, opts) {
2834
2916
  stdio: ["pipe", "pipe", "pipe"]
2835
2917
  };
2836
2918
  try {
2837
- const result = execFileSync8(
2919
+ const result = execFileSync9(
2838
2920
  "kubectl",
2839
2921
  [
2840
2922
  "exec",
@@ -2863,7 +2945,7 @@ function clusterName(tenant, env) {
2863
2945
  }
2864
2946
  function assertPodReady(namespace, options) {
2865
2947
  try {
2866
- const output = execFileSync8(
2948
+ const output = execFileSync9(
2867
2949
  "kubectl",
2868
2950
  [
2869
2951
  "get",
@@ -2898,7 +2980,7 @@ async function vpnStatus(options) {
2898
2980
  const ns = resolveNamespace(options);
2899
2981
  logInfo(`Checking Headscale in namespace ${ns}...`);
2900
2982
  assertPodReady(ns, options);
2901
- const podJson = execFileSync8(
2983
+ const podJson = execFileSync9(
2902
2984
  "kubectl",
2903
2985
  [
2904
2986
  "get",
@@ -2920,7 +3002,7 @@ async function vpnStatus(options) {
2920
3002
  const restarts = container?.restartCount ?? 0;
2921
3003
  let endpoint = "unknown";
2922
3004
  try {
2923
- endpoint = execFileSync8(
3005
+ endpoint = execFileSync9(
2924
3006
  "kubectl",
2925
3007
  [
2926
3008
  "get",
@@ -3075,7 +3157,7 @@ function findFreePort2() {
3075
3157
  }
3076
3158
  function findTailscale() {
3077
3159
  try {
3078
- return execFileSync8("which", ["tailscale"], {
3160
+ return execFileSync9("which", ["tailscale"], {
3079
3161
  encoding: "utf-8",
3080
3162
  stdio: ["pipe", "pipe", "pipe"]
3081
3163
  }).trim() || null;
@@ -3085,7 +3167,7 @@ function findTailscale() {
3085
3167
  }
3086
3168
  function tailscaleStatus() {
3087
3169
  try {
3088
- const json = execFileSync8("tailscale", ["status", "--json"], {
3170
+ const json = execFileSync9("tailscale", ["status", "--json"], {
3089
3171
  encoding: "utf-8",
3090
3172
  stdio: ["pipe", "pipe", "pipe"]
3091
3173
  });
@@ -3166,7 +3248,7 @@ async function vpnConnectSystem(endpoint) {
3166
3248
  "--accept-routes"
3167
3249
  ];
3168
3250
  try {
3169
- execFileSync8("tailscale", upArgs, { stdio: "inherit" });
3251
+ execFileSync9("tailscale", upArgs, { stdio: "inherit" });
3170
3252
  console.log("");
3171
3253
  logSuccess("VPN connected.");
3172
3254
  } catch (error) {
@@ -3193,7 +3275,7 @@ async function vpnDisconnect(opts) {
3193
3275
  }
3194
3276
  logInfo("Disconnecting system VPN\u2026");
3195
3277
  try {
3196
- execFileSync8("tailscale", ["down"], { stdio: "inherit" });
3278
+ execFileSync9("tailscale", ["down"], { stdio: "inherit" });
3197
3279
  logSuccess("VPN disconnected.");
3198
3280
  } catch (error) {
3199
3281
  const code = error.status;
@@ -3278,6 +3360,7 @@ __export(login_exports, {
3278
3360
  getContextConfig: () => getContextConfig,
3279
3361
  getValidToken: () => getValidToken,
3280
3362
  isRemoteEnvironment: () => isRemoteEnvironment,
3363
+ loginTimeoutMessage: () => loginTimeoutMessage,
3281
3364
  probeCredentials: () => probeCredentials,
3282
3365
  readAllContextConfigs: () => readAllContextConfigs,
3283
3366
  readAllCredentials: () => readAllCredentials,
@@ -3298,7 +3381,7 @@ import * as http from "http";
3298
3381
  import * as crypto2 from "crypto";
3299
3382
  import * as fs9 from "fs";
3300
3383
  import * as path9 from "path";
3301
- import { execFileSync as execFileSync9 } from "child_process";
3384
+ import { execFileSync as execFileSync10 } from "child_process";
3302
3385
  function readConfig() {
3303
3386
  if (!fs9.existsSync(CONFIG_FILE)) return {};
3304
3387
  try {
@@ -3613,9 +3696,9 @@ function login(context, config) {
3613
3696
  const url = authUrl.toString();
3614
3697
  try {
3615
3698
  if (process.platform === "darwin") {
3616
- execFileSync9("open", [url], { stdio: "ignore" });
3699
+ execFileSync10("open", [url], { stdio: "ignore" });
3617
3700
  } else if (process.platform === "linux") {
3618
- execFileSync9("xdg-open", [url], { stdio: "ignore" });
3701
+ execFileSync10("xdg-open", [url], { stdio: "ignore" });
3619
3702
  } else {
3620
3703
  logInfo(`Open this URL in your browser:
3621
3704
  ${url}`);
@@ -3627,7 +3710,7 @@ ${url}`);
3627
3710
  });
3628
3711
  timeoutId = setTimeout(() => {
3629
3712
  teardown();
3630
- reject(new Error("Login timed out (2 minutes)"));
3713
+ reject(new Error(loginTimeoutMessage(context)));
3631
3714
  }, 12e4);
3632
3715
  });
3633
3716
  }
@@ -3668,9 +3751,9 @@ async function attemptDeviceCode(context, config) {
3668
3751
  logInfo("Waiting for authorization...");
3669
3752
  try {
3670
3753
  if (process.platform === "darwin") {
3671
- execFileSync9("open", [openUrl], { stdio: "ignore" });
3754
+ execFileSync10("open", [openUrl], { stdio: "ignore" });
3672
3755
  } else if (process.platform === "linux") {
3673
- execFileSync9("xdg-open", [openUrl], { stdio: "ignore" });
3756
+ execFileSync10("xdg-open", [openUrl], { stdio: "ignore" });
3674
3757
  }
3675
3758
  } catch {
3676
3759
  }
@@ -3731,6 +3814,11 @@ async function attemptDeviceCode(context, config) {
3731
3814
  }
3732
3815
  return "expired";
3733
3816
  }
3817
+ function loginTimeoutMessage(context) {
3818
+ const base = "Login timed out (2 minutes)";
3819
+ if (context !== "local") return base;
3820
+ return `${base}. The local platform already has a signed-up user \u2014 sign in as dev@local.mesh / LocalDev1! instead of registering. If you did register, its confirmation email is in the local mailbox at http://localhost:8025; confirm it there and run the command again.`;
3821
+ }
3734
3822
  function isRemoteEnvironment() {
3735
3823
  if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES) return true;
3736
3824
  if (fs9.existsSync("/.dockerenv")) return true;
@@ -5306,7 +5394,7 @@ var init_kubeconfig = __esm({
5306
5394
  });
5307
5395
 
5308
5396
  // libs/mesh-cli/src/utils/temporal-auth.ts
5309
- import { execFileSync as execFileSync10 } from "node:child_process";
5397
+ import { execFileSync as execFileSync11 } from "node:child_process";
5310
5398
  async function resolveTemporalAuth(tenant, env, platformName = tenant) {
5311
5399
  const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
5312
5400
  const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
@@ -5350,7 +5438,7 @@ async function resolveTemporalAuth(tenant, env, platformName = tenant) {
5350
5438
  }
5351
5439
  if (!results.ZITADEL_ISSUER) {
5352
5440
  try {
5353
- const podEnv = execFileSync10(
5441
+ const podEnv = execFileSync11(
5354
5442
  "kubectl",
5355
5443
  [
5356
5444
  "get",
@@ -5625,7 +5713,7 @@ var init_dev_launch = __esm({
5625
5713
  });
5626
5714
 
5627
5715
  // libs/mesh-cli/src/commands/local/mocks.ts
5628
- import { execFileSync as execFileSync11 } from "child_process";
5716
+ import { execFileSync as execFileSync12 } from "child_process";
5629
5717
  import * as fs14 from "fs";
5630
5718
  import * as path14 from "path";
5631
5719
  function externalMode(name, decl) {
@@ -5778,7 +5866,7 @@ async function composeExternalUp(appRoot, sessionName, name, decl, probes = dock
5778
5866
  if (plan.action === "adopt-stopped") {
5779
5867
  const { container } = plan;
5780
5868
  logInfo(`External '${name}': container '${container}' exists from another checkout \u2014 starting and adopting it.`);
5781
- execFileSync11("docker", ["start", container], { stdio: ["ignore", "ignore", "inherit"] });
5869
+ execFileSync12("docker", ["start", container], { stdio: ["ignore", "ignore", "inherit"] });
5782
5870
  if (!await waitForPort2(decl.port, 3e5)) {
5783
5871
  throw new MeshCliError(
5784
5872
  `External '${name}': adopted container '${container}' never served localhost:${decl.port}.`,
@@ -5787,7 +5875,7 @@ async function composeExternalUp(appRoot, sessionName, name, decl, probes = dock
5787
5875
  }
5788
5876
  return void 0;
5789
5877
  }
5790
- execFileSync11(
5878
+ execFileSync12(
5791
5879
  "docker",
5792
5880
  ["compose", "-p", project, "-f", composeFile, "up", "-d", "--wait", "--wait-timeout", "300"],
5793
5881
  { stdio: ["ignore", "inherit", "inherit"] }
@@ -5796,7 +5884,7 @@ async function composeExternalUp(appRoot, sessionName, name, decl, probes = dock
5796
5884
  }
5797
5885
  function ownsRunningRealization(composeFile, project) {
5798
5886
  try {
5799
- const out = execFileSync11(
5887
+ const out = execFileSync12(
5800
5888
  "docker",
5801
5889
  ["compose", "-p", project, "-f", composeFile, "ps", "--format", "json"],
5802
5890
  { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
@@ -5809,7 +5897,7 @@ function ownsRunningRealization(composeFile, project) {
5809
5897
  function composeServices(composeFile) {
5810
5898
  try {
5811
5899
  const config = JSON.parse(
5812
- execFileSync11("docker", ["compose", "-f", composeFile, "config", "--format", "json"], {
5900
+ execFileSync12("docker", ["compose", "-f", composeFile, "config", "--format", "json"], {
5813
5901
  encoding: "utf-8",
5814
5902
  stdio: ["ignore", "pipe", "ignore"]
5815
5903
  })
@@ -5822,7 +5910,7 @@ function composeServices(composeFile) {
5822
5910
  }
5823
5911
  function portPublisher(port) {
5824
5912
  try {
5825
- const out = execFileSync11(
5913
+ const out = execFileSync12(
5826
5914
  "docker",
5827
5915
  [
5828
5916
  "ps",
@@ -5869,7 +5957,7 @@ function foreignPinnedContainer(composeFile, project) {
5869
5957
  const pinned = svc?.container_name;
5870
5958
  if (!pinned) continue;
5871
5959
  try {
5872
- const owner = execFileSync11(
5960
+ const owner = execFileSync12(
5873
5961
  "docker",
5874
5962
  ["inspect", pinned, "--format", '{{ index .Config.Labels "com.docker.compose.project" }}'],
5875
5963
  { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
@@ -5883,7 +5971,7 @@ function foreignPinnedContainer(composeFile, project) {
5883
5971
  function composeExternalsDown(refs) {
5884
5972
  for (const ref of refs ?? []) {
5885
5973
  try {
5886
- execFileSync11("docker", ["compose", "-p", ref.project, "-f", ref.composeFile, "down", "--remove-orphans"], {
5974
+ execFileSync12("docker", ["compose", "-p", ref.project, "-f", ref.composeFile, "down", "--remove-orphans"], {
5887
5975
  stdio: ["ignore", "inherit", "inherit"]
5888
5976
  });
5889
5977
  } catch {
@@ -7409,7 +7497,7 @@ var init_dev_local = __esm({
7409
7497
  });
7410
7498
 
7411
7499
  // libs/mesh-cli/src/commands/local/docker-runner.ts
7412
- import { execFileSync as execFileSync12 } from "child_process";
7500
+ import { execFileSync as execFileSync13 } from "child_process";
7413
7501
  import * as fs18 from "fs";
7414
7502
  import * as path18 from "path";
7415
7503
  function sessionDir(sessionName) {
@@ -7467,29 +7555,29 @@ function composeArgs(sessionName, args) {
7467
7555
  return ["compose", "-p", sessionName, "-f", path18.join(sessionDir(sessionName), "compose.yml"), ...args];
7468
7556
  }
7469
7557
  function dockerDevUp(sessionName) {
7470
- execFileSync12("docker", composeArgs(sessionName, ["up", "-d"]), {
7558
+ execFileSync13("docker", composeArgs(sessionName, ["up", "-d"]), {
7471
7559
  stdio: ["ignore", "inherit", "inherit"]
7472
7560
  });
7473
7561
  }
7474
7562
  function dockerDevDown(sessionName) {
7475
- execFileSync12("docker", composeArgs(sessionName, ["down", "--remove-orphans"]), {
7563
+ execFileSync13("docker", composeArgs(sessionName, ["down", "--remove-orphans"]), {
7476
7564
  stdio: ["ignore", "inherit", "inherit"]
7477
7565
  });
7478
7566
  }
7479
7567
  function dockerDevPs(sessionName) {
7480
- return execFileSync12("docker", composeArgs(sessionName, ["ps", "--format", "table {{.Service}} {{.State}} {{.Status}}"]), {
7568
+ return execFileSync13("docker", composeArgs(sessionName, ["ps", "--format", "table {{.Service}} {{.State}} {{.Status}}"]), {
7481
7569
  encoding: "utf-8",
7482
7570
  stdio: ["ignore", "pipe", "pipe"]
7483
7571
  });
7484
7572
  }
7485
7573
  function dockerDevLogs(sessionName, service, tail) {
7486
- return execFileSync12("docker", composeArgs(sessionName, ["logs", "--tail", String(tail), service]), {
7574
+ return execFileSync13("docker", composeArgs(sessionName, ["logs", "--tail", String(tail), service]), {
7487
7575
  encoding: "utf-8",
7488
7576
  stdio: ["ignore", "pipe", "pipe"]
7489
7577
  });
7490
7578
  }
7491
7579
  function dockerDevRestart(sessionName, service) {
7492
- execFileSync12("docker", composeArgs(sessionName, ["restart", service]), {
7580
+ execFileSync13("docker", composeArgs(sessionName, ["restart", service]), {
7493
7581
  stdio: ["ignore", "inherit", "inherit"]
7494
7582
  });
7495
7583
  }
@@ -7558,7 +7646,7 @@ var init_peer_addressing = __esm({
7558
7646
  });
7559
7647
 
7560
7648
  // libs/mesh-cli/src/utils/worktree-identity.ts
7561
- import { execFileSync as execFileSync13 } from "node:child_process";
7649
+ import { execFileSync as execFileSync14 } from "node:child_process";
7562
7650
  import * as crypto3 from "node:crypto";
7563
7651
  import * as path19 from "node:path";
7564
7652
  function sanitizeSlug(name) {
@@ -7630,7 +7718,7 @@ var init_worktree_identity = __esm({
7630
7718
  PORT_BLOCK_SIZE = 40;
7631
7719
  PORT_BLOCK_SERVICE_SUBRANGE = 24;
7632
7720
  STACK_SLUG_MAX = 16;
7633
- defaultGitRunner = (args, cwd) => execFileSync13("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
7721
+ defaultGitRunner = (args, cwd) => execFileSync14("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
7634
7722
  }
7635
7723
  });
7636
7724
 
@@ -7692,7 +7780,7 @@ var init_dev_token_server = __esm({
7692
7780
  });
7693
7781
 
7694
7782
  // libs/mesh-cli/src/commands/dev.ts
7695
- import { execFileSync as execFileSync14, spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
7783
+ import { execFileSync as execFileSync15, spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
7696
7784
  import * as fs19 from "fs";
7697
7785
  import * as net8 from "net";
7698
7786
  import * as os6 from "os";
@@ -8023,7 +8111,7 @@ function detectStack(appRoot, stageArg) {
8023
8111
  if (stageArg) return stageArg;
8024
8112
  if (process.env.MESH_STAGE) return process.env.MESH_STAGE;
8025
8113
  try {
8026
- const result = execFileSync14("pulumi", ["stack", "--show-name"], {
8114
+ const result = execFileSync15("pulumi", ["stack", "--show-name"], {
8027
8115
  cwd: appRoot,
8028
8116
  encoding: "utf-8",
8029
8117
  stdio: ["ignore", "pipe", "ignore"]
@@ -8046,7 +8134,7 @@ function detectStack(appRoot, stageArg) {
8046
8134
  }
8047
8135
  function stackArgs(appRoot, stack) {
8048
8136
  try {
8049
- const selected = execFileSync14("pulumi", ["stack", "--show-name"], {
8137
+ const selected = execFileSync15("pulumi", ["stack", "--show-name"], {
8050
8138
  cwd: appRoot,
8051
8139
  encoding: "utf-8",
8052
8140
  stdio: ["ignore", "pipe", "ignore"]
@@ -8055,7 +8143,7 @@ function stackArgs(appRoot, stack) {
8055
8143
  } catch {
8056
8144
  }
8057
8145
  try {
8058
- execFileSync14("pulumi", ["stack", "select", stack], {
8146
+ execFileSync15("pulumi", ["stack", "select", stack], {
8059
8147
  cwd: appRoot,
8060
8148
  encoding: "utf-8",
8061
8149
  stdio: ["ignore", "pipe", "ignore"]
@@ -8084,7 +8172,7 @@ function getDevOutput(appRoot, stack, awsEnv) {
8084
8172
  }
8085
8173
  function hasTmux() {
8086
8174
  try {
8087
- execFileSync14("which", ["tmux"], { stdio: "ignore" });
8175
+ execFileSync15("which", ["tmux"], { stdio: "ignore" });
8088
8176
  return true;
8089
8177
  } catch {
8090
8178
  return false;
@@ -8092,7 +8180,7 @@ function hasTmux() {
8092
8180
  }
8093
8181
  function sessionExists(name) {
8094
8182
  try {
8095
- execFileSync14("tmux", ["has-session", "-t", name], { stdio: "ignore" });
8183
+ execFileSync15("tmux", ["has-session", "-t", name], { stdio: "ignore" });
8096
8184
  return true;
8097
8185
  } catch {
8098
8186
  return false;
@@ -8100,7 +8188,7 @@ function sessionExists(name) {
8100
8188
  }
8101
8189
  function killSession(name) {
8102
8190
  try {
8103
- execFileSync14("tmux", ["kill-session", "-t", name], { stdio: "ignore" });
8191
+ execFileSync15("tmux", ["kill-session", "-t", name], { stdio: "ignore" });
8104
8192
  return true;
8105
8193
  } catch {
8106
8194
  return false;
@@ -8240,7 +8328,7 @@ function getServiceEnvVars(service, tunnels) {
8240
8328
  }
8241
8329
  function setTmuxEnv(sessionName, vars) {
8242
8330
  for (const [key, value] of Object.entries(vars)) {
8243
- execFileSync14("tmux", ["set-environment", "-t", sessionName, key, value], { stdio: "ignore" });
8331
+ execFileSync15("tmux", ["set-environment", "-t", sessionName, key, value], { stdio: "ignore" });
8244
8332
  }
8245
8333
  }
8246
8334
  function envPrefix(vars) {
@@ -8281,7 +8369,7 @@ function resolveTemporalEncodingKey(tenant, env, appName) {
8281
8369
  const namespace = `${tenant}-${env}-${appName}`;
8282
8370
  const secretName = `${namespace}-temporal-encoding-key`;
8283
8371
  try {
8284
- const b64 = execFileSync14(
8372
+ const b64 = execFileSync15(
8285
8373
  "kubectl",
8286
8374
  [
8287
8375
  "get",
@@ -8486,7 +8574,7 @@ async function allocateSsmLocalPort(remotePort, reservedPorts) {
8486
8574
  }
8487
8575
  function hasSessionManagerPlugin() {
8488
8576
  try {
8489
- execFileSync14("which", ["session-manager-plugin"], { stdio: "ignore" });
8577
+ execFileSync15("which", ["session-manager-plugin"], { stdio: "ignore" });
8490
8578
  return true;
8491
8579
  } catch {
8492
8580
  return false;
@@ -8585,13 +8673,13 @@ async function startSsmTunnels(sessionName, devOutput) {
8585
8673
  localPortNumber: [String(localPort)]
8586
8674
  })}'`
8587
8675
  ].join(" ");
8588
- execFileSync14("tmux", ["new-window", "-t", sessionName, "-n", windowName]);
8589
- execFileSync14(
8676
+ execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", windowName]);
8677
+ execFileSync15(
8590
8678
  "tmux",
8591
8679
  ["set-option", "-t", `${sessionName}:${windowName}`, "remain-on-exit", "on"],
8592
8680
  { stdio: "ignore" }
8593
8681
  );
8594
- execFileSync14("tmux", ["send-keys", "-t", `${sessionName}:${windowName}`, ssmCmd, "Enter"]);
8682
+ execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:${windowName}`, ssmCmd, "Enter"]);
8595
8683
  rewritten.tunnels[tunnelName] = {
8596
8684
  host: "localhost",
8597
8685
  port: localPort
@@ -8635,7 +8723,7 @@ async function updateDevboxProxy(devOutput, appRoot) {
8635
8723
  lines.push("");
8636
8724
  }
8637
8725
  const caddyfile = lines.join("\n");
8638
- execFileSync14(
8726
+ execFileSync15(
8639
8727
  "docker",
8640
8728
  [
8641
8729
  "run",
@@ -8651,7 +8739,7 @@ CADDYEOF`
8651
8739
  ],
8652
8740
  { stdio: "pipe" }
8653
8741
  );
8654
- execFileSync14(
8742
+ execFileSync15(
8655
8743
  "docker",
8656
8744
  [
8657
8745
  "run",
@@ -8722,13 +8810,13 @@ async function setupSubdomainRouting(devOutput, appRoot) {
8722
8810
  fs19.writeFileSync(caddyfile, caddyLines.join("\n"));
8723
8811
  let caddyRunning = false;
8724
8812
  try {
8725
- execFileSync14("pgrep", ["-f", "caddy run.*mesh-dev-caddy"], { stdio: "pipe" });
8813
+ execFileSync15("pgrep", ["-f", "caddy run.*mesh-dev-caddy"], { stdio: "pipe" });
8726
8814
  caddyRunning = true;
8727
8815
  } catch {
8728
8816
  }
8729
8817
  if (caddyRunning) {
8730
8818
  try {
8731
- execFileSync14("caddy", ["reload", "--config", caddyfile, "--adapter", "caddyfile"], {
8819
+ execFileSync15("caddy", ["reload", "--config", caddyfile, "--adapter", "caddyfile"], {
8732
8820
  stdio: "pipe"
8733
8821
  });
8734
8822
  } catch {
@@ -8737,7 +8825,7 @@ async function setupSubdomainRouting(devOutput, appRoot) {
8737
8825
  }
8738
8826
  } else {
8739
8827
  try {
8740
- execFileSync14("which", ["caddy"], { stdio: "pipe" });
8828
+ execFileSync15("which", ["caddy"], { stdio: "pipe" });
8741
8829
  } catch {
8742
8830
  logWarn(
8743
8831
  "Caddy not found. Install for subdomain routing: curl -fsSL https://caddyserver.com/api/download?os=linux&arch=arm64 -o /usr/local/bin/caddy && chmod +x /usr/local/bin/caddy"
@@ -8779,7 +8867,7 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
8779
8867
  return devOutput;
8780
8868
  }
8781
8869
  logInfo(`Creating tmux session: ${sessionName}`);
8782
- execFileSync14("tmux", ["new-session", "-d", "-s", sessionName, "-n", "status", "-c", appRoot]);
8870
+ execFileSync15("tmux", ["new-session", "-d", "-s", sessionName, "-n", "status", "-c", appRoot]);
8783
8871
  setTmuxEnv(sessionName, awsEnv);
8784
8872
  const hasTunnels = Object.keys(devOutput.tunnels).length > 0;
8785
8873
  let effectiveTransport = tunnelPlan.transport;
@@ -8890,13 +8978,13 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
8890
8978
  const tokenPort = await findFreePort3();
8891
8979
  const tokenUrl = `http://127.0.0.1:${tokenPort}`;
8892
8980
  setTmuxEnv(sessionName, { DEV_USER_TOKEN_URL: tokenUrl });
8893
- execFileSync14("tmux", ["new-window", "-t", sessionName, "-n", "token-server"]);
8894
- execFileSync14(
8981
+ execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", "token-server"]);
8982
+ execFileSync15(
8895
8983
  "tmux",
8896
8984
  ["set-option", "-t", `${sessionName}:token-server`, "remain-on-exit", "on"],
8897
8985
  { stdio: "ignore" }
8898
8986
  );
8899
- execFileSync14("tmux", [
8987
+ execFileSync15("tmux", [
8900
8988
  "send-keys",
8901
8989
  "-t",
8902
8990
  `${sessionName}:token-server`,
@@ -8972,11 +9060,11 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
8972
9060
  cmd,
8973
9061
  service.env?.OTEL_RESOURCE_ATTRIBUTES ? logShipperPath() : void 0
8974
9062
  );
8975
- execFileSync14("tmux", ["new-window", "-t", sessionName, "-n", name, "-c", serviceDir]);
8976
- execFileSync14("tmux", ["set-option", "-t", `${sessionName}:${name}`, "remain-on-exit", "on"], {
9063
+ execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", name, "-c", serviceDir]);
9064
+ execFileSync15("tmux", ["set-option", "-t", `${sessionName}:${name}`, "remain-on-exit", "on"], {
8977
9065
  stdio: "ignore"
8978
9066
  });
8979
- execFileSync14("tmux", ["send-keys", "-t", `${sessionName}:${name}`, launchCmd, "Enter"]);
9067
+ execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:${name}`, launchCmd, "Enter"]);
8980
9068
  logSuccess(`Started: ${name} (${serviceDir}, port ${service.port})`);
8981
9069
  }
8982
9070
  if (process.env.DEVCONTAINER === "1") {
@@ -8987,7 +9075,7 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
8987
9075
  }
8988
9076
  }
8989
9077
  const statusCmd = `watch -n2 -t npx mesh dev --status --session '${sessionName}'`;
8990
- execFileSync14("tmux", ["send-keys", "-t", `${sessionName}:status`, statusCmd, "Enter"]);
9078
+ execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:status`, statusCmd, "Enter"]);
8991
9079
  console.log("");
8992
9080
  logSuccess(`Dev session started: ${sessionName}`);
8993
9081
  console.log("");
@@ -9044,7 +9132,7 @@ async function showStatus2(sessionName, devOutput, asJson) {
9044
9132
  }
9045
9133
  let windows = [];
9046
9134
  try {
9047
- const raw = execFileSync14(
9135
+ const raw = execFileSync15(
9048
9136
  "tmux",
9049
9137
  ["list-windows", "-t", sessionName, "-F", "#{window_name} #{pane_dead}"],
9050
9138
  { encoding: "utf-8" }
@@ -9166,7 +9254,7 @@ async function restartService(sessionName, serviceName, appRoot, devOutput, awsE
9166
9254
  }
9167
9255
  const target = `${sessionName}:${serviceName}`;
9168
9256
  try {
9169
- execFileSync14("tmux", ["respawn-pane", "-k", "-t", target], { stdio: "ignore" });
9257
+ execFileSync15("tmux", ["respawn-pane", "-k", "-t", target], { stdio: "ignore" });
9170
9258
  } catch {
9171
9259
  logError(`Window '${serviceName}' not found in session.`);
9172
9260
  process.exit(1);
@@ -9191,7 +9279,7 @@ async function restartService(sessionName, serviceName, appRoot, devOutput, awsE
9191
9279
  const serviceVars = getServiceEnvVars(service, devOutput.tunnels);
9192
9280
  restartCmd = `${envPrefix(serviceVars)}${cmd}`;
9193
9281
  }
9194
- execFileSync14("tmux", ["send-keys", "-t", target, restartCmd, "Enter"]);
9282
+ execFileSync15("tmux", ["send-keys", "-t", target, restartCmd, "Enter"]);
9195
9283
  logSuccess(`Restarted: ${serviceName}`);
9196
9284
  }
9197
9285
  function showLogs(sessionName, serviceName, tail) {
@@ -9201,7 +9289,7 @@ function showLogs(sessionName, serviceName, tail) {
9201
9289
  }
9202
9290
  const target = `${sessionName}:${serviceName}`;
9203
9291
  try {
9204
- const result = execFileSync14("tmux", ["capture-pane", "-t", target, "-p", "-S", `-${tail}`], {
9292
+ const result = execFileSync15("tmux", ["capture-pane", "-t", target, "-p", "-S", `-${tail}`], {
9205
9293
  encoding: "utf-8"
9206
9294
  });
9207
9295
  process.stdout.write(result);
@@ -10084,7 +10172,7 @@ var init_dev = __esm({
10084
10172
  });
10085
10173
 
10086
10174
  // libs/mesh-cli/src/commands/dev-doctor.ts
10087
- import { execFileSync as execFileSync15 } from "node:child_process";
10175
+ import { execFileSync as execFileSync16 } from "node:child_process";
10088
10176
  import * as fs20 from "node:fs";
10089
10177
  import * as net9 from "node:net";
10090
10178
  import * as path21 from "node:path";
@@ -10221,7 +10309,7 @@ function collectSessionPids(panePids, psOut) {
10221
10309
  }
10222
10310
  function whoHasPort(port) {
10223
10311
  try {
10224
- const lsof = execFileSync15("lsof", ["-ti", `:${port}`], {
10312
+ const lsof = execFileSync16("lsof", ["-ti", `:${port}`], {
10225
10313
  encoding: "utf8",
10226
10314
  stdio: ["ignore", "pipe", "ignore"]
10227
10315
  });
@@ -10229,7 +10317,7 @@ function whoHasPort(port) {
10229
10317
  if (!pid) return null;
10230
10318
  let ps = "";
10231
10319
  try {
10232
- ps = execFileSync15("ps", ["-o", "comm=", "-p", pid], {
10320
+ ps = execFileSync16("ps", ["-o", "comm=", "-p", pid], {
10233
10321
  encoding: "utf8",
10234
10322
  stdio: ["ignore", "pipe", "ignore"]
10235
10323
  });
@@ -10243,7 +10331,7 @@ function whoHasPort(port) {
10243
10331
  function sessionProcessTree(sessionName) {
10244
10332
  let panePids;
10245
10333
  try {
10246
- const out = execFileSync15(
10334
+ const out = execFileSync16(
10247
10335
  "tmux",
10248
10336
  ["list-panes", "-s", "-t", sessionName, "-F", "#{pane_pid}"],
10249
10337
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
@@ -10255,7 +10343,7 @@ function sessionProcessTree(sessionName) {
10255
10343
  if (panePids.length === 0) return /* @__PURE__ */ new Set();
10256
10344
  let psOut = "";
10257
10345
  try {
10258
- psOut = execFileSync15("ps", ["-eo", "pid=,ppid="], {
10346
+ psOut = execFileSync16("ps", ["-eo", "pid=,ppid="], {
10259
10347
  encoding: "utf8",
10260
10348
  stdio: ["ignore", "pipe", "ignore"]
10261
10349
  });
@@ -10377,7 +10465,7 @@ var init_dev_doctor = __esm({
10377
10465
  phases: ["preflight", "ondemand"],
10378
10466
  async run() {
10379
10467
  try {
10380
- execFileSync15("which", ["tmux"], { stdio: "ignore" });
10468
+ execFileSync16("which", ["tmux"], { stdio: "ignore" });
10381
10469
  return { status: "ok", summary: "installed" };
10382
10470
  } catch {
10383
10471
  return {
@@ -10482,7 +10570,7 @@ var init_dev_doctor = __esm({
10482
10570
  }
10483
10571
  let repoRoot2;
10484
10572
  try {
10485
- repoRoot2 = execFileSync15("git", ["rev-parse", "--show-toplevel"], {
10573
+ repoRoot2 = execFileSync16("git", ["rev-parse", "--show-toplevel"], {
10486
10574
  cwd: ctx.appRoot,
10487
10575
  encoding: "utf8",
10488
10576
  stdio: ["ignore", "pipe", "ignore"]
@@ -12229,7 +12317,7 @@ var init_conversations = __esm({
12229
12317
  });
12230
12318
 
12231
12319
  // libs/mesh-cli/src/utils/scaffold-versions.ts
12232
- import { execFileSync as execFileSync16 } from "child_process";
12320
+ import { execFileSync as execFileSync17 } from "child_process";
12233
12321
  function isPrerelease(range) {
12234
12322
  return /[-+]/.test(range.replace(/^\^/, ""));
12235
12323
  }
@@ -12239,7 +12327,7 @@ function toCaretRange(version) {
12239
12327
  }
12240
12328
  function resolvePublishedRange(pkg, cwd) {
12241
12329
  try {
12242
- const out = execFileSync16("npm", ["view", pkg, "version"], {
12330
+ const out = execFileSync17("npm", ["view", pkg, "version"], {
12243
12331
  cwd,
12244
12332
  encoding: "utf-8",
12245
12333
  timeout: 15e3,
@@ -12273,6 +12361,7 @@ __export(create_app_exports, {
12273
12361
  NO_REGISTRY_ACCESS_MESSAGE: () => NO_REGISTRY_ACCESS_MESSAGE,
12274
12362
  PLATFORM_MONOREPO_NAME: () => PLATFORM_MONOREPO_NAME,
12275
12363
  bootstrapAppsRepo: () => bootstrapAppsRepo,
12364
+ composableNextSteps: () => composableNextSteps,
12276
12365
  copyTemplate: () => copyTemplate,
12277
12366
  describeNoAppsHome: () => describeNoAppsHome,
12278
12367
  ensureRegistryAccess: () => ensureRegistryAccess,
@@ -12786,15 +12875,30 @@ function printComposableNextSteps(appDir, context) {
12786
12875
  console.log(" api/src/storage.ts S3 helpers");
12787
12876
  }
12788
12877
  console.log("");
12789
- console.log("Next steps:");
12790
- console.log(` cd ${appDir}`);
12791
- console.log(" pnpm install");
12792
- console.log(" mesh skills sync # re-run once deps are installed: picks up package skills + Intent");
12793
- console.log(" pnpm install # again only if skills sync reports it added @tanstack/intent");
12794
- console.log(" mesh stack init # personal dev stack (deploy: false)");
12795
- console.log(" mesh deploy up --yes # deploy via the stack's deployer role");
12796
- console.log(" mesh dev # run locally");
12797
- console.log("");
12878
+ for (const line of composableNextSteps(appDir, findMeshJson(process.cwd())?.data.platform ?? null)) {
12879
+ console.log(line);
12880
+ }
12881
+ }
12882
+ function composableNextSteps(appDir, platform) {
12883
+ const common = [
12884
+ "Next steps:",
12885
+ ` cd ${appDir}`,
12886
+ " pnpm install",
12887
+ " mesh skills sync # re-run once deps are installed: picks up package skills + Intent",
12888
+ " pnpm install # again only if skills sync reports it added @tanstack/intent"
12889
+ ];
12890
+ const local = [
12891
+ " mesh start # the local Mesh platform (once; Docker \u2014 from anywhere)",
12892
+ " mesh dev # run the app against it"
12893
+ ];
12894
+ const deploy = [
12895
+ " mesh stack init # personal dev stack (deploy: false)",
12896
+ " mesh deploy up --yes # deploy via the stack's deployer role"
12897
+ ];
12898
+ if (platform === null || platform === "local") {
12899
+ return [...common, ...local, "", "When you deploy to a Mesh platform:", ...deploy];
12900
+ }
12901
+ return [...common, ...deploy, " mesh dev # run locally against the platform"];
12798
12902
  }
12799
12903
  var __filename, __dirname, packageRoot, TEMPLATES, REMOVED_TEMPLATES, PRIMITIVES, HUB_ACCOUNTS, PLATFORM_MONOREPO_NAME, VALID_PRIMITIVES, NO_REGISTRY_ACCESS_MESSAGE;
12800
12904
  var init_create_app = __esm({
@@ -13558,7 +13662,7 @@ var init_deploy_preflight = __esm({
13558
13662
  });
13559
13663
 
13560
13664
  // libs/mesh-cli/src/commands/deploy.ts
13561
- import { execFileSync as execFileSync17 } from "child_process";
13665
+ import { execFileSync as execFileSync18 } from "child_process";
13562
13666
  function buildPulumiArgs(pulumiArgs, stack) {
13563
13667
  const base = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? ["up", ...pulumiArgs] : [...pulumiArgs];
13564
13668
  const op = base[0];
@@ -13621,7 +13725,7 @@ function registerDeployCommand(program2) {
13621
13725
  delete env.AWS_PROFILE;
13622
13726
  const finalArgs = buildPulumiArgs(pulumiArgs, stack);
13623
13727
  try {
13624
- execFileSync17("pulumi", finalArgs, {
13728
+ execFileSync18("pulumi", finalArgs, {
13625
13729
  cwd: appRoot,
13626
13730
  env,
13627
13731
  stdio: "inherit"
@@ -13914,7 +14018,7 @@ var init_discover = __esm({
13914
14018
  });
13915
14019
 
13916
14020
  // libs/mesh-cli/src/docs/assemble.ts
13917
- import { execFileSync as execFileSync18 } from "node:child_process";
14021
+ import { execFileSync as execFileSync19 } from "node:child_process";
13918
14022
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync15 } from "node:fs";
13919
14023
  import path28 from "node:path";
13920
14024
  import { parse as parseYaml4 } from "yaml";
@@ -14405,7 +14509,7 @@ function renderVersionJson(args) {
14405
14509
  }
14406
14510
  function currentCommit(repoRoot2) {
14407
14511
  try {
14408
- return execFileSync18("git", ["rev-parse", "--short", "HEAD"], {
14512
+ return execFileSync19("git", ["rev-parse", "--short", "HEAD"], {
14409
14513
  cwd: repoRoot2,
14410
14514
  encoding: "utf-8"
14411
14515
  }).trim();
@@ -14424,7 +14528,7 @@ function currentBaseline(repoRoot2) {
14424
14528
  }
14425
14529
  }
14426
14530
  function publishSetAtRef(repoRoot2, ref) {
14427
- const git = (gitArgs) => execFileSync18("git", gitArgs, {
14531
+ const git = (gitArgs) => execFileSync19("git", gitArgs, {
14428
14532
  cwd: repoRoot2,
14429
14533
  encoding: "utf-8",
14430
14534
  maxBuffer: 64 * 1024 * 1024
@@ -14912,7 +15016,7 @@ var init_portal = __esm({
14912
15016
  });
14913
15017
 
14914
15018
  // libs/mesh-cli/src/utils/build-info.ts
14915
- import { execFileSync as execFileSync19 } from "child_process";
15019
+ import { execFileSync as execFileSync20 } from "child_process";
14916
15020
  import * as fs25 from "fs";
14917
15021
  import * as path30 from "path";
14918
15022
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -14970,7 +15074,7 @@ function resolveCliRuntime(opts) {
14970
15074
  }
14971
15075
  if (!vcs) return info;
14972
15076
  try {
14973
- const git = (args) => execFileSync19("git", args, {
15077
+ const git = (args) => execFileSync20("git", args, {
14974
15078
  cwd: root,
14975
15079
  encoding: "utf-8",
14976
15080
  stdio: ["ignore", "pipe", "pipe"]
@@ -15067,7 +15171,7 @@ var init_serve = __esm({
15067
15171
  });
15068
15172
 
15069
15173
  // libs/mesh-cli/src/docs/registry-docs.ts
15070
- import { execFileSync as execFileSync20 } from "node:child_process";
15174
+ import { execFileSync as execFileSync21 } from "node:child_process";
15071
15175
  import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync26, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
15072
15176
  import { tmpdir as tmpdir7 } from "node:os";
15073
15177
  import path31 from "node:path";
@@ -15159,7 +15263,7 @@ async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fet
15159
15263
  rmSync6(staging, { recursive: true, force: true });
15160
15264
  mkdirSync13(staging, { recursive: true });
15161
15265
  try {
15162
- execFileSync20("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
15266
+ execFileSync21("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
15163
15267
  stdio: ["pipe", "pipe", "pipe"]
15164
15268
  });
15165
15269
  } finally {
@@ -15197,7 +15301,7 @@ __export(start_exports, {
15197
15301
  tmuxInstallHint: () => tmuxInstallHint,
15198
15302
  tmuxServeArgs: () => tmuxServeArgs
15199
15303
  });
15200
- import { execFileSync as execFileSync21 } from "node:child_process";
15304
+ import { execFileSync as execFileSync22 } from "node:child_process";
15201
15305
  import { appendFileSync as appendFileSync2, existsSync as existsSync24, writeFileSync as writeFileSync18 } from "node:fs";
15202
15306
  import path32 from "node:path";
15203
15307
  function registryAuthOrThrow() {
@@ -15230,7 +15334,7 @@ function tmuxInstallHint(platform = process.platform) {
15230
15334
  }
15231
15335
  function tmuxAvailable() {
15232
15336
  try {
15233
- execFileSync21("tmux", ["-V"], { stdio: ["pipe", "pipe", "pipe"] });
15337
+ execFileSync22("tmux", ["-V"], { stdio: ["pipe", "pipe", "pipe"] });
15234
15338
  return true;
15235
15339
  } catch {
15236
15340
  return false;
@@ -15241,7 +15345,7 @@ function shouldDetach(args) {
15241
15345
  }
15242
15346
  function docsSessionExists() {
15243
15347
  try {
15244
- execFileSync21("tmux", ["has-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15348
+ execFileSync22("tmux", ["has-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15245
15349
  return true;
15246
15350
  } catch {
15247
15351
  return false;
@@ -15265,12 +15369,12 @@ Or run in the foreground instead: mesh docs start --foreground`
15265
15369
  if (docsSessionExists()) {
15266
15370
  logInfo(`Replacing the docs server already running in tmux session "${DOCS_TMUX_SESSION}".`);
15267
15371
  try {
15268
- execFileSync21("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15372
+ execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15269
15373
  } catch {
15270
15374
  }
15271
15375
  }
15272
15376
  const { command, cwd } = tmuxServeArgs(args);
15273
- execFileSync21(
15377
+ execFileSync22(
15274
15378
  "tmux",
15275
15379
  ["new-session", "-d", "-s", DOCS_TMUX_SESSION, "-c", cwd, "--", ...command],
15276
15380
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -15308,7 +15412,7 @@ function printReady(url, label) {
15308
15412
  }
15309
15413
  function runDocsStop() {
15310
15414
  try {
15311
- execFileSync21("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15415
+ execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
15312
15416
  logSuccess(`Stopped the docs server (tmux session "${DOCS_TMUX_SESSION}").`);
15313
15417
  } catch {
15314
15418
  logInfo(`No docs server is running (no tmux session named "${DOCS_TMUX_SESSION}").`);
@@ -15681,7 +15785,7 @@ var init_docs = __esm({
15681
15785
  });
15682
15786
 
15683
15787
  // libs/mesh-cli/src/commands/hub/index.ts
15684
- import { execFileSync as execFileSync22 } from "node:child_process";
15788
+ import { execFileSync as execFileSync23 } from "node:child_process";
15685
15789
  import * as fs27 from "node:fs";
15686
15790
  import * as net12 from "node:net";
15687
15791
  import * as os9 from "node:os";
@@ -15747,7 +15851,7 @@ function parseTmuxEnv(output) {
15747
15851
  }
15748
15852
  function readTmuxSessionEnv(sessionName) {
15749
15853
  try {
15750
- const out = execFileSync22("tmux", ["show-environment", "-t", sessionName], {
15854
+ const out = execFileSync23("tmux", ["show-environment", "-t", sessionName], {
15751
15855
  encoding: "utf-8",
15752
15856
  stdio: ["ignore", "pipe", "ignore"]
15753
15857
  });
@@ -15758,7 +15862,7 @@ function readTmuxSessionEnv(sessionName) {
15758
15862
  }
15759
15863
  function tmuxSessionExists(sessionName) {
15760
15864
  try {
15761
- execFileSync22("tmux", ["has-session", "-t", sessionName], { stdio: "ignore" });
15865
+ execFileSync23("tmux", ["has-session", "-t", sessionName], { stdio: "ignore" });
15762
15866
  return true;
15763
15867
  } catch {
15764
15868
  return false;
@@ -15927,7 +16031,7 @@ function redactEnv(env) {
15927
16031
  async function hubDevAction(opts) {
15928
16032
  if (opts.kill) {
15929
16033
  if (tmuxSessionExists(HUB_SESSION)) {
15930
- execFileSync22("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
16034
+ execFileSync23("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
15931
16035
  fs27.rmSync(hubEnvDir(), { recursive: true, force: true });
15932
16036
  logSuccess(`Killed Hub session '${HUB_SESSION}'.`);
15933
16037
  } else {
@@ -15981,17 +16085,17 @@ async function hubDevAction(opts) {
15981
16085
  for (const warning of assembled.warnings) logWarn(warning);
15982
16086
  const apiDir = path34.join(platformDir, "apps", "hub", "api");
15983
16087
  const uiDir = path34.join(platformDir, "apps", "hub", "ui");
15984
- execFileSync22("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
16088
+ execFileSync23("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
15985
16089
  if (!assembled.apiEnv.DEV_USER_TOKEN_URL && assembled.apiEnv.DEV_USER_ID_TOKEN) {
15986
16090
  const platform = session.state.devOutput.platform;
15987
16091
  const credContext = `mesh.${platform.env}`;
15988
16092
  const tokenPort = await findFreePort4();
15989
16093
  const tokenUrl = `http://127.0.0.1:${tokenPort}`;
15990
- execFileSync22("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
15991
- execFileSync22("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
16094
+ execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
16095
+ execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
15992
16096
  stdio: "ignore"
15993
16097
  });
15994
- execFileSync22("tmux", [
16098
+ execFileSync23("tmux", [
15995
16099
  "send-keys",
15996
16100
  "-t",
15997
16101
  `${HUB_SESSION}:token-server`,
@@ -16009,12 +16113,12 @@ async function hubDevAction(opts) {
16009
16113
  const envFile = path34.join(hubEnvDir(), `${window}.env.sh`);
16010
16114
  writeEnvFile(envFile, env);
16011
16115
  if (createWindow) {
16012
- execFileSync22("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
16116
+ execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
16013
16117
  }
16014
- execFileSync22("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
16118
+ execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
16015
16119
  stdio: "ignore"
16016
16120
  });
16017
- execFileSync22("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
16121
+ execFileSync23("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
16018
16122
  };
16019
16123
  launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
16020
16124
  launch("ui", uiDir, assembled.uiEnv, `pnpm dev --port ${uiPort} --strictPort`, true);
@@ -16093,7 +16197,7 @@ var init_hub = __esm({
16093
16197
  });
16094
16198
 
16095
16199
  // libs/mesh-cli/src/commands/registry-publish.ts
16096
- import { execFileSync as execFileSync23, execSync } from "child_process";
16200
+ import { execFileSync as execFileSync24, execSync } from "child_process";
16097
16201
  import * as fs28 from "fs";
16098
16202
  import * as path35 from "path";
16099
16203
  function repoRoot() {
@@ -16177,14 +16281,14 @@ function registerRegistryPublish(registry) {
16177
16281
  ...opts.dryRun ? ["--dry-run"] : []
16178
16282
  ];
16179
16283
  logInfo(`Publishing ${selected.length} package(s)...`);
16180
- execFileSync23("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
16284
+ execFileSync24("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
16181
16285
  } finally {
16182
16286
  for (const b of backups) fs28.writeFileSync(b.file, b.content);
16183
16287
  logInfo("Restored source package.json versions.");
16184
16288
  const collateral = [...dirtyPackageJsons(root)].filter((f) => !preDirty.has(f));
16185
16289
  if (collateral.length) {
16186
16290
  try {
16187
- execFileSync23("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
16291
+ execFileSync24("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
16188
16292
  logInfo(`Reverted ${collateral.length} package.json file(s) re-vendored by build:publish.`);
16189
16293
  } catch {
16190
16294
  logError(
@@ -16236,12 +16340,12 @@ __export(registry_exports, {
16236
16340
  shouldRemintAfter: () => shouldRemintAfter,
16237
16341
  shouldRepairUserNpmrc: () => shouldRepairUserNpmrc
16238
16342
  });
16239
- import { execFileSync as execFileSync24 } from "child_process";
16343
+ import { execFileSync as execFileSync25 } from "child_process";
16240
16344
  import * as fs29 from "fs";
16241
16345
  import * as path36 from "path";
16242
16346
  function getEndpoint(env) {
16243
16347
  try {
16244
- const result = execFileSync24("aws", [
16348
+ const result = execFileSync25("aws", [
16245
16349
  "codeartifact",
16246
16350
  "get-repository-endpoint",
16247
16351
  "--domain",
@@ -16268,7 +16372,7 @@ function getEndpoint(env) {
16268
16372
  }
16269
16373
  function codeartifactLogin(env) {
16270
16374
  try {
16271
- execFileSync24("aws", [
16375
+ execFileSync25("aws", [
16272
16376
  "codeartifact",
16273
16377
  "login",
16274
16378
  "--tool",
@@ -16323,7 +16427,7 @@ function resolvePublisherRoleArn({
16323
16427
  }
16324
16428
  function assumeRole2(roleArn) {
16325
16429
  try {
16326
- const result = execFileSync24("aws", [
16430
+ const result = execFileSync25("aws", [
16327
16431
  "sts",
16328
16432
  "assume-role",
16329
16433
  "--role-arn",
@@ -16345,7 +16449,7 @@ function assumeRole2(roleArn) {
16345
16449
  }
16346
16450
  function assumeRoleWithWebIdentity2(roleArn, idToken, sessionName) {
16347
16451
  try {
16348
- const result = execFileSync24("aws", [
16452
+ const result = execFileSync25("aws", [
16349
16453
  "sts",
16350
16454
  "assume-role-with-web-identity",
16351
16455
  "--role-arn",
@@ -16681,7 +16785,7 @@ region = us-east-2`,
16681
16785
  { remediation: { command: `aws configure sso --profile ${profile}` } }
16682
16786
  );
16683
16787
  }
16684
- const probe = () => execFileSync24("aws", ["sts", "get-caller-identity", "--profile", profile], {
16788
+ const probe = () => execFileSync25("aws", ["sts", "get-caller-identity", "--profile", profile], {
16685
16789
  stdio: ["ignore", "pipe", "pipe"],
16686
16790
  timeout: 2e4
16687
16791
  });
@@ -16692,7 +16796,7 @@ region = us-east-2`,
16692
16796
  } catch {
16693
16797
  logInfo(`AWS SSO session for profile '${profile}' is stale \u2014 opening browser login\u2026`);
16694
16798
  }
16695
- execFileSync24("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
16799
+ execFileSync25("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
16696
16800
  try {
16697
16801
  probe();
16698
16802
  } catch (err) {
@@ -16922,7 +17026,7 @@ __export(wizard_exports, {
16922
17026
  });
16923
17027
  import * as fs30 from "fs";
16924
17028
  import * as path37 from "path";
16925
- import { execFileSync as execFileSync25 } from "child_process";
17029
+ import { execFileSync as execFileSync26 } from "child_process";
16926
17030
  import chalk4 from "chalk";
16927
17031
  function wizardExitCode(steps) {
16928
17032
  return steps.some((s) => s.status === "fail") ? 1 : 0;
@@ -16989,7 +17093,7 @@ function classifyRepo(cwd) {
16989
17093
  return { kind: "other-repo", root };
16990
17094
  }
16991
17095
  function gitInit(dir) {
16992
- execFileSync25("git", ["init", "-q"], { cwd: dir, stdio: ["ignore", "ignore", "pipe"] });
17096
+ execFileSync26("git", ["init", "-q"], { cwd: dir, stdio: ["ignore", "ignore", "pipe"] });
16993
17097
  }
16994
17098
  async function runWizardSteps(ctx, steps) {
16995
17099
  const results = [];
@@ -17584,7 +17688,7 @@ var init_init = __esm({
17584
17688
  });
17585
17689
 
17586
17690
  // libs/mesh-cli/src/commands/install-shim.ts
17587
- import { execFileSync as execFileSync26 } from "child_process";
17691
+ import { execFileSync as execFileSync27 } from "child_process";
17588
17692
  import * as fs32 from "fs";
17589
17693
  import * as os10 from "os";
17590
17694
  import * as path39 from "path";
@@ -17697,7 +17801,7 @@ function registerInstallShimCommand(program2) {
17697
17801
  let status = 0;
17698
17802
  let ok = true;
17699
17803
  try {
17700
- stdout = execFileSync26(target, ["--help"], {
17804
+ stdout = execFileSync27(target, ["--help"], {
17701
17805
  cwd: process.cwd(),
17702
17806
  encoding: "utf8",
17703
17807
  stdio: ["ignore", "pipe", "pipe"]
@@ -17744,7 +17848,7 @@ var init_install_shim = __esm({
17744
17848
  });
17745
17849
 
17746
17850
  // libs/mesh-cli/src/commands/local/hub-local.ts
17747
- import { execFile as execFile3, execFileSync as execFileSync27 } from "child_process";
17851
+ import { execFile as execFile3, execFileSync as execFileSync28 } from "child_process";
17748
17852
  import * as fs33 from "fs";
17749
17853
  import * as os11 from "os";
17750
17854
  import * as path40 from "path";
@@ -17764,7 +17868,7 @@ function npmrcPath() {
17764
17868
  }
17765
17869
  function imageExists(tag) {
17766
17870
  try {
17767
- execFileSync27("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
17871
+ execFileSync28("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
17768
17872
  return true;
17769
17873
  } catch {
17770
17874
  return false;
@@ -17774,7 +17878,7 @@ function ensureHubAuthImage() {
17774
17878
  if (imageExists(HUB_AUTH_IMAGE)) return;
17775
17879
  logInfo(`Building ${HUB_AUTH_IMAGE} (Hub auth proxy)\u2026`);
17776
17880
  const hubStackDir = path40.join(findPackageRoot(), "stack", "hub");
17777
- execFileSync27(
17881
+ execFileSync28(
17778
17882
  "docker",
17779
17883
  ["build", "-f", path40.join(hubStackDir, "Dockerfile.auth"), "-t", HUB_AUTH_IMAGE, hubStackDir],
17780
17884
  { stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
@@ -17788,7 +17892,7 @@ function hasRegistryAuth() {
17788
17892
  function localHubVersion() {
17789
17893
  const versions = HUB_IMAGES.map((name) => {
17790
17894
  try {
17791
- const out = execFileSync27("docker", ["images", name, "--format", "{{.Tag}}"], {
17895
+ const out = execFileSync28("docker", ["images", name, "--format", "{{.Tag}}"], {
17792
17896
  encoding: "utf-8",
17793
17897
  stdio: ["ignore", "pipe", "pipe"]
17794
17898
  });
@@ -17867,7 +17971,7 @@ async function ensureHubImages() {
17867
17971
  const context = path40.join(cacheDir(), `context-${version}`);
17868
17972
  fs33.rmSync(context, { recursive: true, force: true });
17869
17973
  fs33.mkdirSync(context, { recursive: true });
17870
- execFileSync27("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
17974
+ execFileSync28("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
17871
17975
  stdio: ["ignore", "pipe", "pipe"]
17872
17976
  });
17873
17977
  const hubStackDir = path40.join(findPackageRoot(), "stack", "hub");
@@ -17878,7 +17982,7 @@ async function ensureHubImages() {
17878
17982
  const tag = `${name}:${version}`;
17879
17983
  if (imageExists(tag)) continue;
17880
17984
  logInfo(`Building ${tag} from the published tarball\u2026`);
17881
- execFileSync27(
17985
+ execFileSync28(
17882
17986
  "docker",
17883
17987
  [
17884
17988
  "build",
@@ -17908,7 +18012,7 @@ function readHubCompiledAuthz(version) {
17908
18012
  if (!tarball || !fs33.existsSync(path40.join(cacheDir(), tarball))) return null;
17909
18013
  fs33.mkdirSync(context, { recursive: true });
17910
18014
  try {
17911
- execFileSync27(
18015
+ execFileSync28(
17912
18016
  "tar",
17913
18017
  ["-xzf", path40.join(cacheDir(), tarball), "-C", context, "--strip-components", "1", `package/${HUB_COMPILED_AUTHZ_REL}`],
17914
18018
  { stdio: ["ignore", "pipe", "pipe"] }
@@ -18002,7 +18106,7 @@ async function buildHubImagesFromSource(repoRoot2) {
18002
18106
  await execFileAsync2("pnpm", ["pack", "--pack-destination", context], { cwd: hubDir, maxBuffer: 64 * 1024 * 1024 });
18003
18107
  const tarball = fs33.readdirSync(context).find((f) => f.endsWith(".tgz"));
18004
18108
  if (!tarball) throw new MeshCliError("pnpm pack produced no tarball for apps/hub");
18005
- execFileSync27("tar", ["-xzf", path40.join(context, tarball), "-C", context, "--strip-components", "1"], {
18109
+ execFileSync28("tar", ["-xzf", path40.join(context, tarball), "-C", context, "--strip-components", "1"], {
18006
18110
  stdio: ["ignore", "pipe", "pipe"]
18007
18111
  });
18008
18112
  const rewritten = normalizeHubManifests(context, readWorkspaceCatalog(repoRoot2));
@@ -18014,7 +18118,7 @@ async function buildHubImagesFromSource(repoRoot2) {
18014
18118
  ]) {
18015
18119
  const tag = `${name}:${version}`;
18016
18120
  logInfo(`Building ${tag} from source\u2026`);
18017
- execFileSync27(
18121
+ execFileSync28(
18018
18122
  "docker",
18019
18123
  ["build", "-f", path40.join(hubStackDir, dockerfile), "-t", tag, "--secret", `id=npmrc,src=${npmrc}`, context],
18020
18124
  { stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
@@ -18185,8 +18289,42 @@ function printEndpoints(hubRunning) {
18185
18289
  console.log(
18186
18290
  ` ${chalk6.dim(`test users: ${TEST_USERS.map((u) => u.email).join(", ")} (password: ${TEST_USERS[0].password})`)}`
18187
18291
  );
18188
- console.log("");
18189
- logInfo("Next: `mesh dev` in an apps repo wires services to this stack automatically.");
18292
+ printStartHere(hubRunning);
18293
+ }
18294
+ function startHereLines(hubRunning, up) {
18295
+ const dev = TEST_USERS[0];
18296
+ const mailbox = STACK_ENDPOINTS.find((e) => e.service === "mailpit");
18297
+ const flag = (service) => up?.get(service) === false ? chalk6.red(" (not responding \u2014 see Endpoints below)") : "";
18298
+ const lines = ["", chalk6.bold.cyan("\u2605 Start here")];
18299
+ if (hubRunning) {
18300
+ lines.push(
18301
+ ` ${"Hub".padEnd(10)} ${chalk6.bold.cyan(`http://localhost:${hubPort()}`)}${flag("hub-ui")}`,
18302
+ ` ${"".padEnd(10)} ${chalk6.dim(`sign in as ${dev.email} / ${dev.password} \u2014 an account already exists; do not Register`)}`
18303
+ );
18304
+ }
18305
+ if (mailbox) {
18306
+ lines.push(
18307
+ ` ${"Mailbox".padEnd(10)} ${chalk6.cyan(mailbox.url)}${flag("mailpit")}`,
18308
+ ` ${"".padEnd(10)} ${chalk6.dim("every email the local platform sends (sign-up, verification, password reset) lands here \u2014 nothing leaves your machine")}`
18309
+ );
18310
+ }
18311
+ lines.push(
18312
+ ` ${"Your app".padEnd(10)} ${chalk6.cyan("cd apps/<name> && mesh dev")} ${chalk6.dim("no app yet? mesh create-app")}`,
18313
+ ` ${"Check-up".padEnd(10)} ${chalk6.cyan("mesh status")}`,
18314
+ ""
18315
+ );
18316
+ return lines;
18317
+ }
18318
+ function printStartHere(hubRunning, up) {
18319
+ for (const line of startHereLines(hubRunning, up)) console.log(line);
18320
+ }
18321
+ function portMoveHints() {
18322
+ return /* @__PURE__ */ new Map([
18323
+ [
18324
+ Number(hubPort()),
18325
+ "MESH_HUB_PORT=<free port> mesh start (or mesh start --no-hub, which starts everything else)"
18326
+ ]
18327
+ ]);
18190
18328
  }
18191
18329
  function repoRootForHubSource() {
18192
18330
  for (let dir = process.cwd(); ; ) {
@@ -18208,6 +18346,10 @@ function registerLocalCommands(program2) {
18208
18346
  "--takeover",
18209
18347
  "re-own a stack that another checkout started (may recreate shared containers with THIS checkout's config)",
18210
18348
  false
18349
+ ).option(
18350
+ "--skip-port-check",
18351
+ "start even though a host port the stack wants is already held (the service that loses the bind will be broken \u2014 use when the holder is something you cannot stop)",
18352
+ false
18211
18353
  ).action(async (opts) => {
18212
18354
  ensureDockerAvailable();
18213
18355
  const foreign = stackOwnedElsewhere();
@@ -18226,6 +18368,15 @@ Re-running start from here may recreate shared containers with this checkout's c
18226
18368
  if (foreign && opts.takeover) {
18227
18369
  logWarn(`Taking over the running stack (previously driven from ${foreign}).`);
18228
18370
  }
18371
+ if (!opts.skipPortCheck && !stackServices().some((s) => s.state === "running")) {
18372
+ const ports = hostPortsOf([...STACK_ENDPOINTS, ...opts.hub ? hubEndpoints() : []]);
18373
+ const conflicts = await findPortConflicts(ports, `${COMPOSE_PROJECT}-`);
18374
+ if (conflicts.length > 0) {
18375
+ throw new MeshCliError(describePortConflicts(conflicts, portMoveHints()), {
18376
+ remediation: { command: "mesh start # once the port is free" }
18377
+ });
18378
+ }
18379
+ }
18229
18380
  let hubVersion;
18230
18381
  if (opts.hub) {
18231
18382
  if (opts.hubFromSource) {
@@ -18406,6 +18557,7 @@ Re-running start from here may recreate shared containers with this checkout's c
18406
18557
  if (fabric && !fabric.ok) process.exitCode = 1;
18407
18558
  return;
18408
18559
  }
18560
+ printStartHere(hasHub, new Map(probes.map((p) => [p.service, p.up])));
18409
18561
  console.log(chalk6.bold("Containers"));
18410
18562
  for (const service of services) {
18411
18563
  const ok = service.state === "running" && (service.health === void 0 || service.health === "healthy");
@@ -18452,6 +18604,7 @@ var init_local = __esm({
18452
18604
  init_seed();
18453
18605
  init_seed_zitadel();
18454
18606
  init_seed_hub_catalog();
18607
+ init_helpers();
18455
18608
  WAIT_TIMEOUT_MS = 18e4;
18456
18609
  WAIT_POLL_MS = 3e3;
18457
18610
  }
@@ -19646,7 +19799,7 @@ var init_site = __esm({
19646
19799
  });
19647
19800
 
19648
19801
  // libs/mesh-cli/src/commands/stack.ts
19649
- import { execFileSync as execFileSync28 } from "child_process";
19802
+ import { execFileSync as execFileSync29 } from "child_process";
19650
19803
  import * as path42 from "path";
19651
19804
  import * as fs35 from "fs";
19652
19805
  import { parse as parseYaml5 } from "yaml";
@@ -19739,7 +19892,7 @@ function readBaseConfigFromYaml(appRoot, stack) {
19739
19892
  }
19740
19893
  function getGitHubUsername() {
19741
19894
  try {
19742
- const result = execFileSync28("gh", ["api", "user", "--jq", ".login"], {
19895
+ const result = execFileSync29("gh", ["api", "user", "--jq", ".login"], {
19743
19896
  encoding: "utf-8",
19744
19897
  stdio: ["pipe", "pipe", "pipe"]
19745
19898
  });
@@ -19748,7 +19901,7 @@ function getGitHubUsername() {
19748
19901
  } catch {
19749
19902
  }
19750
19903
  try {
19751
- const result = execFileSync28("git", ["config", "user.email"], {
19904
+ const result = execFileSync29("git", ["config", "user.email"], {
19752
19905
  encoding: "utf-8",
19753
19906
  stdio: ["pipe", "pipe", "pipe"]
19754
19907
  });
@@ -19834,7 +19987,7 @@ Specify which to base on: mesh stack init --from <stack>`
19834
19987
  if (!opts.adopt) {
19835
19988
  let existing = [];
19836
19989
  try {
19837
- const raw = execFileSync28("pulumi", ["stack", "ls", "--json"], {
19990
+ const raw = execFileSync29("pulumi", ["stack", "ls", "--json"], {
19838
19991
  cwd: appRoot,
19839
19992
  encoding: "utf-8",
19840
19993
  env: pulumiEnv,
@@ -19858,7 +20011,7 @@ Specify which to base on: mesh stack init --from <stack>`
19858
20011
  logInfo(`Using KMS secrets provider: ${secretsProvider}`);
19859
20012
  }
19860
20013
  try {
19861
- execFileSync28("pulumi", initArgs, {
20014
+ execFileSync29("pulumi", initArgs, {
19862
20015
  cwd: appRoot,
19863
20016
  env: pulumiEnv,
19864
20017
  stdio: "inherit"
@@ -19875,7 +20028,7 @@ Specify which to base on: mesh stack init --from <stack>`
19875
20028
  }
19876
20029
  }
19877
20030
  try {
19878
- execFileSync28("pulumi", ["stack", "select", newStack], {
20031
+ execFileSync29("pulumi", ["stack", "select", newStack], {
19879
20032
  cwd: appRoot,
19880
20033
  env: pulumiEnv,
19881
20034
  stdio: ["pipe", "pipe", "pipe"]
@@ -19885,7 +20038,7 @@ Specify which to base on: mesh stack init --from <stack>`
19885
20038
  if (!configExists) {
19886
20039
  let baseConfig = {};
19887
20040
  try {
19888
- const raw = execFileSync28(
20041
+ const raw = execFileSync29(
19889
20042
  "pulumi",
19890
20043
  ["config", "--json", "--stack", baseStack],
19891
20044
  { cwd: appRoot, env: pulumiEnv, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
@@ -19903,19 +20056,19 @@ Specify which to base on: mesh stack init --from <stack>`
19903
20056
  if (key === "mesh:deploy") continue;
19904
20057
  try {
19905
20058
  if (entry.objectValue !== void 0) {
19906
- execFileSync28(
20059
+ execFileSync29(
19907
20060
  "pulumi",
19908
20061
  ["config", "set", key, JSON.stringify(entry.objectValue)],
19909
20062
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
19910
20063
  );
19911
20064
  } else if (entry.value === "true" || entry.value === "false") {
19912
- execFileSync28(
20065
+ execFileSync29(
19913
20066
  "pulumi",
19914
20067
  ["config", "set", "--type", "bool", key, entry.value],
19915
20068
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
19916
20069
  );
19917
20070
  } else {
19918
- execFileSync28(
20071
+ execFileSync29(
19919
20072
  "pulumi",
19920
20073
  ["config", "set", key, entry.value],
19921
20074
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
@@ -19928,7 +20081,7 @@ Specify which to base on: mesh stack init --from <stack>`
19928
20081
  const baseEnv = readConfigBlockKey(appRoot, baseStack, "mesh:coreEnv") ?? (baseTenant && baseStack.startsWith(`${baseTenant}-`) ? baseStack.slice(baseTenant.length + 1) : baseStack);
19929
20082
  const setCfg = (args) => {
19930
20083
  try {
19931
- execFileSync28("pulumi", ["config", "set", ...args], {
20084
+ execFileSync29("pulumi", ["config", "set", ...args], {
19932
20085
  cwd: appRoot,
19933
20086
  env: pulumiEnv,
19934
20087
  stdio: ["pipe", "pipe", "pipe"]
@@ -19977,7 +20130,7 @@ Specify which to base on: mesh stack init --from <stack>`
19977
20130
  `Configured Pulumi.${newStack}.yaml (personal platform env on core '${baseEnv}'${opts.parentZone ? `, DNS zone ${newStack.startsWith(`${baseTenant}-`) ? newStack.slice(baseTenant.length + 1) : newStack}.${opts.parentZone}` : ""})`
19978
20131
  );
19979
20132
  } else {
19980
- execFileSync28("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
20133
+ execFileSync29("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
19981
20134
  cwd: appRoot,
19982
20135
  env: pulumiEnv,
19983
20136
  stdio: ["pipe", "pipe", "pipe"]
@@ -20007,7 +20160,7 @@ Specify which to base on: mesh stack init --from <stack>`
20007
20160
  const args = ["stack", "rm", name];
20008
20161
  if (opts.yes) args.push("--yes");
20009
20162
  try {
20010
- execFileSync28("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
20163
+ execFileSync29("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
20011
20164
  logSuccess(`Removed stack ${name}`);
20012
20165
  } catch (err) {
20013
20166
  process.exit(err.status ?? 1);
@@ -20219,12 +20372,12 @@ var init_recover_conversation = __esm({
20219
20372
  });
20220
20373
 
20221
20374
  // libs/mesh-cli/src/utils/temporal-codec.ts
20222
- import { execFileSync as execFileSync29 } from "child_process";
20375
+ import { execFileSync as execFileSync30 } from "child_process";
20223
20376
  import { webcrypto as crypto4 } from "node:crypto";
20224
20377
  function resolveTemporalEncodingKeyFromK8s(namespace) {
20225
20378
  const secretName = `${namespace}-temporal-encoding-key`;
20226
20379
  try {
20227
- const b64 = execFileSync29(
20380
+ const b64 = execFileSync30(
20228
20381
  "kubectl",
20229
20382
  [
20230
20383
  "get",
@@ -23564,7 +23717,7 @@ var init_src4 = __esm({
23564
23717
  import * as fs38 from "fs";
23565
23718
  import * as path45 from "path";
23566
23719
  import { createRequire as createRequire2 } from "module";
23567
- import { execFileSync as execFileSync30 } from "child_process";
23720
+ import { execFileSync as execFileSync31 } from "child_process";
23568
23721
  function resolveExtractorPath() {
23569
23722
  try {
23570
23723
  const require2 = createRequire2(import.meta.url);
@@ -23619,7 +23772,7 @@ for (const file of files) {
23619
23772
  process.stdout.write(JSON.stringify(results));
23620
23773
  `;
23621
23774
  try {
23622
- const result = execFileSync30("npx", ["tsx", "--eval", script], {
23775
+ const result = execFileSync31("npx", ["tsx", "--eval", script], {
23623
23776
  encoding: "utf-8",
23624
23777
  stdio: ["pipe", "pipe", "inherit"],
23625
23778
  maxBuffer: 10 * 1024 * 1024
@@ -23661,7 +23814,7 @@ for (const file of files) {
23661
23814
  process.stdout.write(JSON.stringify(results));
23662
23815
  `;
23663
23816
  try {
23664
- const result = execFileSync30("npx", ["tsx", "--eval", script], {
23817
+ const result = execFileSync31("npx", ["tsx", "--eval", script], {
23665
23818
  encoding: "utf-8",
23666
23819
  stdio: ["pipe", "pipe", "inherit"],
23667
23820
  maxBuffer: 10 * 1024 * 1024
@@ -23716,7 +23869,7 @@ for (const file of files) {
23716
23869
  process.stdout.write(JSON.stringify(results));
23717
23870
  `;
23718
23871
  try {
23719
- const result = execFileSync30("npx", ["tsx", "--eval", script], {
23872
+ const result = execFileSync31("npx", ["tsx", "--eval", script], {
23720
23873
  encoding: "utf-8",
23721
23874
  stdio: ["pipe", "pipe", "inherit"],
23722
23875
  maxBuffer: 10 * 1024 * 1024