@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 +75 -27
- package/dist/bin/mesh.js.map +1 -1
- package/dist/build-info.json +2 -2
- package/dist/src/commands/hub/index.d.ts +6 -1
- package/dist/src/commands/hub/index.js +71 -26
- package/fragments/base/README.md.hbs +15 -0
- package/package.json +2 -2
- package/skills/core/SKILL.md +3 -2
- package/templates/apps-repo/README.md.hbs +8 -0
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",
|
|
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
|
-
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
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
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
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",
|
|
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",
|
|
17251
|
-
execFileSync24("tmux", ["set-option", "-t", `${
|
|
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
|
-
`${
|
|
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",
|
|
17313
|
+
execFileSync24("tmux", ["new-window", "-t", hubSession, "-n", window, "-c", dir]);
|
|
17273
17314
|
}
|
|
17274
|
-
execFileSync24("tmux", ["set-option", "-t", `${
|
|
17315
|
+
execFileSync24("tmux", ["set-option", "-t", `${hubSession}:${window}`, "remain-on-exit", "on"], {
|
|
17275
17316
|
stdio: "ignore"
|
|
17276
17317
|
});
|
|
17277
|
-
execFileSync24("tmux", ["send-keys", "-t", `${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
17305
|
-
|
|
17306
|
-
|
|
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
|
-
|
|
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_"];
|