@mesh-tech/mesh-cli 0.31.0 → 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
  }
@@ -17087,9 +17112,13 @@ function assembleHubEnv(session, sessionEnv, opts) {
17087
17112
  PORT: String(opts.uiPort),
17088
17113
  API_URL: `http://localhost:${opts.apiPort}`,
17089
17114
  HUB_TENANT: hubTenant,
17115
+ // The shell reads the scope from HUB_SCOPE_ENV; DEFAULT_ENV is only a default.
17116
+ HUB_SCOPE_ENV: scopeEnv,
17090
17117
  // UI namespace parsing + session defaults land on the scoped tenant.
17091
17118
  DEFAULT_ORG: scopeTenants[0],
17092
17119
  DEFAULT_ENV: scopeEnv,
17120
+ // A dev session has no sibling stacks, so the peer list can only be supplied.
17121
+ ...process.env.HUB_PEER_ENVIRONMENTS ? { HUB_PEER_ENVIRONMENTS: process.env.HUB_PEER_ENVIRONMENTS } : {},
17093
17122
  ...audienceEnv
17094
17123
  };
17095
17124
  return { apiEnv, uiEnv, scope: { hubTenant, scopeEnv, scopeTenants }, temporalAddress, warnings, notes };
@@ -17154,8 +17183,8 @@ function isPortListening2(port) {
17154
17183
  socket.once("error", () => done(false));
17155
17184
  });
17156
17185
  }
17157
- function hubEnvDir() {
17158
- return path36.join(os9.tmpdir(), "mesh-dev-sessions", HUB_SESSION);
17186
+ function hubEnvDir(sessionName) {
17187
+ return path36.join(os9.tmpdir(), "mesh-dev-sessions", sessionName);
17159
17188
  }
17160
17189
  function parsePort(raw, fallback, source) {
17161
17190
  if (raw === void 0) return fallback;
@@ -17177,17 +17206,28 @@ function redactEnv(env) {
17177
17206
  }
17178
17207
  async function hubDevAction(opts) {
17179
17208
  if (opts.kill) {
17180
- if (tmuxSessionExists(HUB_SESSION)) {
17181
- execFileSync24("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
17182
- fs29.rmSync(hubEnvDir(), { recursive: true, force: true });
17183
- logSuccess(`Killed Hub session '${HUB_SESSION}'.`);
17184
- } else {
17185
- 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;
17186
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}'.`);
17187
17227
  return;
17188
17228
  }
17189
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");
17190
- 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");
17191
17231
  const platformDir = resolvePlatformDir(opts.platformDir, process.cwd());
17192
17232
  const cwdAppRoot = findAppRoot(process.cwd());
17193
17233
  const session = pickDevSession(listDevSessions(), { session: opts.session, cwdAppRoot });
@@ -17214,9 +17254,10 @@ async function hubDevAction(opts) {
17214
17254
  for (const [k, v] of Object.entries(redactEnv(assembled.uiEnv))) console.log(` ${k}=${v}`);
17215
17255
  return;
17216
17256
  }
17217
- if (tmuxSessionExists(HUB_SESSION)) {
17218
- throw new MeshCliError(`A '${HUB_SESSION}' tmux session is already running.`, {
17219
- 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` }
17220
17261
  });
17221
17262
  }
17222
17263
  for (const [label, port, flag] of [
@@ -17241,20 +17282,20 @@ async function hubDevAction(opts) {
17241
17282
  for (const warning of assembled.warnings) logWarn(warning);
17242
17283
  const apiDir = path36.join(platformDir, "apps", "hub", "api");
17243
17284
  const uiDir = path36.join(platformDir, "apps", "hub", "ui");
17244
- execFileSync24("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
17285
+ execFileSync24("tmux", ["new-session", "-d", "-s", hubSession, "-n", "api", "-c", apiDir]);
17245
17286
  if (!assembled.apiEnv.DEV_USER_TOKEN_URL && assembled.apiEnv.DEV_USER_ID_TOKEN) {
17246
17287
  const platform = session.state.devOutput.platform;
17247
17288
  const credContext = `mesh.${platform.env}`;
17248
17289
  const tokenPort = await findFreePort4();
17249
17290
  const tokenUrl = `http://127.0.0.1:${tokenPort}`;
17250
- execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
17251
- 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"], {
17252
17293
  stdio: "ignore"
17253
17294
  });
17254
17295
  execFileSync24("tmux", [
17255
17296
  "send-keys",
17256
17297
  "-t",
17257
- `${HUB_SESSION}:token-server`,
17298
+ `${hubSession}:token-server`,
17258
17299
  `npx mesh dev __token-server ${tokenPort} ${credContext}`,
17259
17300
  "Enter"
17260
17301
  ]);
@@ -17266,15 +17307,15 @@ async function hubDevAction(opts) {
17266
17307
  logSuccess(`Dev-user token-server: ${tokenUrl} (context ${credContext})`);
17267
17308
  }
17268
17309
  const launch = (window, dir, env, cmd, createWindow) => {
17269
- const envFile = path36.join(hubEnvDir(), `${window}.env.sh`);
17310
+ const envFile = path36.join(hubEnvDir(hubSession), `${window}.env.sh`);
17270
17311
  writeEnvFile(envFile, env);
17271
17312
  if (createWindow) {
17272
- execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
17313
+ execFileSync24("tmux", ["new-window", "-t", hubSession, "-n", window, "-c", dir]);
17273
17314
  }
17274
- execFileSync24("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
17315
+ execFileSync24("tmux", ["set-option", "-t", `${hubSession}:${window}`, "remain-on-exit", "on"], {
17275
17316
  stdio: "ignore"
17276
17317
  });
17277
- 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"]);
17278
17319
  };
17279
17320
  launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
17280
17321
  launch("ui", uiDir, assembled.uiEnv, `pnpm dev --port ${uiPort} --strictPort`, true);
@@ -17284,15 +17325,15 @@ async function hubDevAction(opts) {
17284
17325
  if (!apiUp || !uiUp) {
17285
17326
  throw new MeshCliError(
17286
17327
  `Hub ${apiUp ? "UI" : "API"} did not start listening (api :${apiPort}, ui :${uiPort}).`,
17287
- { 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` } }
17288
17329
  );
17289
17330
  }
17290
17331
  console.log("");
17291
17332
  logSuccess(`Mesh Hub (dev-local scope ${assembled.scope.scopeTenants.join(",")} @ ${assembled.scope.scopeEnv})`);
17292
17333
  console.log(` ${chalk3.bold.cyan(`http://localhost:${uiPort}`)} (also reachable on your tailnet)`);
17293
17334
  console.log(` api http://localhost:${apiPort}`);
17294
- console.log(` logs tmux attach -t ${HUB_SESSION}`);
17295
- 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}`);
17296
17337
  }
17297
17338
  function registerHubCommands(program2) {
17298
17339
  const hub = program2.command("hub").description("Local Hub over dev-local stacks");
@@ -17301,9 +17342,15 @@ function registerHubCommands(program2) {
17301
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(
17302
17343
  "--audience <who>",
17303
17344
  "who the Hub renders for: `operator` (platform staff \u2014 the default here) or `tenant` (a client's operators). Anything else reads as `tenant`"
17304
- ).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);
17305
- }
17306
- 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;
17307
17354
  var init_hub = __esm({
17308
17355
  "libs/mesh-cli/src/commands/hub/index.ts"() {
17309
17356
  "use strict";
@@ -17313,7 +17360,8 @@ var init_hub = __esm({
17313
17360
  init_stack();
17314
17361
  init_log();
17315
17362
  init_pulumi();
17316
- HUB_SESSION = "mesh-hub-dev";
17363
+ HUB_SESSION_PREFIX = "mesh-hub-dev";
17364
+ HUB_SESSION_RE = new RegExp(`^${HUB_SESSION_PREFIX}(-\\d+)?$`);
17317
17365
  DEFAULT_HUB_UI_PORT = Number(DEFAULT_HUB_PORT);
17318
17366
  DEFAULT_HUB_API_PORT = 3002;
17319
17367
  FORWARD_PREFIXES = ["AWS_", "DEV_USER_"];