@mesh-tech/mesh-cli 0.31.1 → 0.32.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
@@ -16931,6 +16931,31 @@ import * as net12 from "node:net";
16931
16931
  import * as os9 from "node:os";
16932
16932
  import * as path36 from "node:path";
16933
16933
  import chalk3 from "chalk";
16934
+ function hubSessionName(apiPort) {
16935
+ return `${HUB_SESSION_PREFIX}-${apiPort}`;
16936
+ }
16937
+ function isHubSession(name) {
16938
+ return HUB_SESSION_RE.test(name);
16939
+ }
16940
+ function listHubSessions() {
16941
+ try {
16942
+ return execFileSync24("tmux", ["list-sessions", "-F", "#{session_name}"], {
16943
+ encoding: "utf8",
16944
+ stdio: ["ignore", "pipe", "ignore"]
16945
+ }).split("\n").map((line) => line.trim()).filter(isHubSession);
16946
+ } catch {
16947
+ return [];
16948
+ }
16949
+ }
16950
+ function killTargets(live, named) {
16951
+ return named ? live.filter((name) => name === named) : live;
16952
+ }
16953
+ function killInstructions(targets) {
16954
+ return targets.map((name) => {
16955
+ const port = name.slice(HUB_SESSION_PREFIX.length + 1);
16956
+ return port ? ` ${name}: mesh hub dev --kill --api-port ${port}` : ` ${name}: tmux kill-session -t ${name}`;
16957
+ });
16958
+ }
16934
16959
  function devSessionsDir() {
16935
16960
  return path36.join(os9.tmpdir(), "mesh-dev-sessions");
16936
16961
  }
@@ -17158,8 +17183,8 @@ function isPortListening2(port) {
17158
17183
  socket.once("error", () => done(false));
17159
17184
  });
17160
17185
  }
17161
- function hubEnvDir() {
17162
- return path36.join(os9.tmpdir(), "mesh-dev-sessions", HUB_SESSION);
17186
+ function hubEnvDir(sessionName) {
17187
+ return path36.join(os9.tmpdir(), "mesh-dev-sessions", sessionName);
17163
17188
  }
17164
17189
  function parsePort(raw, fallback, source) {
17165
17190
  if (raw === void 0) return fallback;
@@ -17181,17 +17206,28 @@ function redactEnv(env) {
17181
17206
  }
17182
17207
  async function hubDevAction(opts) {
17183
17208
  if (opts.kill) {
17184
- if (tmuxSessionExists(HUB_SESSION)) {
17185
- execFileSync24("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
17186
- fs29.rmSync(hubEnvDir(), { recursive: true, force: true });
17187
- logSuccess(`Killed Hub session '${HUB_SESSION}'.`);
17188
- } else {
17189
- logInfo(`No '${HUB_SESSION}' tmux session running.`);
17209
+ const namedPort = opts.apiPort || process.env.MESH_HUB_DEV_API_PORT;
17210
+ const named = namedPort ? hubSessionName(parsePort(namedPort, DEFAULT_HUB_API_PORT, opts.apiPort ? "--api-port" : "MESH_HUB_DEV_API_PORT")) : null;
17211
+ const targets = killTargets(listHubSessions(), named);
17212
+ if (targets.length === 0) {
17213
+ logInfo(named ? `No '${named}' tmux session running.` : "No `mesh hub dev` session running.");
17214
+ return;
17190
17215
  }
17216
+ if (targets.length > 1) {
17217
+ throw new MeshCliError(
17218
+ `${targets.length} Hub sessions are running \u2014 name the one to stop.
17219
+ ${killInstructions(targets).join("\n")}`,
17220
+ { remediation: { command: "mesh hub dev --kill --api-port <port> # the API port that Hub was started on" } }
17221
+ );
17222
+ }
17223
+ const target = targets[0];
17224
+ execFileSync24("tmux", ["kill-session", "-t", target], { stdio: "ignore" });
17225
+ fs29.rmSync(hubEnvDir(target), { recursive: true, force: true });
17226
+ logSuccess(`Killed Hub session '${target}'.`);
17191
17227
  return;
17192
17228
  }
17193
17229
  const uiPort = opts.port ? parsePort(opts.port, DEFAULT_HUB_UI_PORT, "--port") : parsePort(process.env.MESH_HUB_DEV_PORT, DEFAULT_HUB_UI_PORT, "MESH_HUB_DEV_PORT");
17194
- const apiPort = parsePort(opts.apiPort, DEFAULT_HUB_API_PORT, "--api-port");
17230
+ const apiPort = opts.apiPort ? parsePort(opts.apiPort, DEFAULT_HUB_API_PORT, "--api-port") : parsePort(process.env.MESH_HUB_DEV_API_PORT, DEFAULT_HUB_API_PORT, "MESH_HUB_DEV_API_PORT");
17195
17231
  const platformDir = resolvePlatformDir(opts.platformDir, process.cwd());
17196
17232
  const cwdAppRoot = findAppRoot(process.cwd());
17197
17233
  const session = pickDevSession(listDevSessions(), { session: opts.session, cwdAppRoot });
@@ -17218,9 +17254,10 @@ async function hubDevAction(opts) {
17218
17254
  for (const [k, v] of Object.entries(redactEnv(assembled.uiEnv))) console.log(` ${k}=${v}`);
17219
17255
  return;
17220
17256
  }
17221
- if (tmuxSessionExists(HUB_SESSION)) {
17222
- throw new MeshCliError(`A '${HUB_SESSION}' tmux session is already running.`, {
17223
- remediation: { command: "mesh hub dev --kill # then re-run mesh hub dev" }
17257
+ const hubSession = hubSessionName(apiPort);
17258
+ if (tmuxSessionExists(hubSession)) {
17259
+ throw new MeshCliError(`A '${hubSession}' tmux session is already running.`, {
17260
+ remediation: { command: `mesh hub dev --kill --api-port ${apiPort} # then re-run` }
17224
17261
  });
17225
17262
  }
17226
17263
  for (const [label, port, flag] of [
@@ -17245,20 +17282,20 @@ async function hubDevAction(opts) {
17245
17282
  for (const warning of assembled.warnings) logWarn(warning);
17246
17283
  const apiDir = path36.join(platformDir, "apps", "hub", "api");
17247
17284
  const uiDir = path36.join(platformDir, "apps", "hub", "ui");
17248
- execFileSync24("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
17285
+ execFileSync24("tmux", ["new-session", "-d", "-s", hubSession, "-n", "api", "-c", apiDir]);
17249
17286
  if (!assembled.apiEnv.DEV_USER_TOKEN_URL && assembled.apiEnv.DEV_USER_ID_TOKEN) {
17250
17287
  const platform = session.state.devOutput.platform;
17251
17288
  const credContext = `mesh.${platform.env}`;
17252
17289
  const tokenPort = await findFreePort4();
17253
17290
  const tokenUrl = `http://127.0.0.1:${tokenPort}`;
17254
- execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
17255
- execFileSync24("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
17291
+ execFileSync24("tmux", ["new-window", "-t", hubSession, "-n", "token-server", "-c", platformDir]);
17292
+ execFileSync24("tmux", ["set-option", "-t", `${hubSession}:token-server`, "remain-on-exit", "on"], {
17256
17293
  stdio: "ignore"
17257
17294
  });
17258
17295
  execFileSync24("tmux", [
17259
17296
  "send-keys",
17260
17297
  "-t",
17261
- `${HUB_SESSION}:token-server`,
17298
+ `${hubSession}:token-server`,
17262
17299
  `npx mesh dev __token-server ${tokenPort} ${credContext}`,
17263
17300
  "Enter"
17264
17301
  ]);
@@ -17270,15 +17307,15 @@ async function hubDevAction(opts) {
17270
17307
  logSuccess(`Dev-user token-server: ${tokenUrl} (context ${credContext})`);
17271
17308
  }
17272
17309
  const launch = (window, dir, env, cmd, createWindow) => {
17273
- const envFile = path36.join(hubEnvDir(), `${window}.env.sh`);
17310
+ const envFile = path36.join(hubEnvDir(hubSession), `${window}.env.sh`);
17274
17311
  writeEnvFile(envFile, env);
17275
17312
  if (createWindow) {
17276
- execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
17313
+ execFileSync24("tmux", ["new-window", "-t", hubSession, "-n", window, "-c", dir]);
17277
17314
  }
17278
- execFileSync24("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
17315
+ execFileSync24("tmux", ["set-option", "-t", `${hubSession}:${window}`, "remain-on-exit", "on"], {
17279
17316
  stdio: "ignore"
17280
17317
  });
17281
- execFileSync24("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
17318
+ execFileSync24("tmux", ["send-keys", "-t", `${hubSession}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
17282
17319
  };
17283
17320
  launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
17284
17321
  launch("ui", uiDir, assembled.uiEnv, `pnpm dev --port ${uiPort} --strictPort`, true);
@@ -17288,15 +17325,15 @@ async function hubDevAction(opts) {
17288
17325
  if (!apiUp || !uiUp) {
17289
17326
  throw new MeshCliError(
17290
17327
  `Hub ${apiUp ? "UI" : "API"} did not start listening (api :${apiPort}, ui :${uiPort}).`,
17291
- { remediation: { command: `tmux attach -t ${HUB_SESSION} # inspect the ${apiUp ? "ui" : "api"} window` } }
17328
+ { remediation: { command: `tmux attach -t ${hubSession} # inspect the ${apiUp ? "ui" : "api"} window` } }
17292
17329
  );
17293
17330
  }
17294
17331
  console.log("");
17295
17332
  logSuccess(`Mesh Hub (dev-local scope ${assembled.scope.scopeTenants.join(",")} @ ${assembled.scope.scopeEnv})`);
17296
17333
  console.log(` ${chalk3.bold.cyan(`http://localhost:${uiPort}`)} (also reachable on your tailnet)`);
17297
17334
  console.log(` api http://localhost:${apiPort}`);
17298
- console.log(` logs tmux attach -t ${HUB_SESSION}`);
17299
- console.log(` stop mesh hub dev --kill`);
17335
+ console.log(` logs tmux attach -t ${hubSession}`);
17336
+ console.log(` stop mesh hub dev --kill --api-port ${apiPort}`);
17300
17337
  }
17301
17338
  function registerHubCommands(program2) {
17302
17339
  const hub = program2.command("hub").description("Local Hub over dev-local stacks");
@@ -17305,9 +17342,15 @@ function registerHubCommands(program2) {
17305
17342
  ).option("--session <name>", "dev session to observe (default: auto-detect from cwd)").option("--tenants <list>", "comma-separated HUB_SCOPE_TENANTS override (default: the session's tenant)").option(
17306
17343
  "--audience <who>",
17307
17344
  "who the Hub renders for: `operator` (platform staff \u2014 the default here) or `tenant` (a client's operators). Anything else reads as `tenant`"
17308
- ).option("--port <port>", `Hub UI host port (default: $MESH_HUB_DEV_PORT or ${DEFAULT_HUB_UI_PORT})`).option("--api-port <port>", `Hub API port (default: ${DEFAULT_HUB_API_PORT})`).option("--platform-dir <dir>", "mesh-platform checkout to run the Hub from (default: $MESH_PLATFORM_DIR or walk up from cwd)").option("--print-env", "print the assembled env (secrets redacted) and exit without launching").option("--kill", "tear down the running Hub session").action(hubDevAction);
17309
- }
17310
- var HUB_SESSION, DEFAULT_HUB_UI_PORT, DEFAULT_HUB_API_PORT, FORWARD_PREFIXES, localPlatformEnv2, SENSITIVE_RE;
17345
+ ).option("--port <port>", `Hub UI host port (default: $MESH_HUB_DEV_PORT or ${DEFAULT_HUB_UI_PORT})`).option(
17346
+ "--api-port <port>",
17347
+ `Hub API port, and the key its tmux session is named after \u2014 several Hubs coexist on different ones (default: $MESH_HUB_DEV_API_PORT or ${DEFAULT_HUB_API_PORT})`
17348
+ ).option("--platform-dir <dir>", "mesh-platform checkout to run the Hub from (default: $MESH_PLATFORM_DIR or walk up from cwd)").option("--print-env", "print the assembled env (secrets redacted) and exit without launching").option(
17349
+ "--kill",
17350
+ "tear down a running Hub session; with several up, name it with --api-port (or $MESH_HUB_DEV_API_PORT)"
17351
+ ).action(hubDevAction);
17352
+ }
17353
+ var HUB_SESSION_PREFIX, HUB_SESSION_RE, DEFAULT_HUB_UI_PORT, DEFAULT_HUB_API_PORT, FORWARD_PREFIXES, localPlatformEnv2, SENSITIVE_RE;
17311
17354
  var init_hub = __esm({
17312
17355
  "libs/mesh-cli/src/commands/hub/index.ts"() {
17313
17356
  "use strict";
@@ -17317,7 +17360,8 @@ var init_hub = __esm({
17317
17360
  init_stack();
17318
17361
  init_log();
17319
17362
  init_pulumi();
17320
- HUB_SESSION = "mesh-hub-dev";
17363
+ HUB_SESSION_PREFIX = "mesh-hub-dev";
17364
+ HUB_SESSION_RE = new RegExp(`^${HUB_SESSION_PREFIX}(-\\d+)?$`);
17321
17365
  DEFAULT_HUB_UI_PORT = Number(DEFAULT_HUB_PORT);
17322
17366
  DEFAULT_HUB_API_PORT = 3002;
17323
17367
  FORWARD_PREFIXES = ["AWS_", "DEV_USER_"];