@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
24,551 lines • 930 kB
JavaScript
#!/usr/bin/env node
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// libs/mesh-cli/src/utils/errors.ts
import chalk from "chalk";
function renderErrorBody(err) {
const message = err instanceof Error ? err.message : String(err);
if (!(err instanceof MeshCliError)) return message;
const lines = [message];
if (err.remediation?.command) {
lines.push(chalk.cyan(" \u2192 run: ") + err.remediation.command);
}
if (err.remediation?.docs) {
lines.push(chalk.cyan(" \u2192 see: ") + err.remediation.docs);
}
return lines.join("\n");
}
function renderError(err) {
return chalk.red("\u2717") + " " + renderErrorBody(err);
}
function renderErrorJson(err) {
if (err instanceof MeshCliError) {
return JSON.stringify({
error: err.message,
...err.remediation ? { remediation: err.remediation } : {}
});
}
return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
}
function emitJsonPayload(payload) {
console.log(JSON.stringify(payload, null, 2));
jsonPayloadEmitted = true;
}
function hasEmittedJsonPayload() {
return jsonPayloadEmitted;
}
function handleCliError(err) {
const wantsJson = process.argv.includes("--json");
if (wantsJson && !hasEmittedJsonPayload()) {
console.log(renderErrorJson(err));
} else {
console.error(renderError(err));
}
process.exit(err instanceof MeshCliError ? err.exitCode : 1);
}
var MeshCliError, jsonPayloadEmitted;
var init_errors = __esm({
"libs/mesh-cli/src/utils/errors.ts"() {
"use strict";
MeshCliError = class extends Error {
remediation;
exitCode;
constructor(message, options = {}) {
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
this.name = "MeshCliError";
this.remediation = options.remediation;
this.exitCode = options.exitCode ?? 1;
}
};
jsonPayloadEmitted = false;
}
});
// libs/mesh-cli/src/utils/log.ts
import chalk2 from "chalk";
function logPrefix(opts) {
const enabled = opts.envFlag === "1" || opts.envFlag !== "0" && !opts.isTTY;
return enabled ? `[${opts.now.toISOString().slice(11, 19)}] ` : "";
}
function prefix() {
return chalk2.dim(
logPrefix({
isTTY: !!process.stderr.isTTY,
envFlag: process.env.MESH_LOG_TIMESTAMPS,
now: /* @__PURE__ */ new Date()
})
);
}
function logInfo(message) {
console.error(prefix() + chalk2.blue("\u2139"), message);
}
function logSuccess(message) {
console.error(prefix() + chalk2.green("\u2713"), message);
}
function logWarn(message) {
console.error(prefix() + chalk2.yellow("\u26A0"), message);
}
function logError(message) {
console.error(prefix() + chalk2.red("\u2717"), message);
}
function formatElapsed(ms) {
const totalSec = Math.max(0, Math.round(ms / 1e3));
const h = Math.floor(totalSec / 3600);
const m = Math.floor(totalSec % 3600 / 60);
const s = totalSec % 60;
if (h > 0) return `${h}h ${m}m`;
if (m > 0) return `${m}m ${s}s`;
return `${s}s`;
}
function startHeartbeat(label, intervalMs = 15e3) {
const startedAt = Date.now();
let lastOutputAt = startedAt;
const timer = setInterval(() => {
if (Date.now() - lastOutputAt < intervalMs) return;
logInfo(`\u2026 still working: ${label} (${formatElapsed(Date.now() - startedAt)} elapsed)`);
}, intervalMs);
timer.unref?.();
return {
stop: () => clearInterval(timer),
touch: () => {
lastOutputAt = Date.now();
}
};
}
var init_log = __esm({
"libs/mesh-cli/src/utils/log.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/context.ts
import * as fs from "fs";
import * as path from "path";
function findFileUpward(filename, startDir = process.cwd()) {
let currentDir = startDir;
const root = path.parse(currentDir).root;
while (currentDir !== root) {
const filePath = path.join(currentDir, filename);
if (fs.existsSync(filePath)) {
return filePath;
}
currentDir = path.dirname(currentDir);
}
return void 0;
}
function detectContext(stageArg) {
let tenant;
let stage;
const cwd = process.cwd();
if (fs.existsSync("Pulumi.yaml")) {
const files = fs.readdirSync(".").filter(
(f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml"
);
const configFile = files[0];
if (configFile) {
const content = fs.readFileSync(configFile, "utf-8");
const tenantMatch = content.match(/^\s*mesh:tenant:\s*["']?([^"'\n]+)["']?/m);
if (tenantMatch?.[1]) {
tenant = tenantMatch[1].trim();
}
const stageMatch = configFile.match(/Pulumi\.(.+)\.yaml/);
if (stageMatch?.[1]) {
stage = stageMatch[1];
}
}
}
const sstConfigPath = findFileUpward("sst.config.ts");
let sstDir;
if (sstConfigPath) {
sstDir = path.dirname(sstConfigPath);
const parentConfigPath = path.join(sstDir, "..", "config.ts");
if (fs.existsSync(parentConfigPath)) {
const configContent = fs.readFileSync(parentConfigPath, "utf-8");
const tenantMatch = configContent.match(/tenant:\s*["']([^"']+)["']/);
if (tenantMatch) {
tenant = tenantMatch[1];
}
}
const sstStagePath = path.join(sstDir, ".sst", "stage");
if (fs.existsSync(sstStagePath)) {
stage = fs.readFileSync(sstStagePath, "utf-8").trim();
}
}
if (!tenant) {
const pathMatch = cwd.match(/tenants\/([^/]+)/);
if (pathMatch) {
tenant = pathMatch[1];
}
}
stage = stageArg || process.env.MESH_STAGE || process.env.SST_STAGE || stage || "dev";
tenant = process.env.MESH_TENANT || tenant || "mesh";
let platformEnv = stage;
let platformEnvMap = {};
let defaultPlatformEnv;
const configPath = sstDir ? path.join(sstDir, "..", "config.ts") : "../config.ts";
if (fs.existsSync(configPath)) {
const configContent = fs.readFileSync(configPath, "utf-8");
const mapMatch = configContent.match(/platformEnvMap:\s*\{([^}]+)\}/);
if (mapMatch?.[1]) {
const entries = mapMatch[1].matchAll(/(\w+):\s*["']([^"']+)["']/g);
for (const entry of entries) {
const key = entry[1];
const value = entry[2];
if (key && value) {
platformEnvMap[key] = value;
}
}
}
const defaultMatch = configContent.match(/defaultPlatformEnv:\s*["']([^"']+)["']/);
if (defaultMatch?.[1]) {
defaultPlatformEnv = defaultMatch[1];
}
}
const tenantPrefix = `${tenant}-`;
const parsedEnv = stage.startsWith(tenantPrefix) ? stage.slice(tenantPrefix.length) : stage;
platformEnv = platformEnvMap[parsedEnv] ?? platformEnvMap[stage] ?? defaultPlatformEnv ?? parsedEnv;
logInfo(`Stage: ${stage}, Platform: ${platformEnv}, Tenant: ${tenant}`);
return { tenant, stage, platformEnv };
}
var init_context = __esm({
"libs/mesh-cli/src/utils/context.ts"() {
"use strict";
init_log();
}
});
// libs/mesh-cli/src/utils/bastion.ts
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
async function getPlatformBastionInfo(tenant, platformEnv, region) {
const ssm = new SSMClient(region ? { region } : {});
let primaryError;
const platformPath = `/mesh-platform/${tenant}/${platformEnv}/platform`;
logInfo(`Looking up platform bastion from ${platformPath}...`);
try {
const response = await ssm.send(
new GetParameterCommand({ Name: platformPath })
);
if (response.Parameter?.Value) {
const platform = JSON.parse(response.Parameter.Value);
if (platform.platformBastion) {
const info = platform.platformBastion;
logSuccess(`Found bastion: ${info.instanceId}`);
const serviceNames = Object.keys(info.services);
if (serviceNames.length > 0) {
logInfo(`Available services: ${serviceNames.join(", ")}`);
}
return info;
}
}
} catch (err) {
primaryError = err;
}
const legacyPath = `/mesh-platform/${tenant}/${platformEnv}/platform-bastion`;
logInfo(`Trying legacy path ${legacyPath}...`);
try {
const response = await ssm.send(
new GetParameterCommand({ Name: legacyPath })
);
if (!response.Parameter?.Value) {
throw new Error(`Platform bastion not found`);
}
const info = JSON.parse(response.Parameter.Value);
logSuccess(`Found bastion: ${info.instanceId}`);
const serviceNames = Object.keys(info.services);
if (serviceNames.length > 0) {
logInfo(`Available services: ${serviceNames.join(", ")}`);
}
return info;
} catch (legacyError) {
const cause = primaryError ?? legacyError;
const name = cause?.name ?? "";
const credsProblem = /Expired|UnrecognizedClient|InvalidClientTokenId|InvalidSignature|CredentialsProviderError|AccessDenied/i.test(
name
);
if (credsProblem) {
logError(
`Could not read the platform bastion from ${platformPath} \u2014 AWS error: ${name}.`
);
logInfo(
"This is almost always a CREDENTIALS problem, not a missing bastion."
);
logInfo(
" \u2022 The read uses the ambient AWS creds of this process; they must be valid AND able to read the HUB param above."
);
logInfo(
" \u2022 Check: `aws sts get-caller-identity` (ExpiredToken \u2192 refresh; AccessDenied \u2192 those creds lack hub read \u2014 use InfraAdmin-grade creds)."
);
logInfo(
" \u2022 Stale creds often hide in the tmux GLOBAL env (`tmux show-environment -g | grep AWS_`); a per-shell `unset` won't clear them."
);
} else {
logError(`Platform bastion not found in ${platformPath} or ${legacyPath}`);
logInfo("Make sure platformBastion is enabled in your platform config and deployed.");
}
throw cause;
}
}
var init_bastion = __esm({
"libs/mesh-cli/src/utils/bastion.ts"() {
"use strict";
init_log();
}
});
// libs/mesh-cli/src/utils/credentials.ts
import {
SecretsManagerClient,
GetSecretValueCommand
} from "@aws-sdk/client-secrets-manager";
async function getDatabaseUrl(tenant, stage, options) {
const secretsManager = new SecretsManagerClient({});
const secretName = options?.app ? `mesh/${tenant}/${stage}/${options.app}/db-credentials` : `mesh/${tenant}/${stage}/db-credentials`;
logInfo(`Looking up credentials at ${secretName}...`);
try {
const response = await secretsManager.send(
new GetSecretValueCommand({ SecretId: secretName })
);
if (response.SecretString) {
const secret = JSON.parse(response.SecretString);
if (secret.DATABASE_URL) {
const parsed = new URL(secret.DATABASE_URL);
logSuccess(`Got credentials for user: ${parsed.username}`);
return secret.DATABASE_URL;
}
if (secret.username && secret.password) {
logSuccess(`Got credentials for user: ${secret.username}`);
const database = secret.dbname ?? secret.database ?? "postgres";
return `postgresql://${secret.username}:${encodeURIComponent(secret.password)}@${secret.host ?? "localhost"}:${secret.port ?? 5432}/${database}`;
}
throw new Error(`Secret ${secretName} has unexpected format (needs DATABASE_URL or username/password)`);
}
} catch (error) {
if (error.message?.includes("unexpected format")) {
throw error;
}
}
throw new Error(`Could not find credentials at ${secretName}`);
}
function rewriteDatabaseUrl(url, options) {
try {
const parsed = new URL(url);
if (options.endpoint) {
if (options.endpoint.includes(":")) {
const colonIndex = options.endpoint.lastIndexOf(":");
parsed.hostname = options.endpoint.slice(0, colonIndex);
parsed.port = options.endpoint.slice(colonIndex + 1);
} else {
parsed.hostname = options.endpoint;
parsed.port = parsed.port || "5432";
}
}
if (options.sslMode) {
parsed.searchParams.set("sslmode", options.sslMode);
if (options.sslMode === "require" || options.sslMode === "no-verify") {
parsed.searchParams.set("sslaccept", "accept_invalid_certs");
}
}
return parsed.toString();
} catch {
return url;
}
}
var init_credentials = __esm({
"libs/mesh-cli/src/utils/credentials.ts"() {
"use strict";
init_log();
}
});
// libs/mesh-cli/src/utils/pulumi.ts
import { execFileSync } from "child_process";
import * as path2 from "path";
import * as fs2 from "fs";
function findAppRoot(startDir) {
let dir = startDir;
while (true) {
if (fs2.existsSync(path2.join(dir, "Pulumi.yaml"))) return dir;
const parent = path2.dirname(dir);
if (parent === dir) return null;
dir = parent;
}
}
function findStackConfigs(appRoot) {
return fs2.readdirSync(appRoot).filter((f) => /^Pulumi\..+\.yaml$/.test(f) && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
}
function getCurrentStack(appRoot) {
try {
const result = execFileSync("pulumi", ["stack", "--show-name"], {
encoding: "utf-8",
cwd: appRoot,
stdio: ["pipe", "pipe", "pipe"]
});
return result.trim() || null;
} catch {
return null;
}
}
function readStackConfig(appRoot, stack, key) {
const configFile = path2.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs2.existsSync(configFile)) return null;
const content = fs2.readFileSync(configFile, "utf-8");
const pattern = new RegExp(`^\\s+${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
const match = content.match(pattern);
if (!match) return null;
return match[1].trim().replace(/^["']|["']$/g, "");
}
function pulumiStackOutput(appRoot, key, extraArgs, env) {
const execEnv = env ? { ...process.env, ...env } : void 0;
try {
const result = execFileSync(
"pulumi",
["stack", "output", key, "--json", ...extraArgs],
{ cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
);
if (result.includes('"[secret]"')) {
return execFileSync(
"pulumi",
["stack", "output", key, "--json", "--show-secrets", ...extraArgs],
{ cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
);
}
return result;
} catch (err) {
const errMsg = err?.stderr ?? "";
if (errMsg.includes("kms:") || errMsg.includes("KMS") || errMsg.includes("secrets manager")) {
throw err;
}
return execFileSync(
"pulumi",
["stack", "output", key, "--json", "--show-secrets", ...extraArgs],
{ cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
);
}
}
var init_pulumi = __esm({
"libs/mesh-cli/src/utils/pulumi.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/vpn.ts
import { execFile, execFileSync as execFileSync2 } from "node:child_process";
import { existsSync as existsSync3 } from "node:fs";
async function isVpnConnected() {
try {
const stdout = await execTailscaleCmd(["status", "--json"]);
const status = JSON.parse(stdout);
return status.BackendState === "Running" && status.Self?.Online === true;
} catch {
return false;
}
}
function headscaleDnsConfig(tenant = "mesh", env = "dev", overrides) {
return {
namespace: `${tenant}-${env}-headscale`,
pod: "headscale-0",
container: "dns-writer",
filePath: "/var/lib/headscale/dns/extra-records.json",
...overrides
};
}
function execTailscaleCmd(args) {
const socketPath2 = TAILSCALE_SOCKET_PATHS.find(existsSync3);
const attempts = [];
for (const binary of TAILSCALE_PATHS) {
if (socketPath2) {
attempts.push({ binary, args: ["--socket", socketPath2, ...args] });
}
attempts.push({ binary, args });
}
return new Promise((resolve19, reject) => {
let index = 0;
function tryNext() {
if (index >= attempts.length) {
reject(new Error("All tailscale binary attempts failed"));
return;
}
const attempt = attempts[index++];
execFile(attempt.binary, attempt.args, (error, stdout) => {
if (error) {
tryNext();
} else {
resolve19(stdout);
}
});
}
tryNext();
});
}
async function getTailscaleInfo() {
try {
const stdout = await execTailscaleCmd(["status", "--json"]);
const status = JSON.parse(stdout);
if (status.BackendState === "Running" && status.Self?.Online === true && status.Self.HostName && status.Self.TailscaleIPs?.length) {
return {
hostname: status.Self.HostName,
ip: status.Self.TailscaleIPs[0]
};
}
return null;
} catch {
return null;
}
}
function dnsWriterExec(cfg, cmd, interactive = false) {
return [
"exec",
...interactive ? ["-i"] : [],
"-n",
cfg.namespace,
cfg.pod,
"-c",
cfg.container,
"--",
...cmd
];
}
function readDnsRecords(cfg) {
try {
const raw = execFileSync2("kubectl", dnsWriterExec(cfg, ["cat", cfg.filePath]), {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
});
const trimmed = raw.trim();
if (!trimmed || trimmed === "[]") return [];
try {
return JSON.parse(trimmed);
} catch {
console.warn(`[vpn] DNS records file contains invalid JSON, treating as empty`);
return [];
}
} catch (err) {
const msg = err.message ?? String(err);
if (msg.includes("not found") || msg.includes("Unable to connect")) {
throw new Error(
`Cannot reach Headscale DNS writer (namespace=${cfg.namespace}, pod=${cfg.pod}).
Verify kubectl context and that the Headscale pod is running:
kubectl get pods -n ${cfg.namespace}`
);
}
if (msg.includes("No such file")) {
return [];
}
throw err;
}
}
function registerDnsRecords(cfg, records) {
const json = JSON.stringify(records);
try {
execFileSync2(
"kubectl",
dnsWriterExec(cfg, ["sh", "-c", `cat > '${cfg.filePath}'`], true),
{ input: json, stdio: ["pipe", "pipe", "pipe"] }
);
} catch (err) {
const msg = err.message ?? String(err);
throw new Error(
`Failed to write DNS records to Headscale (namespace=${cfg.namespace}, pod=${cfg.pod}).
${msg.includes("not found") ? "Pod not found. " : ""}Verify kubectl context: kubectl get pods -n ${cfg.namespace}`
);
}
}
function unregisterDnsRecords(cfg, suffix) {
try {
const records = readDnsRecords(cfg);
const filtered = records.filter((r) => !r.name.endsWith(suffix));
registerDnsRecords(cfg, filtered);
} catch {
}
}
var TAILSCALE_PATHS, TAILSCALE_SOCKET_PATHS;
var init_vpn = __esm({
"libs/mesh-cli/src/utils/vpn.ts"() {
"use strict";
TAILSCALE_PATHS = [
"tailscale",
"/Applications/Tailscale.app/Contents/MacOS/Tailscale"
];
TAILSCALE_SOCKET_PATHS = ["/var/run/tailscale/tailscaled.sock", "/tmp/tailscale.sock"];
}
});
// libs/mesh-cli/src/utils/index.ts
var init_utils = __esm({
"libs/mesh-cli/src/utils/index.ts"() {
"use strict";
init_errors();
init_log();
init_context();
init_bastion();
init_credentials();
init_pulumi();
init_vpn();
}
});
// libs/mesh-cli/src/commands/local/seed.ts
function localAwsEnv() {
return {
AWS_ENDPOINT_URL: LOCAL_AWS_ENDPOINT,
AWS_REGION: LOCAL_AWS_REGION,
AWS_ACCESS_KEY_ID: "test",
AWS_SECRET_ACCESS_KEY: "test"
};
}
function buildAppTenantsSeed() {
const tenantExport = {
albGroupName: LOCAL_TENANT,
certificateArn: "",
baseDomain: "localhost",
deployerRoleArn: "arn:aws:iam::000000000000:role/local-dev-deployer",
artifactsBucket: ARTIFACTS_BUCKET,
spicedb: {
endpoint: "localhost:50051",
presharedKeySecret: "local-dev-key"
},
zitadel: {
orgId: "local",
domain: "localhost:8080",
issuer: "http://localhost:8080"
}
};
return {
hubTenant: LOCAL_TENANT,
primaryTenant: tenantExport,
sharedTenants: {}
};
}
async function seedTemporalNamespace() {
await ensureTemporalNamespace(
TEMPORAL_NAMESPACE,
`Mesh local tenant '${LOCAL_TENANT}' env '${LOCAL_ENV}' (seeded by mesh start)`
);
}
async function ensureTemporalNamespace(namespace, description) {
const { Connection } = await import("@temporalio/client");
const connection = await Connection.connect({ address: TEMPORAL_ADDRESS });
try {
await connection.workflowService.registerNamespace({
namespace,
description: description ?? `Mesh local namespace '${namespace}'`,
// protobufjs accepts plain numbers for int64 at runtime; the generated
// typing insists on Long (whose package isn't a direct dep), hence any.
workflowExecutionRetentionPeriod: { seconds: 3 * 24 * 60 * 60 }
});
logInfo(`Registered Temporal namespace '${namespace}' \u2014 waiting for it to become active\u2026`);
const deadline = Date.now() + 2e4;
while (Date.now() < deadline) {
try {
await connection.workflowService.describeTaskQueue({
namespace,
taskQueue: { name: "namespace-propagation-probe" },
taskQueueType: 1
});
break;
} catch {
await new Promise((resolve19) => setTimeout(resolve19, 1e3));
}
}
logSuccess(`Temporal namespace '${namespace}' is active`);
} catch (err) {
if (err?.name === "NamespaceAlreadyExistsError" || /already exists/i.test(err?.message ?? "")) {
logInfo(`Temporal namespace '${namespace}' already exists`);
} else {
throw err;
}
} finally {
await connection.close();
}
}
function buildFabricCheckPayload(targetBytes = 8e3) {
const base = { probe: "advanced-tier-round-trip", tenant: LOCAL_TENANT, env: LOCAL_ENV, pad: "" };
const overhead = JSON.stringify(base).length;
return JSON.stringify({ ...base, pad: "x".repeat(Math.max(0, targetBytes - overhead)) });
}
async function verifyFabric() {
const { SSMClient: SSMClient5, PutParameterCommand, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(AWS_CONFIG);
const payload = buildFabricCheckPayload();
const mainParam = FABRIC_CHECK_PATH;
const childParams = [`${FABRIC_CHECK_PATH}/vpc`, `${FABRIC_CHECK_PATH}/eks`];
try {
await ssm.send(
new PutParameterCommand({
Name: mainParam,
Type: "String",
Tier: "Advanced",
Overwrite: true,
Value: payload
})
);
for (const name of childParams) {
await ssm.send(
new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: `{"probe":"${name}"}` })
);
}
const roundTrip = await ssm.send(new GetParameterCommand2({ Name: mainParam }));
if (roundTrip.Parameter?.Value !== payload) {
throw new MeshCliError(
`Local AWS fabric failed the ${payload.length}-byte Advanced-tier SSM round-trip (got ${roundTrip.Parameter?.Value?.length ?? 0} bytes back).`,
{
remediation: {
docs: "libs/mesh-cli/stack/docker-compose.yml \u2014 swap the ministack image for motoserver/moto (design \xA72.5 fallback)"
}
}
);
}
const byPath = await ssm.send(
new GetParametersByPathCommand3({ Path: FABRIC_CHECK_PATH, Recursive: true })
);
const returned = new Set((byPath.Parameters ?? []).map((p) => p.Name));
const missing = childParams.filter((name) => !returned.has(name));
if (missing.length > 0) {
throw new MeshCliError(
`Local AWS fabric's GetParametersByPath missed child parameter(s): ${missing.join(", ")} (splitKeys exports would not resolve).`,
{
remediation: {
docs: "libs/mesh-cli/stack/docker-compose.yml \u2014 swap the ministack image for motoserver/moto (design \xA72.5 fallback)"
}
}
);
}
logSuccess(`Fabric check passed: ${payload.length}-byte Advanced-tier round-trip + GetParametersByPath`);
} finally {
await ssm.send(new DeleteParametersCommand({ Names: [mainParam, ...childParams] })).catch(() => {
});
}
}
async function registerTenantEnv(tenant, opts = {}) {
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(AWS_CONFIG);
await ssm.send(
new PutParameterCommand({
Name: `/mesh-platform/${tenant}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ name: tenant, tier: opts.tier ?? "shared", subdomain: tenant }),
Description: `Tenant registration (seeded by mesh CLI, local platform)`
})
);
await ssm.send(
new PutParameterCommand({
Name: `/mesh-platform/${tenant}/${LOCAL_ENV}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({
env: LOCAL_ENV,
region: LOCAL_AWS_REGION,
baseDomain: "localhost",
// monitoring is true: the local stack runs the same Loki/Tempo/
// Prometheus backends the hosted platform provides.
services: { temporal: true, zitadel: true, monitoring: true }
}),
Description: `Environment registration (seeded by mesh CLI, local platform)`
})
);
}
async function seedLocalPlatform() {
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(AWS_CONFIG);
await ssm.send(
new PutParameterCommand({
Name: APP_TENANTS_PARAM,
Type: "String",
Overwrite: true,
Value: JSON.stringify(buildAppTenantsSeed()),
Description: `TenantEnvironments exports for ${LOCAL_TENANT}-${LOCAL_ENV} (seeded by mesh start)`
})
);
await registerTenantEnv(LOCAL_TENANT, { tier: "hub" });
logSuccess(`Registered tenant '${LOCAL_TENANT}' env '${LOCAL_ENV}' \u2192 ${APP_TENANTS_PARAM}`);
const { S3Client, CreateBucketCommand, PutObjectCommand } = await import("@aws-sdk/client-s3");
const s3 = new S3Client({ ...AWS_CONFIG, forcePathStyle: true });
for (const bucket of [ARTIFACTS_BUCKET, DATA_BUCKET]) {
try {
await s3.send(new CreateBucketCommand({ Bucket: bucket }));
logSuccess(`Created bucket s3://${bucket}`);
} catch (err) {
if (err?.name === "BucketAlreadyOwnedByYou" || err?.name === "BucketAlreadyExists") {
logInfo(`Bucket s3://${bucket} already exists`);
} else {
throw err;
}
}
}
for (const seed of DATA_BUCKET_SEEDS) {
await s3.send(
new PutObjectCommand({
Bucket: DATA_BUCKET,
Key: seed.key,
Body: seed.body,
ContentType: "application/json"
})
);
}
logSuccess(`Seeded s3://${DATA_BUCKET} with ${DATA_BUCKET_SEEDS.length} sample object(s)`);
await seedTemporalNamespace();
return { parameter: APP_TENANTS_PARAM, bucket: ARTIFACTS_BUCKET };
}
var LOCAL_TENANT, LOCAL_ENV, LOCAL_AWS_ENDPOINT, LOCAL_AWS_REGION, ARTIFACTS_BUCKET, DATA_BUCKET, APP_TENANTS_PARAM, DATA_BUCKET_SEEDS, LOCAL_AWS_CONFIG, AWS_CONFIG, FABRIC_CHECK_PATH, TEMPORAL_NAMESPACE, TEMPORAL_ADDRESS;
var init_seed = __esm({
"libs/mesh-cli/src/commands/local/seed.ts"() {
"use strict";
init_log();
init_errors();
LOCAL_TENANT = "local";
LOCAL_ENV = "dev";
LOCAL_AWS_ENDPOINT = "http://localhost:4566";
LOCAL_AWS_REGION = "us-east-2";
ARTIFACTS_BUCKET = "mesh-local-artifacts";
DATA_BUCKET = "mesh-local-data";
APP_TENANTS_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/app-tenants`;
DATA_BUCKET_SEEDS = [
{
key: "samples/pipes/fragment-0001.json",
body: JSON.stringify(
{
id: "sample-0001",
kind: "pipes-fragment",
account: "0001",
asOf: "2026-01-01",
lines: [{ amount: "125.00", currency: "USD", description: "Sample credit" }]
},
null,
2
)
}
];
LOCAL_AWS_CONFIG = {
endpoint: LOCAL_AWS_ENDPOINT,
region: LOCAL_AWS_REGION,
credentials: { accessKeyId: "test", secretAccessKey: "test" }
};
AWS_CONFIG = LOCAL_AWS_CONFIG;
FABRIC_CHECK_PATH = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/.fabric-check`;
TEMPORAL_NAMESPACE = `${LOCAL_TENANT}-${LOCAL_ENV}`;
TEMPORAL_ADDRESS = "localhost:7233";
}
});
// libs/mesh-cli/src/commands/local/helpers.ts
import * as net from "net";
import { execFileSync as execFileSync3 } from "child_process";
async function upsertLocalSecret(secretId, value, client) {
const { SecretsManagerClient: SecretsManagerClient10, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
const sm = client ?? new SecretsManagerClient10(LOCAL_AWS_CONFIG);
const secretString = JSON.stringify(value);
try {
await sm.send(new CreateSecretCommand4({ Name: secretId, SecretString: secretString }));
} catch (err) {
if (err?.name === "ResourceExistsException") {
await sm.send(new PutSecretValueCommand4({ SecretId: secretId, SecretString: secretString }));
} else {
throw err;
}
}
}
function probeTcp(port, opts = {}) {
const { host = "127.0.0.1", timeoutMs = 2e3 } = opts;
return new Promise((resolve19) => {
const socket = net.connect({ host, port, timeout: timeoutMs });
socket.once("connect", () => {
socket.destroy();
resolve19(true);
});
socket.once("error", () => resolve19(false));
socket.once("timeout", () => {
socket.destroy();
resolve19(false);
});
});
}
function hostPortsOf(endpoints) {
const ports = /* @__PURE__ */ new Set();
const fromUrl = (raw) => {
try {
const u = new URL(raw.includes("://") ? raw : `tcp://${raw}`);
const port = Number(u.port || (u.protocol === "https:" ? 443 : u.protocol === "http:" ? 80 : NaN));
if (Number.isInteger(port) && port > 0) ports.add(port);
} catch {
}
};
for (const e of endpoints) {
if (e.probe.kind === "tcp") ports.add(e.probe.port);
else if (e.probe.kind === "http") fromUrl(e.probe.url);
fromUrl(e.url);
}
return [...ports].sort((a, b) => a - b);
}
function parseDockerPsPorts(output, ignorePrefix) {
const held = /* @__PURE__ */ new Map();
for (const line of output.split("\n")) {
const tab = line.indexOf(" ");
if (tab < 0) continue;
const name = line.slice(0, tab).trim();
if (!name || name.startsWith(ignorePrefix)) continue;
for (const m of line.slice(tab + 1).matchAll(/:(\d+)->\d+\/(?:tcp|udp)/g)) {
const port = Number(m[1]);
if (!held.has(port)) held.set(port, name);
}
}
return held;
}
async function findPortConflicts(ports, ignorePrefix, io = defaultPortConflictIo) {
const unique = [...new Set(ports)].sort((a, b) => a - b);
const bound = (await Promise.all(unique.map(async (port) => await io.listening(port) ? port : null))).filter(
(p) => p !== null
);
if (bound.length === 0) return [];
const containers = parseDockerPsPorts(io.dockerPs(), ignorePrefix);
return bound.map((port) => {
const container = containers.get(port);
if (container) return { port, holder: { kind: "container", name: container } };
const process2 = io.processOn(port);
return { port, holder: process2 ? { kind: "process", name: process2 } : null };
});
}
function describePortConflicts(conflicts, moveHints = /* @__PURE__ */ new Map()) {
const lines = conflicts.flatMap(({ port, holder }) => {
const remedy = holder?.kind === "container" ? `docker stop ${holder.name}` : `lsof -nP -iTCP:${port} -sTCP:LISTEN, then stop what it names`;
const what = holder ? `held by ${holder.kind} ${holder.name}` : "held by something lsof would not name";
const move = moveHints.get(port);
return move ? [` ${String(port).padEnd(5)} ${what.padEnd(38)} (${remedy})`, ` ${"".padEnd(5)} or: ${move}`] : [` ${String(port).padEnd(5)} ${what.padEnd(38)} (${remedy})`];
});
return `Cannot start the local platform \u2014 ${conflicts.length === 1 ? "a host port it needs is" : "host ports it needs are"} already in use:
` + 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';
}
var defaultPortConflictIo;
var init_helpers = __esm({
"libs/mesh-cli/src/commands/local/helpers.ts"() {
"use strict";
init_seed();
defaultPortConflictIo = {
dockerPs: () => {
try {
return execFileSync3("docker", ["ps", "--format", "{{.Names}} {{.Ports}}"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
} catch {
return "";
}
},
processOn: (port) => {
try {
const out = execFileSync3("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fc"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
const cmd = out.split("\n").find((l) => l.startsWith("c"));
return cmd ? cmd.slice(1) : null;
} catch {
return null;
}
},
listening: (port) => probeTcp(port, { timeoutMs: 300 })
};
}
});
// libs/mesh-cli/src/utils/cache-home.ts
import fs3 from "node:fs";
import os from "node:os";
import path3 from "node:path";
function meshCacheHome() {
return process.env.MESH_CACHE_HOME || path3.join(os.homedir(), ".cache", "mesh");
}
function meshCacheDir(...segments) {
const dir = path3.join(meshCacheHome(), ...segments);
fs3.mkdirSync(dir, { recursive: true });
return dir;
}
var init_cache_home = __esm({
"libs/mesh-cli/src/utils/cache-home.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/commands/local/stack.ts
var stack_exports = {};
__export(stack_exports, {
COMPOSE_PROJECT: () => COMPOSE_PROJECT,
DEFAULT_HUB_PORT: () => DEFAULT_HUB_PORT,
HUB_OVERLAY_FILE: () => HUB_OVERLAY_FILE,
ONE_SHOT_SERVICES: () => ONE_SHOT_SERVICES,
STACK_ENDPOINTS: () => STACK_ENDPOINTS,
compose: () => compose,
composeLogPath: () => composeLogPath,
composeStreamed: () => composeStreamed,
crashedServices: () => crashedServices,
ensureDockerAvailable: () => ensureDockerAvailable,
findPackageRoot: () => findPackageRoot,
hubApiRunning: () => hubApiRunning,
hubEndpoints: () => hubEndpoints,
hubOverlayRunning: () => hubOverlayRunning,
hubOverlayServices: () => hubOverlayServices,
hubPort: () => hubPort,
localLogsDir: () => localLogsDir,
localProbesDir: () => localProbesDir,
parseComposePs: () => parseComposePs,
parseComposeServiceNames: () => parseComposeServiceNames,
probeEndpoint: () => probeEndpoint,
stackDir: () => stackDir,
stackOwnedElsewhere: () => stackOwnedElsewhere,
stackServices: () => stackServices,
summarizeComposeFailure: () => summarizeComposeFailure,
writeAppServiceProbes: () => writeAppServiceProbes
});
import { execFileSync as execFileSync4, spawn, spawnSync } from "child_process";
import * as fs4 from "fs";
import * as path4 from "path";
import { fileURLToPath } from "url";
import { parse as parseYaml } from "yaml";
function localLogsDir() {
return meshCacheDir("mesh-local", "logs");
}
function localProbesDir() {
return meshCacheDir("mesh-local", "probes");
}
function writeAppServiceProbes(args) {
const entries = Object.entries(args.services);
if (entries.length === 0) return void 0;
const healthPath = args.healthPath ?? "/health";
const targets = entries.map(([name, port]) => ({
targets: [`http://host.docker.internal:${port}${healthPath}`],
labels: {
type: "service",
tenant: args.tenant,
env: args.env,
app: args.app,
service: name,
target: name
}
}));
const file = path4.join(localProbesDir(), `${args.tenant}-${args.app}-services.json`);
fs4.writeFileSync(file, `${JSON.stringify(targets, null, 2)}
`);
return file;
}
function hubPort() {
const raw = process.env.MESH_HUB_PORT?.trim();
if (!raw) return DEFAULT_HUB_PORT;
if (!/^\d+$/.test(raw) || Number(raw) < 1 || Number(raw) > 65535) {
throw new MeshCliError(`MESH_HUB_PORT must be a TCP port number 1\u201365535 (got '${raw}').`, {
remediation: { command: "unset MESH_HUB_PORT # or export a valid port, e.g. 9100" }
});
}
return raw;
}
function hubEndpoints() {
return [
{
service: "hub-api",
label: "Hub API",
url: "http://localhost:4568",
probe: { kind: "http", url: "http://localhost:4568/health" }
},
{
service: "hub-ui",
label: "Hub UI",
url: `http://localhost:${hubPort()}`,
probe: { kind: "http", url: `http://localhost:${hubPort()}/ping` },
hint: "sign in: admin@local.mesh or dev@local.mesh / LocalDev1! (oauth2-proxy, same as deployed)"
}
];
}
function findPackageRoot(startDir) {
let dir = startDir ?? path4.dirname(fileURLToPath(import.meta.url));
for (let i = 0; i < 8; i++) {
const pkgPath = path4.join(dir, "package.json");
if (fs4.existsSync(pkgPath)) {
try {
const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
if (pkg.name === "@mesh-tech/mesh-cli") return dir;
} catch {
}
}
const parent = path4.dirname(dir);
if (parent === dir) break;
dir = parent;
}
throw new MeshCliError("Could not locate the mesh-cli package root (stack assets missing).", {
remediation: { command: "npm install -g @mesh-tech/mesh-cli" }
});
}
function stackDir() {
const dir = path4.join(findPackageRoot(), "stack");
if (!fs4.existsSync(path4.join(dir, "docker-compose.yml"))) {
throw new MeshCliError(`Local stack assets not found at ${dir}.`, {
remediation: { command: "npm install -g @mesh-tech/mesh-cli" }
});
}
return dir;
}
function parseComposeServiceNames(yamlText) {
const doc = parseYaml(yamlText);
return Object.keys(doc?.services ?? {});
}
function hubOverlayServices() {
if (!hubOverlayServicesCache) {
const file = path4.join(stackDir(), HUB_OVERLAY_FILE);
if (!fs4.existsSync(file)) {
throw new MeshCliError(`Local stack assets are incomplete: ${file} is missing.`, {
remediation: { command: "npm install -g @mesh-tech/mesh-cli" }
});
}
hubOverlayServicesCache = new Set(parseComposeServiceNames(fs4.readFileSync(file, "utf-8")));
}
return hubOverlayServicesCache;
}
function stackOwnedElsewhere() {
try {
const first = compose(["ps", "-q"]).trim().split("\n").filter(Boolean)[0];
if (!first) return void 0;
const label = execFileSync4(
"docker",
["inspect", first, "--format", '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'],
{ encoding: "utf-8" }
).trim();
if (!label) return void 0;
const real = (p) => {
try {
return fs4.realpathSync(p);
} catch {
return path4.resolve(p);
}
};
return real(label) === real(stackDir()) ? void 0 : label;
} catch {
return void 0;
}
}
function ensureDockerAvailable() {
const probe = spawnSync("docker", ["info", "--format", "{{.ServerVersion}}"], {
stdio: ["ignore", "pipe", "pipe"]
});
if (probe.error || probe.status !== 0) {
throw new MeshCliError("Docker is not available (is Docker Desktop / the docker daemon running?).", {
remediation: { docs: "https://docs.docker.com/get-docker/" }
});
}
}
function compose(args, opts = {}) {
const dir = stackDir();
const files = ["-f", path4.join(dir, "docker-compose.yml")];
if (opts.hub) files.push("-f", path4.join(dir, "docker-compose.hub.yml"));
const fullArgs = ["compose", "-p", COMPOSE_PROJECT, ...files, ...args];
return execFileSync4("docker", fullArgs, {
cwd: dir,
encoding: "utf-8",
// MESH_LOCAL_LOGS on every call: the compose file mounts it into the
// OTel collector, and an unset variable would both warn and resolve to
// a different mount (recreate churn between CLI and raw compose runs).
// IGNORE_ORPHANS: base-only `up` calls would otherwise flag the hub
// overlay's running containers as orphans.
env: {
...process.env,
MESH_LOCAL_LOGS: localLogsDir(),
MESH_LOCAL_PROBES: localProbesDir(),
COMPOSE_IGNORE_ORPHANS: "1",
...opts.env
},
stdio: opts.inherit ? ["ignore", "inherit", "inherit"] : ["ignore", "pipe", "pipe"]
});
}
function summarizeComposeFailure(output) {
const lines = output.split("\n").map((l) => l.trim()).filter(Boolean);
const errorish = lines.filter(
(l) => /\b(error|failed|failure|fatal|denied|unauthorized|cannot|no such)\b/i.test(l)
);
const line = errorish.at(-1) ?? lines.at(-1) ?? "no output captured";
return line.length > 300 ? `${line.slice(0, 297)}\u2026` : line;
}
function composeLogPath(op) {
const dir = meshCacheDir("mesh-local");
return path4.join(dir, `compose-${op.replace(/[^a-z0-9-]/gi, "_")}.log`);
}
async function composeStreamed(args, opts = {}) {
const dir = stackDir();
const files = ["-f", path4.join(dir, "docker-compose.yml")];
if (opts.hub) files.push("-f", path4.join(dir, "docker-compose.hub.yml"));
const fullArgs = ["compose", "-p", COMPOSE_PROJECT, ...files, ...args];
const env = {
...process.env,
MESH_LOCAL_LOGS: localLogsDir(),
MESH_LOCAL_PROBES: localProbesDir(),
COMPOSE_IGNORE_ORPHANS: "1",
...opts.env
};
const op = args[0] ?? "cmd";
if (process.stderr.isTTY) {
const res = spawnSync("docker", fullArgs, {
cwd: dir,
env,
stdio: ["ignore", "inherit", "inherit"]
});
if (res.status !== 0) {
throw new MeshCliError(
`docker compose ${op} failed (exit ${res.status ?? "?"}) \u2014 the compose output above has the details.`,
{
remediation: {
command: `docker compose -p ${COMPOSE_PROJECT} ps && docker compose -p ${COMPOSE_PROJECT} logs --tail 50`
}
}
);
}
return;
}
const logPath2 = composeLogPath(op);
const logStream = fs4.createWriteStream(logPath2);
const heartbeat = startHeartbeat(`docker compose ${args.join(" ")}`);
let captured = "";
let exitCode;
try {
exitCode = await new Promise((resolve19, reject) => {
const child = spawn("docker", fullArgs, {
cwd: dir,
env,
stdio: ["ignore", "pipe", "pipe"]
});
const consume = (chunk) => {
heartbeat.touch();
const text = chunk.toString();
captured += text;
logStream.write(text);
process.stderr.write(text);
};
child.stdout.on("data", consume);
child.stderr.on("data", consume);
child.on("error", reject);
child.on("close", (code) => resolve19(code ?? 1));
});
} finally {
heartbeat.stop();
await new Promise((resolve19) => logStream.end(resolve19));
}
if (exitCode !== 0) {
throw new MeshCliError(
`docker compose ${op} failed (exit ${exitCode}): ${summarizeComposeFailure(captured)}`,
{
remediation: {
command: `docker compose -p ${COMPOSE_PROJECT} logs --tail 50`,
docs: logPath2
}
}
);
}
}
function parseComposePs(output) {
const services = [];
for (const line of output.split("\n")) {
const trimmed = line.trim();
if (!trimmed) continue;
try {
const entry = JSON.parse(trimmed);
services.push({
name: entry.Service ?? entry.Name ?? "unknown",
state: entry.State ?? "unknown",
health: entry.Health || void 0
});
} catch {
}
}
return services;
}
function stackServices() {
return parseComposePs(compose(["ps", "-a", "--format", "json"], { hub: true }));
}
function crashedServices(services, opts) {
return services.filter(
(s) => s.state === "exited" && !ONE_SHOT_SERVICES.has(s.name) && (opts.overlayStarted || !opts.overlayServices.has(s.name))
);
}
function hubOverlayRunning(services, overlayServices) {
return services.some((s) => overlayServices.has(s.name) && s.state === "running");
}
function hubApiRunning(services) {
return services.some((s) => s.name === "hub-api" && s.state === "running");
}
async function probeEndpoint(endpoint) {
if (endpoint.probe.kind === "http") {
try {
const res = await fetch(endpoint.probe.url, { signal: AbortSignal.timeout(3e3) });
return res.status < 500;
} catch {
return false;
}
}
if (endpoint.probe.kind === "tcp") {
return probeTcp(endpoint.probe.port, { timeoutMs: 3e3 });
}
return true;
}
var COMPOSE_PROJECT, STACK_ENDPOINTS, DEFAULT_HUB_PORT, HUB_OVERLAY_FILE, hubOverlayServicesCache, ONE_SHOT_SERVICES;
var init_stack = __esm({
"libs/mesh-cli/src/commands/local/stack.ts"() {
"use strict";
init_helpers();
init_cache_home();
init_errors();
init_log();
COMPOSE_PROJECT = "mesh-local";
STACK_ENDPOINTS = [
{
service: "temporal",
label: "Temporal gRPC",
url: "localhost:7233",
probe: { kind: "tcp", port: 7233 }
},
{
service: "temporal-ui",
label: "Temporal UI",
url: "http://localhost:8233/namespaces/local-dev/workflows",
probe: { kind: "http", url: "http://localhost:8233" },
hint: "seeded namespace: local-dev"
},
{
service: "zitadel",
label: "Zitadel console",
url: "http://localhost:8080",
probe: { kind: "http", url: "http://localhost:8080/debug/healthz" },
hint: "admin@local.mesh / LocalDev1!"
},
{
service: "mailpit",
label: "Mailbox (local mail)",
url: "http://localhost:8025",
probe: { kind: "http", url: "http://localhost:8025/readyz" },
hint: "every activation + password-reset mail Zitadel sends locally lands here"
},
{
service: "database",
label: "Postgres",
url: "postgres://postgres:postgres@localhost:5433",
probe: { kind: "tcp", port: 5433 },
hint: "databases: temporal, app, spicedb, hub"
},
{
service: "spicedb",
label: "SpiceDB gRPC",
url: "localhost:50051",
probe: { kind: "tcp", port: 50051 },
hint: "preshared key: local-dev-key"
},
{
service: "ministack",
label: "ministack (local AWS endpoint)",
url: "http://localhost:4566",
probe: { kind: "http", url: "http://localhost:4566/_ministack/health" },
hint: "SSM registry under /mesh-platform/local/dev"
},
{
service: "stackport",
label: "StackPort (local AWS console)",
url: "http://localhost:4567",
probe: { kind: "http", url: "http://localhost:4567" },
hint: "browse the registry (SSM), secrets, S3 artifacts"
},
{
service: "memcached",
label: "Memcached",
url: "localhost:11211",
probe: { kind: "tcp", port: 11211 }
},
{
service: "loki",
label: "Loki (logs)",
url: "http://localhost:3100",
probe: { kind: "http", url: "http://localhost:3100/ready" },
hint: "mesh dev --local service logs, hosted label scheme"
},
{
service: "tempo",
label: "Tempo (traces)",
url: "http://localhost:3200",
probe: { kind: "http", url: "http://localhost:3200/ready" }
},
{
service: "prometheus",
label: "Prometheus (metrics)",
url: "http://localhost:9090",
probe: { kind: "http", url: "http://localhost:9090/-/ready" }
},
{
service: "otel-collector",
label: "OTel collector (OTLP in)",
url: "http://localhost:4318",
probe: { kind: "http", url: "http://localhost:13133" },
hint: "apps: OTEL_EXPORTER_OTLP_ENDPOINT (injected by mesh dev --local)"
},
{
service: "elasticsearch",
label: "OpenSearch (Temporal visibility)",
url: "http://localhost:9200",
probe: { kind: "http", url: "http://localhost:9200" }
}
];
DEFAULT_HUB_PORT = "9000";
HUB_OVERLAY_FILE = "docker-compose.hub.yml";
ONE_SHOT_SERVICES = /* @__PURE__ */ new Set(["spicedb-migrate", "zitadel-machinekey-init"]);
}
});
// libs/mesh-cli/src/commands/skills.ts
import * as fs5 from "fs";
import * as path5 from "path";
import { execFileSync as execFileSync5 } from "child_process";
function resolveTargetRoot(startDir = process.cwd()) {
try {
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
cwd: startDir,
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
}).trim();
} catch {
return startDir;
}
}
function cliAsset(...segments) {
return path5.join(findPackageRoot(), ...segments);
}
function listBaseSkills() {
const dir = cliAsset("skills");
if (!fs5.existsSync(dir)) return [];
return fs5.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs5.existsSync(path5.join(dir, entry.name, "SKILL.md"))).map((entry) => ({ name: entry.name, source: path5.join(dir, entry.name, "SKILL.md") }));
}
function renderManagedSkill(source) {
const raw = fs5.readFileSync(source, "utf-8");
const frontmatter = raw.match(/^(---\n[\s\S]*?\n---\n)/)?.[1];
if (!frontmatter) return `${MANAGED_MARKER}
${raw}`;
return `${frontmatter}
${MANAGED_MARKER}
${raw.slice(frontmatter.length)}`;
}
function meshTechScopeDirs(root, maxDepth = 6) {
const scopes = [];
const walk2 = (dir, depth) => {
if (depth > maxDepth) return;
let entries;
try {
entries = fs5.readdirSync(dir, { withFileTypes: true });
} catch {
return;
}
for (const entry of entries) {
if (!entry.isDirectory()) continue;
if (entry.name === "node_modules") {
const scope = path5.join(dir, entry.name, "@mesh-tech");
if (fs5.existsSync(scope)) scopes.push(scope);
continue;
}
if (entry.name.startsWith(".")) continue;
walk2(path5.join(dir, entry.name), depth + 1);
}
};
walk2(root, 0);
return scopes;
}
function listPackageSkills(root) {
const byName = /* @__PURE__ */ new Map();
for (const scopeDir of meshTechScopeDirs(root)) {
for (const pkg of fs5.readdirSync(scopeDir, { withFileTypes: true })) {
if (!pkg.isDirectory() && !pkg.isSymbolicLink()) continue;
if (pkg.name === BASE_SKILL_PACKAGE) continue;
const skillsDir = path5.join(scopeDir, pkg.name, "skills");
if (!fs5.existsSync(skillsDir)) continue;
for (const domain of fs5.readdirSync(skillsDir, { withFileTypes: true })) {
const source = path5.join(skillsDir, domain.name, "SKILL.md");
if (!fs5.existsSync(source)) continue;
const name = `mesh-${pkg.name}-${domain.name}`;
if (!byName.has(name)) byName.set(name, { name, dir: path5.join(skillsDir, domain.name), source });
}
}
}
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
}
function copySkillExtras(sourceDir, targetDir) {
for (const entry of fs5.readdirSync(sourceDir, { withFileTypes: true })) {
if (entry.name === "SKILL.md") continue;
fs5.cpSync(path5.join(sourceDir, entry.name), path5.join(targetDir, entry.name), { recursive: true });
}
}
function seedAppSkill(root, appName, appRelPath) {
const target = path5.join(root, ".claude", "skills", appName, "SKILL.md");
if (fs5.existsSync(target)) return null;
const template = fs5.readFileSync(cliAsset("assets", "app-skill", "SKILL.md"), "utf-8");
const body = template.replaceAll("__APP_NAME__", appName).replaceAll("__APP_PATH__", appRelPath || ".");
fs5.mkdirSync(path5.dirname(target), { recursive: true });
fs5.writeFileSync(target, body);
return path5.relative(root, target);
}
function planSync(root) {
const items = [];
for (const skill of listBaseSkills()) {
const target = path5.join(root, ".claude", "skills", `mesh-${skill.name}`, "SKILL.md");
const desired = renderManagedSkill(skill.source);
const label = `.claude/skills/mesh-${skill.name}/SKILL.md (base skill)`;
if (!fs5.existsSync(target)) {
items.push({
label,
state: "write",
apply: () => {
fs5.mkdirSync(path5.dirname(target), { recursive: true });
fs5.writeFileSync(target, desired);
}
});
} else {
const current = fs5.readFileSync(target, "utf-8");
if (current === desired) {
items.push({ label, state: "ok" });
} else if (current.includes(MANAGED_MARKER)) {
items.push({ label, state: "write", apply: () => fs5.writeFileSync(target, desired) });
} else {
items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
}
}
}
const packageSkills = listPackageSkills(root);
for (const skill of packageSkills) {
const targetDir = path5.join(root, ".claude", "skills", skill.name);
const target = path5.join(targetDir, "SKILL.md");
const desired = renderManagedSkill(skill.source);
const label = `.claude/skills/${skill.name}/SKILL.md (platform skill)`;
if (!fs5.existsSync(target)) {
items.push({
label,
state: "write",
apply: () => {
fs5.mkdirSync(targetDir, { recursive: true });
fs5.writeFileSync(target, desired);
copySkillExtras(skill.dir, targetDir);
}
});
} else {
const current = fs5.readFileSync(target, "utf-8");
if (current === desired) {
items.push({ label, state: "ok" });
} else if (current.includes(MANAGED_MARKER)) {
items.push({
label,
state: "write",
apply: () => {
fs5.writeFileSync(target, desired);
copySkillExtras(skill.dir, targetDir);
}
});
} else {
items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
}
}
}
if (packageSkills.length === 0 && meshTechScopeDirs(root).length === 0) {
logInfo("No @mesh-tech packages installed yet \u2014 run `pnpm install`, then `mesh skills sync` again for the platform skills.");
}
const hookSource = cliAsset("assets", "intent", "intent-claude-gate.mjs");
const hookTarget = path5.join(root, HOOK_RELATIVE);
const hookDesired = fs5.readFileSync(hookSource, "utf-8");
const hookCurrent = fs5.existsSync(hookTarget) ? fs5.readFileSync(hookTarget, "utf-8") : null;
items.push(
hookCurrent === hookDesired ? { label: `${HOOK_RELATIVE} (Intent gate)`, state: "ok" } : {
label: `${HOOK_RELATIVE} (Intent gate)`,
state: "write",
apply: () => {
fs5.mkdirSync(path5.dirname(hookTarget), { recursive: true });
fs5.writeFileSync(hookTarget, hookDesired, { mode: 493 });
}
}
);
const rootPkgPath = path5.join(root, "package.json");
if (fs5.existsSync(rootPkgPath)) {
let rootPkg = null;
try {
rootPkg = JSON.parse(fs5.readFileSync(rootPkgPath, "utf-8"));
} catch {
rootPkg = null;
}
const label = "package.json (@tanstack/intent devDependency)";
if (!rootPkg) {
items.push({ label: `${label} \u2014 package.json is unparseable, leaving as-is`, state: "skip" });
} else if (rootPkg.devDependencies?.["@tanstack/intent"] || rootPkg.dependencies?.["@tanstack/intent"]) {
items.push({ label, state: "ok" });
} else {
items.push({
label,
// Writing the dependency does not install it. Bare `mesh skills sync`
// in an already-installed repo leaves `pnpm exec intent` unresolvable
// until the next install, so say so rather than reporting success.
remediation: "Added @tanstack/intent \u2014 run `pnpm install` so `pnpm exec intent` resolves.",
state: "write",
apply: () => {
rootPkg.devDependencies = rootPkg.devDependencies ?? {};
rootPkg.devDependencies["@tanstack/intent"] = INTENT_RANGE;
rootPkg.devDependencies = Object.fromEntries(
Object.entries(rootPkg.devDependencies).sort(([a], [b]) => a.localeCompare(b))
);
fs5.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + "\n");
}
});
}
}
const settingsPath = path5.join(root, ".claude", "settings.json");
let settings = {};
try {
settings = JSON.parse(fs5.readFileSync(settingsPath, "utf-8"));
} catch {
settings = {};
}
const sessionStart = settings?.hooks?.SessionStart ?? [];
const hasHook = sessionStart.some(
(entry) => (entry?.hooks ?? []).some((h) => JSON.stringify(h?.args ?? h?.command ?? "").includes("intent-claude-gate.mjs"))
);
items.push(
hasHook ? { label: ".claude/settings.json (SessionStart Intent hook)", state: "ok" } : {
label: ".claude/settings.json (SessionStart Intent hook)",
state: "write",
apply: () => {
settings.hooks = settings.hooks ?? {};
settings.hooks.SessionStart = settings.hooks.SessionStart ?? [];
settings.hooks.SessionStart.push({
matcher: "startup|resume|clear|compact",
hooks: [
{
type: "command",
command: "node",
args: ["${CLAUDE_PROJECT_DIR}/.intent/hooks/intent-claude-gate.mjs"],
timeout: 10,
statusMessage: "Loading Intent skill catalog"
}
]
});
fs5.mkdirSync(path5.dirname(settingsPath), { recursive: true });
fs5.writeFileSync(settingsPath, JSON.stringify(settings, null, 1) + "\n");
}
}
);
const agentsPath = path5.join(root, "AGENTS.md");
const fence2 = fs5.readFileSync(cliAsset("assets", "intent", "agents-fence.md"), "utf-8");
const agentsCurrent = fs5.existsSync(agentsPath) ? fs5.readFileSync(agentsPath, "utf-8") : null;
items.push(
agentsCurrent?.includes(FENCE_START) ? { label: "AGENTS.md (intent-skills fence)", state: "ok" } : {
label: "AGENTS.md (intent-skills fence)",
state: "write",
apply: () => {
const next = agentsCurrent ? `${fence2}
${agentsCurrent}` : `${fence2}
# Agent Instructions
`;
fs5.writeFileSync(agentsPath, next);
}
}
);
return items;
}
function syncSkills(root, opts = {}) {
const items = planSync(root);
let dirty = false;
for (const item of items) {
if (item.state === "ok") continue;
if (item.state === "skip") {
logWarn(item.label);
continue;
}
dirty = true;
if (opts.check) {
logWarn(`missing/stale: ${item.label}`);
} else {
item.apply?.();
logSuccess(`synced: ${item.label}`);
if (item.remediation) logInfo(item.remediation);
}
}
if (!dirty) {
logInfo(`Agent skills are in sync (${root})`);
}
return !dirty;
}
function registerSkillsCommands(program2) {
const skills = program2.command("skills").description("Agent-skill distribution (base skills + Intent discovery)");
skills.command("sync").description(
"Install the base building-with-Mesh skills into .claude/skills/, fetch the platform skills shipped by the repo's installed @mesh-tech/* packages, and wire TanStack-Intent discovery (intent devDependency + hook + settings + AGENTS.md fence)"
).option("--check", "verify only (CI/doctor): exit 1 when anything is missing or stale", false).option("--root <path>", "target repo root (default: enclosing git root)").action((opts) => {
const root = path5.resolve(opts.root ?? resolveTargetRoot());
const ok = syncSkills(root, { check: opts.check });
if (opts.check && !ok) {
throw new MeshCliError("Agent skills are missing or stale.", {
remediation: { command: "mesh skills sync" }
});
}
});
}
var MANAGED_MARKER, FENCE_START, HOOK_RELATIVE, INTENT_RANGE, BASE_SKILL_PACKAGE;
var init_skills = __esm({
"libs/mesh-cli/src/commands/skills.ts"() {
"use strict";
init_log();
init_errors();
init_stack();
MANAGED_MARKER = "<!-- managed-by: mesh skills sync \u2014 edits are overwritten; copy content elsewhere to customize -->";
FENCE_START = "<!-- intent-skills:start -->";
HOOK_RELATIVE = path5.join(".intent", "hooks", "intent-claude-gate.mjs");
INTENT_RANGE = "^0.3.6";
BASE_SKILL_PACKAGE = "mesh-cli";
}
});
// libs/mesh-cli/src/utils/first-party-contexts.ts
function firstPartyDomainFor(context) {
if (!Object.hasOwn(FIRST_PARTY_CONTEXTS, context)) return null;
const domain = FIRST_PARTY_CONTEXTS[context];
return typeof domain === "string" && domain ? domain : null;
}
var FIRST_PARTY_CONTEXTS;
var init_first_party_contexts = __esm({
"libs/mesh-cli/src/utils/first-party-contexts.ts"() {
"use strict";
FIRST_PARTY_CONTEXTS = Object.freeze({
// Verified 2026-08-14: https://cli.dev.platform.meshtech.io/.well-known/mesh.json
// answers with the platform's issuer + CLI client id + registryBroker,
// anonymously. (Repointed from dev.trabian.meshtech.io — the trabian tenant
// platform lives in a different AWS account with no registry; the
// platform-dev stack in the registry-owning account is the developer front
// door. mesh-sandbox#32 / #3587.)
"mesh.dev": "dev.platform.meshtech.io"
});
}
});
// libs/mesh-cli/src/utils/pid.ts
function unsignalableReason(pid) {
if (typeof pid !== "number") {
return `expected an integer, got ${typeof pid} (${String(pid)})`;
}
if (!Number.isInteger(pid)) {
return `expected an integer, got ${String(pid)}`;
}
if (pid === 0) {
return "0 is not a pid \u2014 kill(0) signals every process in this shell's own process group";
}
if (pid === 1) {
return "1 is init \u2014 and teardown negates first, so kill(-1) would signal every process you can signal";
}
if (pid < MIN_SIGNALABLE_PID) {
return `${pid} is negative \u2014 that is already a process-group id, not a pid`;
}
if (pid === process.pid) {
return `${pid} is this CLI process itself \u2014 signalling it (or its group) would kill the command doing the teardown`;
}
if (pid === process.ppid) {
return `${pid} is this CLI's parent (your shell) \u2014 signalling its group would kill your terminal session`;
}
return null;
}
function assertSignalablePid(pid, source) {
const reason = unsignalableReason(pid);
if (reason === null) return;
throw new MeshCliError(
`Refusing to signal ${source.what} \u2014 ${reason}. ` + (source.file ? `The recorded value is corrupt or stale; signalling it could terminate unrelated processes.` : `Signalling it could terminate unrelated processes.`),
source.file ? { remediation: { command: `rm ${source.file}` } } : {}
);
}
var MIN_SIGNALABLE_PID;
var init_pid = __esm({
"libs/mesh-cli/src/utils/pid.ts"() {
"use strict";
init_errors();
MIN_SIGNALABLE_PID = 2;
}
});
// libs/mesh-cli/src/utils/tailscale-targets.ts
import { execFileSync as execFileSync6 } from "node:child_process";
import * as fs6 from "node:fs";
import * as path6 from "node:path";
function tenantPortIndex(tenant, registryPath) {
const p = registryPath ?? path6.join(CONFIG_DIR, "tailscale", "port-registry.json");
let reg;
try {
reg = JSON.parse(fs6.readFileSync(p, "utf8"));
} catch {
reg = { mesh: 0 };
}
if (tenant in reg) return reg[tenant];
const idx = Math.max(-1, ...Object.values(reg)) + 1;
reg[tenant] = idx;
fs6.mkdirSync(path6.dirname(p), { recursive: true });
atomicWriteFileSync(p, JSON.stringify(reg, null, 2), 384);
return idx;
}
function resolveTunnelTargets(services, resolveIp, portOffset = 0) {
const reserved = /* @__PURE__ */ new Set();
const targets = [];
for (const [key, svc] of Object.entries(services)) {
const name = TUNNEL_NAME_BY_BASTION_KEY[key];
if (!name) continue;
const targetIp = resolveIp(svc.host);
if (!targetIp) continue;
const localPort = reserveSsmLocalPortCandidate(
svc.port,
reserved,
preferredSsmLocalPort(svc.port) + portOffset
);
targets.push({ name, localPort, targetHost: svc.host, targetIp, remotePort: svc.port });
}
return targets;
}
function resolveElbIp(host) {
try {
const out = execFileSync6("dig", ["+short", host, "@1.1.1.1"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
const ip = out.trim().split("\n").map((l) => l.trim()).find((l) => /^\d+\.\d+\.\d+\.\d+$/.test(l));
return ip ?? null;
} catch {
return null;
}
}
var PORT_BLOCK, TUNNEL_NAME_BY_BASTION_KEY;
var init_tailscale_targets = __esm({
"libs/mesh-cli/src/utils/tailscale-targets.ts"() {
"use strict";
init_dev();
init_login();
PORT_BLOCK = 3e3;
TUNNEL_NAME_BY_BASTION_KEY = {
"temporal-frontend": "temporal",
"temporal-ui": "temporal-ui",
rds: "rds"
};
}
});
// libs/mesh-cli/src/utils/socks-forward.ts
import * as net2 from "node:net";
function ipv4ToBytes(ip) {
const parts = ip.split(".");
if (parts.length !== 4) throw new Error(`not an IPv4 address: ${ip}`);
return parts.map((p) => {
const n = Number(p);
if (!Number.isInteger(n) || n < 0 || n > 255 || !/^\d+$/.test(p)) {
throw new Error(`not an IPv4 address: ${ip}`);
}
return n;
});
}
function socks5ConnectRequest(ip, port) {
return Buffer.from([5, 1, 0, 1, ...ipv4ToBytes(ip), port >> 8 & 255, port & 255]);
}
function interpretSocksReply(code) {
return { ok: code === 0, message: SOCKS_REPLY_MESSAGES[code] ?? `unknown SOCKS reply ${code}` };
}
function createForwarder(opts) {
const socksHost = opts.socksHost ?? "127.0.0.1";
const server = net2.createServer((client) => {
const up = net2.connect(opts.socksPort, socksHost);
let buf = Buffer.alloc(0);
let phase = 0;
up.on("connect", () => up.write(Buffer.from([5, 1, 0])));
up.on("data", (d) => {
if (phase === 2) return;
buf = Buffer.concat([buf, d]);
if (phase === 0) {
if (buf.length < 2) return;
buf = buf.subarray(2);
phase = 1;
up.write(socks5ConnectRequest(opts.targetIp, opts.targetPort));
}
if (phase === 1) {
if (buf.length < 10) return;
const reply = interpretSocksReply(buf[1]);
const leftover = buf.subarray(10);
phase = 2;
if (!reply.ok) {
up.destroy();
client.destroy();
return;
}
if (leftover.length) client.write(leftover);
client.pipe(up);
up.pipe(client);
}
});
client.on("error", () => up.destroy());
up.on("error", () => client.destroy());
client.on("close", () => up.destroy());
up.on("close", () => client.destroy());
});
server.on("error", (err) => opts.onError?.(err));
server.listen(opts.listenPort, "127.0.0.1");
return server;
}
var SOCKS_REPLY_MESSAGES;
var init_socks_forward = __esm({
"libs/mesh-cli/src/utils/socks-forward.ts"() {
"use strict";
SOCKS_REPLY_MESSAGES = {
0: "succeeded",
1: "general SOCKS server failure",
2: "connection not allowed by ruleset",
3: "network unreachable",
4: "host unreachable",
5: "connection refused",
6: "TTL expired",
7: "command not supported",
8: "address type not supported"
};
}
});
// libs/mesh-cli/src/utils/tunnel-ownership.ts
import * as crypto from "node:crypto";
import * as fs7 from "node:fs";
import * as net3 from "node:net";
import * as path7 from "node:path";
import { execFileSync as execFileSync7 } from "node:child_process";
function mintRunnerToken() {
return crypto.randomBytes(16).toString("hex");
}
function readRunnerManifest(file) {
if (!fs7.existsSync(file)) return null;
try {
return JSON.parse(fs7.readFileSync(file, "utf8"));
} catch {
return null;
}
}
function writeRunnerManifest(file, manifest) {
fs7.mkdirSync(path7.dirname(file), { recursive: true });
fs7.writeFileSync(file, JSON.stringify(manifest, null, 2), { mode: 384 });
}
function clearRunnerManifest(file, token) {
if (token !== void 0 && readRunnerManifest(file)?.token !== token) return;
try {
fs7.unlinkSync(file);
} catch {
}
}
function getProcessGroupId(pid) {
try {
const out = execFileSync7("ps", ["-o", "pgid=", "-p", String(pid)], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
}).trim();
const pgid = Number(out);
return Number.isInteger(pgid) && pgid > 0 ? pgid : null;
} catch {
return null;
}
}
function parseRunnerPidsFromPs(psOutput, tenant, exclude) {
const pids = [];
for (const line of psOutput.split("\n")) {
if (!/vpn\s+tunnel\s+(?:__run|__supervise)\s+/.test(line)) continue;
const m = line.trim().match(/^(\d+)\s+(.*)$/);
if (!m) continue;
const argv = m[2].split(/\s+/);
const i = argv.findIndex((a) => a === "__run" || a === "__supervise");
if (i === -1 || argv[i + 1] !== tenant) continue;
const pid = Number(m[1]);
if (pid !== exclude) pids.push(pid);
}
return pids;
}
function discoverRunnerPids(tenant, exclude) {
try {
const out = execFileSync7("ps", ["ax", "-o", "pid=,command="], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
return parseRunnerPidsFromPs(out, tenant, exclude);
} catch {
return [];
}
}
function startControlServer(payload) {
return new Promise((resolve19, reject) => {
const server = net3.createServer((socket) => {
socket.on("error", () => {
});
try {
socket.end(`${JSON.stringify(payload())}
`);
} catch {
socket.destroy();
}
});
server.on("error", reject);
server.listen(0, "127.0.0.1", () => {
resolve19({ server, port: server.address().port });
});
});
}
function queryControl(port, timeoutMs = 700) {
return new Promise((resolve19) => {
const s = new net3.Socket();
let buf = "";
let done = false;
const fin = (v) => {
if (done) return;
done = true;
s.destroy();
resolve19(v);
};
s.setTimeout(timeoutMs);
s.once("timeout", () => fin(null));
s.once("error", () => fin(null));
s.on("data", (d) => {
buf += d.toString();
});
s.once("close", () => {
try {
fin(JSON.parse(buf));
} catch {
fin(null);
}
});
s.connect(port, "127.0.0.1");
});
}
function defaultPidAlive(pid) {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
async function verifyRunnerOwnership(args) {
const { manifest } = args;
if (!manifest) return { ok: false, reason: "no-manifest" };
const alive = args.isPidAlive ?? defaultPidAlive;
if (!alive(manifest.pid)) return { ok: false, reason: "runner-dead" };
const resp = await (args.query ?? queryControl)(manifest.controlPort);
if (!resp) return { ok: false, reason: "no-control-answer" };
if (resp.token !== manifest.token) return { ok: false, reason: "token-mismatch", resp };
if (resp.socksPort !== args.expectedSocksPort) {
return { ok: false, reason: "socks-port-mismatch", resp };
}
const listening = new Set(resp.listening);
const missing = args.expectedPorts.filter((p) => !listening.has(p));
if (missing.length > 0) {
return { ok: false, reason: `ports-not-listening:${missing.join(",")}`, resp };
}
return { ok: true, reason: "", resp };
}
async function pollOwnership(check, deadlineMs) {
let last = { ok: false, reason: "not-checked" };
const end = Date.now() + deadlineMs;
do {
last = await check();
if (last.ok) return last;
await new Promise((r) => setTimeout(r, 250));
} while (Date.now() < end);
return last;
}
function describePortOwner(port) {
try {
const out = execFileSync7("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fcp"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
const pid = out.match(/^p(\d+)$/m)?.[1];
const cmd = out.match(/^c(.+)$/m)?.[1];
if (!pid && !cmd) return null;
return `${cmd ?? "?"} (pid ${pid ?? "?"})`;
} catch {
return null;
}
}
async function findSquattedPorts(ports, accepts) {
const squatted = [];
for (const port of ports) {
if (await accepts(port)) squatted.push({ port, owner: describePortOwner(port) });
}
return squatted;
}
function buildSquatterError(tenant, squatted, detail) {
const lines = squatted.map(
(s) => ` 127.0.0.1:${s.port} held by ${s.owner ?? "an unidentified process"}`
);
const first = squatted[0]?.port ?? "<port>";
return [
`Tunnel port(s) for tenant '${tenant}' are bound by a process mesh does not own${detail ? ` (${detail})` : ""}:`,
...lines,
` Refusing to reuse a foreign listener \u2014 traffic would silently flow to the wrong upstream.`,
` To fix:`,
` mesh vpn -t ${tenant} tunnel down --stop # tear down mesh-owned runners + daemon`,
` lsof -nP -iTCP:${first} -sTCP:LISTEN # identify what still holds the port`,
` then stop that process and re-run.`,
// Port blocks are a 63-bucket hash, so a DIFFERENT tenant's
// runner can collide onto this block — in which case the `down` above is a
// no-op and only lsof identifies the real owner.
` (If lsof names another mesh runner, it belongs to a different tenant \u2014 tear that one down instead.)`
].join("\n");
}
var init_tunnel_ownership = __esm({
"libs/mesh-cli/src/utils/tunnel-ownership.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/tailscale.ts
import * as fs8 from "node:fs";
import * as net4 from "node:net";
import * as os2 from "node:os";
import * as path8 from "node:path";
import { spawn as spawn2, execFileSync as execFileSync8 } from "node:child_process";
function tenantStateDir(tenant) {
return path8.join(CONFIG_DIR, "tailscale", tenant);
}
function socketPath(tenant) {
return path8.join(tenantStateDir(tenant), "tailscaled.sock");
}
function logPath(tenant) {
return path8.join(tenantStateDir(tenant), "tailscaled.log");
}
function statePath(tenant) {
return path8.join(tenantStateDir(tenant), "tunnel-state.json");
}
function daemonMetaPath(tenant) {
return path8.join(tenantStateDir(tenant), "daemon.json");
}
function runnerManifestPath(tenant) {
return path8.join(tenantStateDir(tenant), "runner.json");
}
function readTunnelState(tenant) {
const p = statePath(tenant);
if (!fs8.existsSync(p)) return null;
try {
return JSON.parse(fs8.readFileSync(p, "utf8"));
} catch {
return null;
}
}
function writeTunnelState(tenant, state) {
fs8.mkdirSync(tenantStateDir(tenant), { recursive: true });
atomicWriteFileSync(statePath(tenant), JSON.stringify(state, null, 2), 384);
}
function clearTunnelState(tenant) {
try {
fs8.unlinkSync(statePath(tenant));
} catch {
}
}
function readDaemonMeta(tenant) {
const p = daemonMetaPath(tenant);
if (!fs8.existsSync(p)) return null;
try {
return JSON.parse(fs8.readFileSync(p, "utf8"));
} catch {
return null;
}
}
function writeDaemonMeta(tenant, meta) {
fs8.mkdirSync(tenantStateDir(tenant), { recursive: true });
atomicWriteFileSync(daemonMetaPath(tenant), JSON.stringify(meta, null, 2), 384);
}
function clearDaemonMeta(tenant) {
try {
fs8.unlinkSync(daemonMetaPath(tenant));
} catch {
}
}
function parseDaemonState(statusJson) {
try {
const j = JSON.parse(statusJson);
return { backendState: j.BackendState ?? "Unknown", selfName: j.Self?.HostName };
} catch {
return { backendState: "Unknown" };
}
}
function tailscaledBinPath() {
return resolveBrewBin("tailscaled");
}
function tailscaleBinPath() {
return resolveBrewBin("tailscale");
}
function resolveBrewBin(name) {
const brew = `/opt/homebrew/bin/${name}`;
if (fs8.existsSync(brew)) return brew;
try {
const p = execFileSync8("which", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
if (p) return p;
} catch {
}
throw new Error(`${name} not found \u2014 install the standalone binary: brew install tailscale`);
}
function daemonState(tenant) {
if (!fs8.existsSync(socketPath(tenant))) return { backendState: "Down" };
try {
const out = execFileSync8(
tailscaleBinPath(),
["--socket", socketPath(tenant), "status", "--json"],
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
);
return parseDaemonState(out);
} catch {
return { backendState: "Down" };
}
}
function parseSocksPortFromArgv(argv) {
const m = argv.match(/--socks5-server[=\s]+(?:127\.0\.0\.1|localhost):(\d+)/);
if (!m) return null;
const port = Number(m[1]);
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : null;
}
function findRunningDaemon(tenant) {
const sock = socketPath(tenant);
let out;
try {
out = execFileSync8("ps", ["ax", "-o", "pid=,command="], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
} catch {
return null;
}
for (const line of out.split("\n")) {
if (!line.includes("tailscaled") || !line.includes(`--socket=${sock}`)) continue;
const m = line.trim().match(/^(\d+)\s+(.*)$/);
if (!m) continue;
return { pid: Number(m[1]), socksPort: parseSocksPortFromArgv(m[2]) };
}
return null;
}
function portAccepts(port, timeoutMs = 400) {
return new Promise((resolve19) => {
const s = new net4.Socket();
let done = false;
const fin = (v) => {
if (done) return;
done = true;
s.destroy();
resolve19(v);
};
s.setTimeout(timeoutMs);
s.once("connect", () => fin(true));
s.once("timeout", () => fin(false));
s.once("error", () => fin(false));
s.connect(port, "127.0.0.1");
});
}
async function ensureDaemon(tenant, opts) {
if (daemonState(tenant).backendState !== "Down") {
if (await portAccepts(opts.socksPort)) {
writeDaemonMeta(tenant, {
socksPort: opts.socksPort,
pid: readDaemonMeta(tenant)?.pid ?? findRunningDaemon(tenant)?.pid ?? 0
});
return opts.socksPort;
}
const running = findRunningDaemon(tenant);
if (running?.socksPort && await portAccepts(running.socksPort)) {
writeDaemonMeta(tenant, { socksPort: running.socksPort, pid: running.pid });
return running.socksPort;
}
stopDaemon(tenant);
for (let i = 0; i < 12 && daemonState(tenant).backendState !== "Down"; i++) {
await sleep(250);
}
}
const dir = tenantStateDir(tenant);
fs8.mkdirSync(dir, { recursive: true });
const log = fs8.openSync(logPath(tenant), "a");
try {
const child = spawn2(
tailscaledBinPath(),
[
"--tun=userspace-networking",
`--socket=${socketPath(tenant)}`,
`--statedir=${dir}`,
`--socks5-server=127.0.0.1:${opts.socksPort}`,
"--port=0"
],
{
detached: true,
stdio: ["ignore", log, log],
env: { ...process.env, AWS_REGION: opts.region }
}
);
child.unref();
if (child.pid) writeDaemonMeta(tenant, { socksPort: opts.socksPort, pid: child.pid });
} finally {
fs8.closeSync(log);
}
for (let i = 0; i < 40; i++) {
await sleep(250);
if (daemonState(tenant).backendState !== "Down") return opts.socksPort;
}
throw new Error(`tailscaled did not start for tenant '${tenant}' \u2014 see ${logPath(tenant)}`);
}
function stopDaemon(tenant) {
const meta = readDaemonMeta(tenant) ?? (() => {
const running = findRunningDaemon(tenant);
return running ? { socksPort: running.socksPort ?? 0, pid: running.pid } : null;
})();
if (meta?.pid) {
try {
assertSignalablePid(meta.pid, {
what: `the VPN daemon pid recorded for tenant '${tenant}'`,
file: daemonMetaPath(tenant)
});
process.kill(meta.pid, "SIGTERM");
} catch (err) {
if (err instanceof MeshCliError) logWarn(renderErrorBody(err));
}
}
for (const p of [socketPath(tenant)]) {
try {
fs8.unlinkSync(p);
} catch {
}
}
clearDaemonMeta(tenant);
}
function buildTailscaleUpArgs(opts) {
const args = [
"--socket",
opts.socket,
"up",
"--login-server",
opts.loginServer,
"--accept-routes",
"--hostname",
opts.hostname
];
if (opts.preAuthKey) args.push("--authkey", opts.preAuthKey);
return args;
}
async function joinHeadscale(tenant, loginServer, opts = {}) {
if (daemonState(tenant).backendState === "Running") return;
const hostname2 = `mesh-${tenant}-${os2.hostname().split(".")[0]}`;
const child = spawn2(
tailscaleBinPath(),
buildTailscaleUpArgs({ socket: socketPath(tenant), loginServer, hostname: hostname2, preAuthKey: opts.preAuthKey }),
{ stdio: ["ignore", "pipe", "pipe"] }
);
if (!opts.preAuthKey) {
let buf = "";
let urlShown = false;
const capture = (d) => {
buf += d.toString();
if (urlShown) return;
const m = buf.match(/https?:\/\/\S+\/register\/\S+/);
if (m) {
urlShown = true;
logInfo(
`
This machine isn't registered with the platform VPN yet.
Open this URL in a browser to authorize it (one-time, sign in with Zitadel):
${m[0]}
Waiting for approval (up to 5 min)\u2026`
);
}
};
child.stdout.on("data", capture);
child.stderr.on("data", capture);
}
for (let i = 0; i < 600; i++) {
await sleep(500);
if (daemonState(tenant).backendState === "Running") {
child.kill();
return;
}
}
child.kill();
throw new Error(
`VPN join not completed for tenant '${tenant}' within 5 min. Re-run \`mesh vpn tunnel up\` to resume \u2014 the daemon is still up.`
);
}
function logout(tenant) {
try {
execFileSync8(tailscaleBinPath(), ["--socket", socketPath(tenant), "logout"], { stdio: "ignore" });
} catch {
}
}
function tunnelTargetsEqual(a, b) {
const key = (ts) => ts.map((t) => `${t.name}:${t.localPort}:${t.targetIp}:${t.remotePort}`).sort().join(",");
return key(a) === key(b);
}
function pidAlive(pid) {
if (!pid) return false;
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function killSupervisor(pid, source) {
assertSignalablePid(pid, source ?? { what: "tunnel supervisor pid" });
try {
process.kill(-pid, "SIGTERM");
} catch {
try {
process.kill(pid, "SIGTERM");
} catch {
}
}
}
async function verifyTunnelOwnership(tenant, expected) {
return verifyRunnerOwnership({
manifest: readRunnerManifest(runnerManifestPath(tenant)),
expectedSocksPort: expected.socksPort,
expectedPorts: expected.ports
});
}
function awaitOwnership(tenant, expected, deadlineMs) {
return pollOwnership(() => verifyTunnelOwnership(tenant, expected), deadlineMs);
}
function stopTunnelRunners(tenant, prior) {
if (prior?.managed === "launchd") uninstallLaunchAgent(tenant);
const killOrWarn = (pid, source) => {
try {
killSupervisor(pid, source);
} catch (err) {
if (!(err instanceof MeshCliError)) throw err;
logWarn(renderErrorBody(err));
}
};
if (prior?.supervisorPid)
killOrWarn(prior.supervisorPid, {
what: `the tunnel supervisor pid recorded for tenant '${tenant}'`,
file: statePath(tenant)
});
const manifest = readRunnerManifest(runnerManifestPath(tenant));
if (manifest)
killOrWarn(manifest.pgid || manifest.pid, {
what: `the tunnel runner process group recorded for tenant '${tenant}'`,
file: runnerManifestPath(tenant)
});
for (const pid of discoverRunnerPids(tenant, process.pid))
killOrWarn(pid, { what: `a tunnel runner pid discovered from 'ps' for tenant '${tenant}'` });
clearRunnerManifest(runnerManifestPath(tenant));
}
async function assertTunnelPortsFree(tenant, ports) {
let held = ports;
for (let i = 0; i < 8; i++) {
const stillHeld = [];
for (const p of held) {
if (await portAccepts(p, 250)) stillHeld.push(p);
}
held = stillHeld;
if (held.length === 0) return;
await sleep(250);
}
const squatted = await findSquattedPorts(held, (p) => portAccepts(p, 250));
throw new Error(buildSquatterError(tenant, squatted, "still bound after teardown"));
}
async function ensureForwarders(state) {
const prior = readTunnelState(state.tenant);
const expected = { socksPort: state.socksPort, ports: state.tunnels.map((t) => t.localPort) };
const runnerAlive = prior?.managed === "launchd" ? fs8.existsSync(launchAgentPath(state.tenant)) : pidAlive(prior?.supervisorPid);
if (prior && runnerAlive && tunnelTargetsEqual(prior.tunnels, state.tunnels) && // A prior supervisor spawned for a DIFFERENT SOCKS port still binds the same
// local ports but dials a dead upstream — reusing it silently strands every
// tunnel. The port must match, and the runner must PROVE it is
// ours via the ownership handshake (liveness ≠ ownership — a port answering
// a dial can be another session's SSM tunnel or an orphaned runner, #2991).
// Poll briefly rather than sample once: `ports-not-listening` during the
// runner's own rebind window and `no-control-answer` under load are
// transient, and a single miss here would tear down a healthy runner SHARED
// by every concurrent session on the tenant. This branch is
// only reached when the runner is alive with matching targets, so the
// retry costs nothing on cold-start or genuinely-dead paths.
prior.socksPort === state.socksPort && (await awaitOwnership(state.tenant, expected, 1e3)).ok) {
state.supervisorPid = prior.supervisorPid;
state.managed = prior.managed;
writeTunnelState(state.tenant, state);
return prior.supervisorPid ?? 0;
}
stopTunnelRunners(state.tenant, prior);
await assertTunnelPortsFree(state.tenant, expected.ports);
state.supervisorPid = null;
const meshBin = resolveStableMeshBin(process.argv[1]);
if (launchdAvailable()) {
state.managed = "launchd";
writeTunnelState(state.tenant, state);
if (path8.isAbsolute(meshBin) && installLaunchAgent(state.tenant, meshBin)) {
if ((await awaitOwnership(state.tenant, expected, 8e3)).ok) return 0;
uninstallLaunchAgent(state.tenant);
await assertTunnelPortsFree(state.tenant, expected.ports);
logInfo("launchd runner did not take ownership of the tunnel ports \u2014 using a detached runner.");
} else {
logInfo("launchd install failed \u2014 using a detached self-healing runner.");
}
}
state.managed = "detached";
writeTunnelState(state.tenant, state);
const child = spawn2(meshBin, ["vpn", "tunnel", "__run", state.tenant], {
detached: true,
stdio: "ignore"
});
child.on(
"error",
(err) => logWarn(`tunnel supervisor failed to start (${err.message}) \u2014 falling back to SSM.`)
);
child.unref();
state.supervisorPid = child.pid ?? null;
writeTunnelState(state.tenant, state);
const last = await awaitOwnership(state.tenant, expected, 5e3);
if (last.ok) return child.pid ?? -1;
const squatted = await findSquattedPorts(expected.ports, (p) => portAccepts(p, 250));
if (squatted.length > 0) {
throw new Error(buildSquatterError(state.tenant, squatted, `ownership check: ${last.reason}`));
}
const bindErrors = Object.entries(last.resp?.bindErrors ?? {}).map(([port, code]) => `port ${port}: ${code}`).join(", ");
throw new Error(
`Tailscale forwarders did not take ownership for tenant '${state.tenant}' (${last.reason}${bindErrors ? `; ${bindErrors}` : ""}) \u2014 see ${logPath(state.tenant)}.`
);
}
function rewriteTunnelsForTargets(devOutput, targets) {
const tunnels = { ...devOutput.tunnels };
for (const t of targets) {
tunnels[t.name] = { host: "localhost", port: t.localPort };
}
return { ...devOutput, tunnels };
}
function tailscaleAvailable() {
try {
tailscaledBinPath();
return true;
} catch {
return false;
}
}
async function ensureTailscaleTunnels(tenant, env, opts) {
const socksPort = await ensureDaemon(tenant, { region: opts.region, socksPort: opts.socksPort });
await joinHeadscale(tenant, opts.loginServer, { preAuthKey: opts.preAuthKey });
const bastion = await getPlatformBastionInfo(tenant, env, opts.region);
if (!bastion) {
throw new Error(`Could not read the platform bastion for ${tenant}/${env} (check AWS credentials).`);
}
const portOffset = tenantPortIndex(tenant) * PORT_BLOCK;
const targets = resolveTunnelTargets(bastion.services, resolveElbIp, portOffset);
if (targets.length === 0) {
throw new Error(`No resolvable VPC tunnels found for ${tenant}/${env}.`);
}
const state = {
tenant,
env,
socksPort,
loginServer: opts.loginServer,
supervisorPid: null,
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
tunnels: targets
};
await ensureForwarders(state);
return targets;
}
async function startTailscaleTunnels(devOutput, ctx) {
const targets = await ensureTailscaleTunnels(ctx.tenant, ctx.env, {
region: ctx.region,
loginServer: ctx.loginServer,
socksPort: ctx.socksPort,
preAuthKey: ctx.preAuthKey
});
return rewriteTunnelsForTargets(devOutput, targets);
}
function launchAgentLabel(tenant) {
return `tech.mesh.tunnel.${tenant}`;
}
function xmlEscape(s) {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
function renderLaunchAgentPlist(args) {
const pathEnv = args.pathEnv ?? "/usr/bin:/bin:/usr/sbin:/sbin";
return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${launchAgentLabel(args.tenant)}</string>
<key>ProgramArguments</key>
<array>
<string>${xmlEscape(args.nodeBin)}</string>
<string>${xmlEscape(args.meshBin)}</string>
<string>vpn</string>
<string>tunnel</string>
<string>__run</string>
<string>${xmlEscape(args.tenant)}</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>${xmlEscape(pathEnv)}</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>StandardOutPath</key>
<string>${xmlEscape(args.logPath)}</string>
<key>StandardErrorPath</key>
<string>${xmlEscape(args.logPath)}</string>
</dict>
</plist>
`;
}
function forwardersNeedingRebind(targets, listening) {
return targets.filter((t) => !listening.has(t.localPort));
}
function launchAgentPath(tenant) {
return path8.join(os2.homedir(), "Library", "LaunchAgents", `${launchAgentLabel(tenant)}.plist`);
}
function guiDomain() {
return `gui/${process.getuid?.() ?? 0}`;
}
function launchdAvailable() {
if (_launchdAvailable !== void 0) return _launchdAvailable;
try {
execFileSync8("which", ["launchctl"], { stdio: "ignore" });
execFileSync8("launchctl", ["print", guiDomain()], { stdio: "ignore" });
_launchdAvailable = true;
} catch {
_launchdAvailable = false;
}
return _launchdAvailable;
}
function installLaunchAgent(tenant, meshBin) {
const plistPath = launchAgentPath(tenant);
try {
let realMeshBin = meshBin;
try {
realMeshBin = fs8.realpathSync(meshBin);
} catch {
}
let pathEnv = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
try {
pathEnv = `${path8.dirname(tailscaledBinPath())}:${pathEnv}`;
} catch {
}
fs8.mkdirSync(path8.dirname(plistPath), { recursive: true });
fs8.writeFileSync(
plistPath,
renderLaunchAgentPlist({
tenant,
meshBin: realMeshBin,
nodeBin: process.execPath,
logPath: path8.join(tenantStateDir(tenant), "runner.log"),
pathEnv
})
);
try {
execFileSync8("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
} catch {
}
execFileSync8("launchctl", ["bootstrap", guiDomain(), plistPath], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function uninstallLaunchAgent(tenant) {
const plistPath = launchAgentPath(tenant);
try {
execFileSync8("launchctl", ["bootout", guiDomain(), plistPath], { stdio: "ignore" });
} catch {
}
try {
fs8.unlinkSync(plistPath);
} catch {
}
}
async function runTunnels(tenant) {
const st = readTunnelState(tenant);
if (!st) {
uninstallLaunchAgent(tenant);
clearRunnerManifest(runnerManifestPath(tenant));
process.exit(0);
}
const token = mintRunnerToken();
const servers = /* @__PURE__ */ new Map();
const bindErrors = /* @__PURE__ */ new Map();
const spawnFor = (t) => {
servers.get(t.localPort)?.close();
bindErrors.delete(t.localPort);
const server = createForwarder({
listenPort: t.localPort,
socksPort: st.socksPort,
targetIp: t.targetIp,
targetPort: t.remotePort,
onError: (err) => {
bindErrors.set(t.localPort, err.code ?? err.message);
logWarn(`forwarder ${t.name} failed on 127.0.0.1:${t.localPort}: ${err.code ?? err.message}`);
}
});
server.on("listening", () => bindErrors.delete(t.localPort));
servers.set(t.localPort, server);
};
for (const t of st.tunnels) spawnFor(t);
const control = await startControlServer(
() => ({
token,
tenant,
pid: process.pid,
socksPort: st.socksPort,
listening: st.tunnels.filter((t) => servers.get(t.localPort)?.listening).map((t) => t.localPort),
bindErrors: Object.fromEntries([...bindErrors].map(([p, c]) => [String(p), c]))
})
);
writeRunnerManifest(runnerManifestPath(tenant), {
tenant,
pid: process.pid,
pgid: getProcessGroupId(process.pid) ?? process.pid,
token,
controlPort: control.port,
socksPort: st.socksPort,
ports: st.tunnels.map((t) => t.localPort),
startedAt: (/* @__PURE__ */ new Date()).toISOString()
});
const shutdown = () => {
control.server.close();
for (const s of servers.values()) s.close();
clearRunnerManifest(runnerManifestPath(tenant), token);
process.exit(0);
};
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
for (; ; ) {
await sleep(5e3);
try {
if (daemonState(tenant).backendState === "Down") {
await ensureDaemon(tenant, {
region: process.env.AWS_REGION ?? "us-east-2",
socksPort: st.socksPort
});
}
const listening = /* @__PURE__ */ new Set();
for (const t of st.tunnels) {
if (servers.get(t.localPort)?.listening) listening.add(t.localPort);
}
for (const t of forwardersNeedingRebind(st.tunnels, listening)) spawnFor(t);
} catch {
}
}
}
var sleep, _launchdAvailable;
var init_tailscale = __esm({
"libs/mesh-cli/src/utils/tailscale.ts"() {
"use strict";
init_login();
init_aws_auth();
init_log();
init_errors();
init_pid();
init_bastion();
init_tailscale_targets();
init_socks_forward();
init_tunnel_ownership();
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
}
});
// libs/mesh-cli/src/commands/vpn/tunnel.ts
import * as net5 from "node:net";
function deriveLoginServer(context) {
const cfg = getContextConfig(context);
if (!cfg?.issuer) {
throw new Error(`No login config for context '${context}'. Run: mesh login ${context}`);
}
const u = new URL(cfg.issuer);
const parts = u.hostname.split(".");
parts[0] = "vpn";
return `https://${parts.join(".")}`;
}
function findFreePort() {
return new Promise((resolve19, reject) => {
const s = net5.createServer();
s.on("error", reject);
s.listen(0, "127.0.0.1", () => {
const port = s.address().port;
s.close(() => resolve19(port));
});
});
}
function canConnect(port) {
return new Promise((resolve19) => {
const s = new net5.Socket();
let settled = false;
const done = (ok) => {
if (settled) return;
settled = true;
s.destroy();
resolve19(ok);
};
s.setTimeout(500);
s.once("connect", () => done(true));
s.once("timeout", () => done(false));
s.once("error", () => done(false));
s.connect(port, "127.0.0.1");
});
}
async function tunnelUp(vpn, cmdOpts) {
const { tenant, env } = vpn;
const context = cmdOpts.context ?? `${tenant}.${env}`;
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
const loginServer = deriveLoginServer(context);
const prior = readTunnelState(tenant);
const daemonUp = daemonState(tenant).backendState !== "Down";
const socksPort = daemonUp ? readDaemonMeta(tenant)?.socksPort ?? prior?.socksPort ?? await findFreePort() : await findFreePort();
logInfo(`Bringing up userspace VPN tunnels for tenant '${tenant}'...`);
const targets = await ensureTailscaleTunnels(tenant, env, { region, loginServer, socksPort });
logSuccess(`VPN tunnels up for '${tenant}' (${targets.length}):`);
for (const t of targets) {
logInfo(` ${t.name.padEnd(14)} 127.0.0.1:${t.localPort} \u2192 ${t.targetHost}:${t.remotePort}`);
}
}
function tunnelDown(vpn, cmdOpts) {
const { tenant } = vpn;
const st = readTunnelState(tenant);
if (!st && !cmdOpts.stop && !cmdOpts.logout) {
logInfo(`No tunnels running for tenant '${tenant}'.`);
return;
}
stopTunnelRunners(tenant, st);
if (cmdOpts.logout) {
logout(tenant);
logInfo("Logged the VPN daemon out of Headscale.");
}
if (cmdOpts.stop || cmdOpts.logout) {
stopDaemon(tenant);
logInfo("Stopped the userspace VPN daemon.");
}
clearTunnelState(tenant);
logSuccess(`VPN tunnels down for '${tenant}'.`);
}
async function tunnelStatus(vpn, cmdOpts) {
const { tenant } = vpn;
const st = readTunnelState(tenant);
const ds = daemonState(tenant);
const owned = st ? await verifyTunnelOwnership(tenant, {
socksPort: st.socksPort,
ports: st.tunnels.map((t) => t.localPort)
}) : null;
if (cmdOpts.json) {
console.log(
JSON.stringify(
{
tenant,
daemon: ds,
up: !!st,
managed: st?.managed ?? null,
owned: owned?.ok ?? false,
ownership: owned ? owned.ok ? "owned" : owned.reason : "no-state",
tunnels: st?.tunnels ?? []
},
null,
2
)
);
return;
}
logInfo(`Daemon: ${ds.backendState}${ds.selfName ? ` (${ds.selfName})` : ""}`);
if (!st) {
logInfo(`No tunnels running for tenant '${tenant}'.`);
return;
}
logInfo(`Managed: ${st.managed ?? "detached"}`);
logInfo(
owned?.ok ? `Runner: owned (verified)` : `Runner: NOT OWNED (${owned?.reason}) \u2014 ports below may be foreign listeners`
);
const listening = owned?.ok ? new Set(owned.resp?.listening ?? []) : null;
for (const t of st.tunnels) {
const up = listening ? listening.has(t.localPort) : await canConnect(t.localPort);
logInfo(` ${up ? "\u2713" : "\u2717"} ${t.name.padEnd(14)} 127.0.0.1:${t.localPort} \u2192 ${t.targetHost}:${t.remotePort}`);
}
}
async function guard(fn) {
try {
await fn();
} catch (e) {
logError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
}
function registerTunnelSubcommands(vpn) {
const tunnel = vpn.command("tunnel").description("Userspace-Tailscale tunnels to VPC services (localhost:PORT set)");
tunnel.command("up", { isDefault: true }).description("Start the tenant's VPC tunnels over userspace Tailscale").option("--context <ctx>", "Login context for the VPN (default: <tenant>.<env>)").action((cmdOpts) => guard(() => tunnelUp(vpn.opts(), cmdOpts)));
tunnel.command("down").description("Stop the tenant's VPC tunnels").option("--stop", "Also stop the userspace tailscaled daemon (keeps VPN login)").option("--logout", "Log the daemon out of Headscale and stop it").action(
(cmdOpts) => guard(() => tunnelDown(vpn.opts(), cmdOpts))
);
tunnel.command("status").description("Show daemon + tunnel status").option("--json", "Machine-readable output").action((cmdOpts) => guard(() => tunnelStatus(vpn.opts(), cmdOpts)));
tunnel.command("__run <tenant>", { hidden: true }).action((tenant) => runTunnels(tenant));
tunnel.command("__supervise <tenant>", { hidden: true }).action((tenant) => runTunnels(tenant));
}
var init_tunnel = __esm({
"libs/mesh-cli/src/commands/vpn/tunnel.ts"() {
"use strict";
init_log();
init_login();
init_tailscale();
}
});
// libs/mesh-cli/src/commands/vpn/index.ts
import { execFileSync as execFileSync9 } from "child_process";
import * as net6 from "node:net";
function defaultNamespace(tenant, env) {
return `${tenant}-${env}-headscale`;
}
function resolveNamespace(options) {
return options.namespace ?? defaultNamespace(options.tenant, options.env);
}
function headscaleExec(namespace, args, opts) {
const cmd = ["headscale", ...args];
if (opts?.json) {
cmd.push("--output", "json");
}
const execOpts = {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
};
try {
const result = execFileSync9(
"kubectl",
[
"exec",
"-n",
namespace,
HEADSCALE_POD,
"-c",
HEADSCALE_CONTAINER,
"--",
...cmd
],
execOpts
);
return result.trim();
} catch (error) {
const execError = error;
const stderr = execError.stderr?.toString().trim() ?? "";
if (stderr) {
throw new Error(stderr);
}
throw error;
}
}
function clusterName(tenant, env) {
return `${tenant}-${env}-eks`;
}
function assertPodReady(namespace, options) {
try {
const output = execFileSync9(
"kubectl",
[
"get",
"pod",
HEADSCALE_POD,
"-n",
namespace,
"-o",
"jsonpath={.status.phase}"
],
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
);
if (output.trim() !== "Running") {
logError(`Headscale pod is not running (status: ${output.trim()})`);
process.exit(1);
}
} catch (err) {
const stderr = err.stderr?.toString() ?? "";
logError(`Cannot reach headscale pod in namespace ${namespace}`);
if (stderr.includes("expired") || stderr.includes("token")) {
logInfo("Your AWS credentials appear to be expired. Refresh them and retry.");
} else {
const cluster = clusterName(options.tenant, options.env);
logInfo("Make sure your kubeconfig is configured for the target cluster:");
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
logInfo(` aws eks update-kubeconfig --name ${cluster} --region ${region}`);
}
process.exit(1);
}
}
async function vpnStatus(options) {
const ns = resolveNamespace(options);
logInfo(`Checking Headscale in namespace ${ns}...`);
assertPodReady(ns, options);
const podJson = execFileSync9(
"kubectl",
[
"get",
"pod",
HEADSCALE_POD,
"-n",
ns,
"-o",
"json"
],
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
);
const pod = JSON.parse(podJson);
const container = pod.status?.containerStatuses?.find(
(c) => c.name === HEADSCALE_CONTAINER
);
const image = container?.image ?? "unknown";
const ready = container?.ready ?? false;
const restarts = container?.restartCount ?? 0;
let endpoint = "unknown";
try {
endpoint = execFileSync9(
"kubectl",
[
"get",
"httproute",
"-n",
ns,
"-o",
"jsonpath={.items[0].spec.hostnames[0]}"
],
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
).trim();
if (endpoint) {
endpoint = `https://${endpoint}`;
}
} catch {
}
let userCount = 0;
try {
const usersJson = headscaleExec(ns, ["users", "list"], { json: true });
const users = JSON.parse(usersJson);
userCount = Array.isArray(users) ? users.length : 0;
} catch {
}
let nodeCount = 0;
try {
const nodesJson = headscaleExec(ns, ["nodes", "list"], { json: true });
const nodes = JSON.parse(nodesJson);
nodeCount = Array.isArray(nodes) ? nodes.length : 0;
} catch {
}
console.log("");
console.log(` Endpoint: ${endpoint}`);
console.log(` Image: ${image}`);
console.log(` Ready: ${ready ? "yes" : "no"}`);
console.log(` Restarts: ${restarts}`);
console.log(` Namespace: ${ns}`);
console.log(` Users: ${userCount}`);
console.log(` Nodes: ${nodeCount}`);
console.log("");
if (ready) {
logSuccess("Headscale is healthy");
} else {
logWarn("Headscale pod is not ready");
}
}
async function createApiKey(options) {
const ns = resolveNamespace(options);
logInfo(`Creating Headscale API key in namespace ${ns}...`);
assertPodReady(ns, options);
try {
const output = headscaleExec(ns, ["apikeys", "create"]);
console.log("");
logSuccess("API key created:");
console.log("");
console.log(` ${output}`);
console.log("");
logWarn("Store this key securely \u2014 it cannot be retrieved again.");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(`Failed to create API key: ${message}`);
process.exit(1);
}
}
async function createPreAuthKey(options) {
const ns = resolveNamespace(options);
const { user, expiration, reusable, ephemeral } = options;
logInfo(`Creating pre-auth key for user "${user}" in namespace ${ns}...`);
assertPodReady(ns, options);
try {
headscaleExec(ns, ["users", "create", user]);
logInfo(`Created user "${user}"`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (!message.includes("already exists")) {
logError(`Failed to create user: ${message}`);
process.exit(1);
}
logInfo(`User "${user}" already exists`);
}
const args = ["preauthkeys", "create", "--user", user];
if (expiration) {
args.push("--expiration", expiration);
}
if (reusable) {
args.push("--reusable");
}
if (ephemeral) {
args.push("--ephemeral");
}
try {
const output = headscaleExec(ns, args);
console.log("");
logSuccess("Pre-authentication key created:");
console.log("");
console.log(` ${output}`);
console.log("");
logInfo("Use this key to register a node:");
logInfo(` tailscale up --login-server <endpoint> --authkey ${output}`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(`Failed to create pre-auth key: ${message}`);
process.exit(1);
}
}
async function listUsers(options) {
const ns = resolveNamespace(options);
logInfo(`Listing Headscale users in namespace ${ns}...`);
assertPodReady(ns, options);
try {
const output = headscaleExec(ns, ["users", "list"]);
console.log("");
console.log(output);
console.log("");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(`Failed to list users: ${message}`);
process.exit(1);
}
}
function vpnEndpointFromContext(context) {
const config = getContextConfig(context);
if (!config) {
throw new Error(
`No configuration found for "${context}".
Run: mesh login ${context} (or add the context to ~/.config/mesh/config.json)`
);
}
const issuerUrl = new URL(config.issuer);
const hostParts = issuerUrl.hostname.split(".");
if (hostParts.length < 3) {
throw new Error(
`Cannot derive VPN endpoint from issuer "${config.issuer}". Expected a subdomain like identity.<env>.<domain>.`
);
}
hostParts[0] = "vpn";
return `https://${hostParts.join(".")}`;
}
function parseContextTenantEnv(context) {
const [tenant, env] = context.split(".");
return { tenant: tenant || "mesh", env: env || "dev" };
}
function findFreePort2() {
return new Promise((resolve19, reject) => {
const srv = net6.createServer();
srv.on("error", reject);
srv.listen(0, "127.0.0.1", () => {
const addr = srv.address();
const port = typeof addr === "object" && addr ? addr.port : 0;
srv.close(() => resolve19(port));
});
});
}
function findTailscale() {
try {
return execFileSync9("which", ["tailscale"], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
}).trim() || null;
} catch {
return null;
}
}
function tailscaleStatus() {
try {
const json = execFileSync9("tailscale", ["status", "--json"], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
});
return JSON.parse(json);
} catch {
return null;
}
}
function tailscaleBackendState() {
return tailscaleStatus()?.BackendState ?? null;
}
function normalizeControlUrl(url) {
try {
const u = new URL(url);
return `${u.protocol}//${u.host}`.toLowerCase();
} catch {
return url.replace(/\/+$/, "").toLowerCase();
}
}
async function vpnConnect(context, opts = {}) {
const endpoint = vpnEndpointFromContext(context);
if (opts.system) {
await vpnConnectSystem(endpoint);
return;
}
await vpnConnectUserspace(context, endpoint);
}
async function vpnConnectUserspace(context, loginServer) {
if (!tailscaleAvailable()) {
logError("Standalone tailscale/tailscaled not found.");
logInfo("Install it: brew install tailscale");
logInfo("(The GUI Tailscale.app is not used here \u2014 for whole-machine VPN see `mesh vpn connect --system`.)");
process.exit(1);
}
const { tenant } = parseContextTenantEnv(context);
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
if (daemonState(tenant).backendState === "Running") {
const port = readDaemonMeta(tenant)?.socksPort ?? findRunningDaemon(tenant)?.socksPort;
logSuccess(`Already connected to the platform VPN for '${tenant}' (userspace).`);
if (port) logInfo(` SOCKS5 proxy: 127.0.0.1:${port} (route a tool via ALL_PROXY=socks5://127.0.0.1:${port})`);
return;
}
logInfo(`Connecting to the platform VPN for '${tenant}' (userspace tailscaled)\u2026`);
const socksPort = readDaemonMeta(tenant)?.socksPort ?? await findFreePort2();
const realPort = await ensureDaemon(tenant, { region, socksPort });
await joinHeadscale(tenant, loginServer);
logSuccess(`VPN connected (userspace) for '${tenant}'.`);
logInfo(` SOCKS5 proxy: 127.0.0.1:${realPort} \u2014 route a tool via ALL_PROXY=socks5://127.0.0.1:${realPort}`);
logInfo(" For auto-forwarded VPC services (Temporal, RDS, \u2026): mesh vpn tunnel up (or mesh dev)");
}
async function vpnConnectSystem(endpoint) {
if (!findTailscale()) {
logError("Tailscale CLI not found.");
logInfo("Install it: brew install tailscale (or see https://tailscale.com/download)");
process.exit(1);
}
const status = tailscaleStatus();
if (status?.BackendState === "Running") {
const current = status.ControlURL ? normalizeControlUrl(status.ControlURL) : null;
const target = normalizeControlUrl(endpoint);
if (current === target) {
logSuccess(`Already connected to VPN at ${endpoint}.`);
logInfo("Run: tailscale status to see connected nodes.");
return;
}
if (current) {
logWarn(`Currently connected to ${current}. Reconnecting to ${target}...`);
}
}
logInfo(`Connecting to VPN at ${endpoint}...`);
logInfo("A browser window will open for Zitadel authentication.");
console.log("");
const upArgs = [
"up",
"--reset",
"--login-server",
endpoint,
"--accept-routes"
];
try {
execFileSync9("tailscale", upArgs, { stdio: "inherit" });
console.log("");
logSuccess("VPN connected.");
} catch (error) {
const code = error.status;
const isPermissionError = process.platform === "linux" && (code === 1 || code === 2);
console.log("");
logError(`tailscale up failed (exit ${code}).`);
if (isPermissionError) {
logInfo("On Linux, tailscaled runs as root \u2014 you may need sudo:");
logInfo(` sudo tailscale ${upArgs.join(" ")}`);
}
process.exit(1);
}
}
async function vpnDisconnect(opts) {
if (opts.system) {
if (!findTailscale()) {
logError("Tailscale CLI not found.");
process.exit(1);
}
if (tailscaleBackendState() !== "Running") {
logInfo("System VPN is not connected.");
return;
}
logInfo("Disconnecting system VPN\u2026");
try {
execFileSync9("tailscale", ["down"], { stdio: "inherit" });
logSuccess("VPN disconnected.");
} catch (error) {
const code = error.status;
logError(`tailscale down failed (exit ${code}). Try: sudo tailscale down`);
process.exit(1);
}
return;
}
const { tenant } = opts;
if (daemonState(tenant).backendState === "Down") {
logInfo(`No userspace VPN daemon running for '${tenant}'.`);
return;
}
logInfo(`Disconnecting userspace VPN for '${tenant}'\u2026`);
stopDaemon(tenant);
logSuccess(`VPN disconnected for '${tenant}'.`);
}
function registerVpnCommands(program2) {
const vpn = program2.command("vpn").description("Headscale VPN management").option("-t, --tenant <tenant>", "Platform tenant", "mesh").option("-e, --env <env>", "Platform environment", "dev").option("-n, --namespace <namespace>", "Override K8s namespace (default: {tenant}-{env}-headscale)");
vpn.command("status").description("Show VPN control plane status").action(async () => {
const opts = vpn.opts();
await vpnStatus(opts);
});
vpn.command("connect").description("Connect to the platform VPN (userspace tailscaled; opens browser for Zitadel auth)").argument("<context>", 'Platform context (e.g., "mesh.dev")').option(
"--system",
"Use the whole-machine GUI Tailscale.app instead (system TUN; cannot run headless/sandboxed)"
).action(async (context, cmdOpts) => {
await vpnConnect(context, cmdOpts);
});
vpn.command("disconnect").description(
"Disconnect from the platform VPN \u2014 stops the tenant's shared userspace daemon (also ends any active `mesh vpn tunnel` / `mesh dev` forwards for it); --system for GUI Tailscale"
).option("--system", "Disconnect the whole-machine GUI Tailscale instead of the userspace daemon").action(async (cmdOpts) => {
const { tenant } = vpn.opts();
await vpnDisconnect({ tenant, system: cmdOpts.system });
});
vpn.command("api-key").description("Create a Headscale API key").action(async () => {
const opts = vpn.opts();
await createApiKey(opts);
});
vpn.command("pre-auth-key").description("Create a pre-authentication key for node registration").requiredOption("-u, --user <user>", "User/namespace to create the key for (e.g., matt@trabian.com)").option("--expiration <duration>", "Key expiration (e.g., 24h, 7d)", "24h").option("--reusable", "Allow key to be used multiple times").option("--ephemeral", "Nodes registered with this key are ephemeral").action(async (cmdOpts) => {
const parentOpts = vpn.opts();
await createPreAuthKey({ ...parentOpts, ...cmdOpts });
});
vpn.command("users").description("List registered VPN users").action(async () => {
const opts = vpn.opts();
await listUsers(opts);
});
registerTunnelSubcommands(vpn);
}
var HEADSCALE_POD, HEADSCALE_CONTAINER;
var init_vpn2 = __esm({
"libs/mesh-cli/src/commands/vpn/index.ts"() {
"use strict";
init_utils();
init_login();
init_tunnel();
init_tailscale();
HEADSCALE_POD = "headscale-0";
HEADSCALE_CONTAINER = "headscale";
}
});
// libs/mesh-cli/src/commands/login.ts
var login_exports = {};
__export(login_exports, {
CONFIG_DIR: () => CONFIG_DIR,
CONFIG_FILE: () => CONFIG_FILE,
CREDENTIALS_FILE: () => CREDENTIALS_FILE,
NO_REGISTRY_DEPRECATED: () => NO_REGISTRY_DEPRECATED,
REGISTRY_SEPARATE_HINT: () => REGISTRY_SEPARATE_HINT,
RESERVED_REGISTRY_CONTEXT: () => RESERVED_REGISTRY_CONTEXT,
accountIdFromRoleArn: () => accountIdFromRoleArn,
atomicWriteFileSync: () => atomicWriteFileSync,
clearContextConfig: () => clearContextConfig,
clearCredentials: () => clearCredentials,
decodeJwtPayload: () => decodeJwtPayload,
deviceLoginWithReissue: () => deviceLoginWithReissue,
discoverConfigForContext: () => discoverConfigForContext,
discoverConfigFromWellKnown: () => discoverConfigFromWellKnown,
ensureLogin: () => ensureLogin,
forceRefreshToken: () => forceRefreshToken,
getContextConfig: () => getContextConfig,
getValidToken: () => getValidToken,
isRemoteEnvironment: () => isRemoteEnvironment,
loginTimeoutMessage: () => loginTimeoutMessage,
probeCredentials: () => probeCredentials,
readAllContextConfigs: () => readAllContextConfigs,
readAllCredentials: () => readAllCredentials,
readCredentials: () => readCredentials,
registerLoginCommand: () => registerLoginCommand,
renderAwsFallbackStatus: () => renderAwsFallbackStatus,
renderExportProfileLines: () => renderExportProfileLines,
renderExportStaticLines: () => renderExportStaticLines,
renderNoConfigHelp: () => renderNoConfigHelp,
resolveAwsConfigTarget: () => resolveAwsConfigTarget,
resolveRoleOrExplain: () => resolveRoleOrExplain,
resolveUserAwsConfigPath: () => resolveUserAwsConfigPath,
runLoginFlow: () => runLoginFlow,
tokenStillValid: () => tokenStillValid,
writeContextConfig: () => writeContextConfig
});
import * as http from "http";
import * as crypto2 from "crypto";
import * as fs9 from "fs";
import * as path9 from "path";
import { execFileSync as execFileSync10 } from "child_process";
function readConfig() {
if (!fs9.existsSync(CONFIG_FILE)) return {};
try {
return JSON.parse(fs9.readFileSync(CONFIG_FILE, "utf-8"));
} catch {
return {};
}
}
function writeContextConfig(context, config) {
const existing = readConfig();
existing[context] = config;
fs9.mkdirSync(CONFIG_DIR, { recursive: true });
fs9.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
}
function clearContextConfig(context) {
const existing = readConfig();
if (!(context in existing)) return;
delete existing[context];
fs9.mkdirSync(CONFIG_DIR, { recursive: true });
fs9.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
}
function getContextConfig(context) {
const config = readConfig();
return config[context] ?? null;
}
function parseTenantEnv(context) {
const parts = context.split(".");
if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
return { tenant: parts[0], env: parts[1] };
}
async function discoverConfigFromSsm(context) {
const parsed = parseTenantEnv(context);
if (!parsed) return null;
const { tenant, env } = parsed;
const ssmPath = `/mesh-platform/${tenant}/${env}/platform/zitadel`;
logInfo(`Attempting SSM discovery from ${ssmPath}...`);
try {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
const ssm = new SSMClient5({ region });
const resp = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
const raw = resp.Parameter?.Value;
if (!raw) {
logWarn(`SSM parameter ${ssmPath} has no value`);
return null;
}
const data = JSON.parse(raw);
const issuer = data.endpoint;
const clientId = data.cliClientId;
if (typeof issuer !== "string" || !issuer) {
logWarn("SSM zitadel entry missing 'endpoint' field");
return null;
}
if (typeof clientId !== "string" || !clientId) {
logWarn(
"SSM zitadel entry missing 'cliClientId' field.\n The platform needs to be deployed with an updated ZitadelPlatformIdentity\n that exports cliClientId. Until then, contact your platform admin."
);
return null;
}
const config = { issuer, clientId };
if (typeof data.vpn === "string") config.vpn = data.vpn;
if (typeof data.vpnJoinBroker === "string") config.vpnJoinBroker = data.vpnJoinBroker;
if (typeof data.registryBroker === "string") config.registryBroker = data.registryBroker;
writeContextConfig(context, config);
logSuccess(`Discovered platform configuration for ${context} via SSM`);
return config;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes("ExpiredToken") || message.includes("credentials")) {
logWarn(`SSM discovery failed: AWS credentials expired or unavailable.`);
if (firstPartyDomainFor(context) || context.split(".").length - 1 >= 2) {
logInfo(` Falling back to anonymous HTTPS discovery \u2014 no AWS needed.`);
} else {
logInfo(` If you have an AWS account: aws sso login --profile <profile>`);
logInfo(` Otherwise use the platform's full domain, e.g. mesh login dev.<tenant>.meshtech.io`);
}
} else if (message.includes("ParameterNotFound")) {
logWarn(`SSM parameter not found: ${ssmPath}`);
logInfo(" This platform context may not be deployed.");
} else {
logWarn(`SSM discovery failed: ${message}`);
}
return null;
}
}
async function discoverConfig(domain, contextKey = domain, opts = {}) {
const url = `https://cli.${domain}/.well-known/mesh.json`;
if (!opts.quiet) logInfo(`Attempting discovery from ${url}...`);
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5e3);
const resp = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
if (!resp.ok) {
logWarn(`Discovery endpoint returned ${resp.status}`);
return null;
}
const data = await resp.json();
const issuer = data.issuer;
const clientId = data.clientId;
if (typeof issuer !== "string" || !issuer || typeof clientId !== "string" || !clientId) {
logWarn("Discovery endpoint returned invalid config (missing issuer or clientId)");
return null;
}
const config = { issuer, clientId };
if (typeof data.vpn === "string") config.vpn = data.vpn;
if (typeof data.vpnJoinBroker === "string") config.vpnJoinBroker = data.vpnJoinBroker;
if (typeof data.registryBroker === "string") config.registryBroker = data.registryBroker;
if (!opts.quiet) {
writeContextConfig(contextKey, config);
logSuccess(`Discovered platform configuration for ${contextKey}`);
}
return config;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes("abort")) {
logWarn("Discovery timed out");
} else {
logWarn(`Discovery failed: ${message}`);
}
return null;
}
}
function discoverConfigFromWellKnown(domain, contextKey = domain, opts = {}) {
return discoverConfig(domain, contextKey, opts);
}
function readAllCredentials() {
if (!fs9.existsSync(CREDENTIALS_FILE)) return {};
try {
return JSON.parse(fs9.readFileSync(CREDENTIALS_FILE, "utf-8"));
} catch {
return {};
}
}
function readCredentials(context) {
return readAllCredentials()[context] ?? null;
}
function atomicWriteFileSync(path46, data, mode) {
const tmpPath = `${path46}.${process.pid}.${atomicWriteCounter++}.tmp`;
try {
fs9.writeFileSync(tmpPath, data, { mode });
fs9.renameSync(tmpPath, path46);
} catch (err) {
try {
fs9.unlinkSync(tmpPath);
} catch {
}
throw err;
}
}
function writeCredentials(context, creds) {
fs9.mkdirSync(CONFIG_DIR, { recursive: true });
const all = readAllCredentials();
all[context] = creds;
atomicWriteFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), 384);
}
function clearCredentials(context) {
const all = readAllCredentials();
delete all[context];
fs9.mkdirSync(CONFIG_DIR, { recursive: true });
fs9.writeFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), {
mode: 384
});
}
function base64url(buffer) {
return buffer.toString("base64url");
}
function generateCodeVerifier() {
return base64url(crypto2.randomBytes(32));
}
function generateCodeChallenge(verifier) {
return base64url(crypto2.createHash("sha256").update(verifier).digest());
}
function decodeJwtPayload(token) {
const parts = token.split(".");
if (parts.length !== 3) throw new Error("Invalid JWT");
return JSON.parse(Buffer.from(parts[1], "base64url").toString());
}
async function exchangeCode(issuer, clientId, code, codeVerifier) {
const body = new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: REDIRECT_URI,
client_id: clientId,
code_verifier: codeVerifier
});
const resp = await fetch(`${issuer}/oauth/v2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body.toString()
});
if (!resp.ok) {
const text = await resp.text();
throw new Error(`Token exchange failed (${resp.status}): ${text}`);
}
return resp.json();
}
async function refreshTokens(issuer, clientId, refreshToken) {
const body = new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: clientId
});
const resp = await fetch(`${issuer}/oauth/v2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body.toString()
});
if (!resp.ok) {
throw new Error(`Token refresh failed (${resp.status})`);
}
return resp.json();
}
function login(context, config) {
return new Promise((resolve19, reject) => {
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);
const state = base64url(crypto2.randomBytes(16));
const authUrl = new URL(`${config.issuer}/oauth/v2/authorize`);
authUrl.searchParams.set("client_id", config.clientId);
authUrl.searchParams.set("redirect_uri", REDIRECT_URI);
authUrl.searchParams.set("response_type", "code");
authUrl.searchParams.set("scope", SCOPES);
authUrl.searchParams.set("code_challenge", codeChallenge);
authUrl.searchParams.set("code_challenge_method", "S256");
authUrl.searchParams.set("state", state);
let timeoutId;
const server = http.createServer(async (req, res) => {
try {
const url = new URL(req.url ?? "/", `http://localhost:${REDIRECT_PORT}`);
if (url.pathname !== "/callback") {
res.writeHead(404);
res.end("Not found");
return;
}
const error = url.searchParams.get("error");
if (error) {
const desc = url.searchParams.get("error_description") ?? error;
const safeDesc = desc.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
res.end(
`<html><body><h2>Login failed</h2><p>${safeDesc}</p></body></html>`
);
teardown();
reject(new Error(desc));
return;
}
const returnedState = url.searchParams.get("state");
if (returnedState !== state) {
res.writeHead(400);
res.end("State mismatch");
teardown();
reject(new Error("State mismatch"));
return;
}
const code = url.searchParams.get("code");
if (!code) {
res.writeHead(400);
res.end("No code");
teardown();
reject(new Error("No authorization code received"));
return;
}
const tokens = await exchangeCode(
config.issuer,
config.clientId,
code,
codeVerifier
);
const idPayload = decodeJwtPayload(tokens.id_token);
const email = idPayload.email ?? idPayload.preferred_username ?? "unknown";
const sub = idPayload.sub;
let tenants = [];
try {
const accessPayload = decodeJwtPayload(tokens.access_token);
tenants = accessPayload["urn:mesh:tenants"] ?? [];
} catch {
}
writeCredentials(context, {
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: new Date(
Date.now() + tokens.expires_in * 1e3
).toISOString(),
email,
sub
});
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(
`<html><body><h2>✅ Logged in to ${context}!</h2><p>You can close this tab.</p></body></html>`
);
logSuccess(`Logged in as ${email} (${context})`);
if (tenants.length > 0) {
logInfo(`Deployable tenants: ${tenants.join(", ")}`);
}
hintVpnIfDisconnected(context);
teardown();
resolve19();
} catch (err) {
teardown();
reject(err);
}
});
const teardown = () => {
if (timeoutId) clearTimeout(timeoutId);
server.closeAllConnections?.();
server.close();
};
server.listen(REDIRECT_PORT, () => {
logInfo(`Opening browser for authentication (${context})...`);
const url = authUrl.toString();
try {
if (process.platform === "darwin") {
execFileSync10("open", [url], { stdio: "ignore" });
} else if (process.platform === "linux") {
execFileSync10("xdg-open", [url], { stdio: "ignore" });
} else {
logInfo(`Open this URL in your browser:
${url}`);
}
} catch {
logInfo(`Open this URL in your browser:
${url}`);
}
});
timeoutId = setTimeout(() => {
teardown();
reject(new Error(loginTimeoutMessage(context)));
}, 12e4);
});
}
async function deviceLoginWithReissue(attempt, maxCodes = MAX_DEVICE_CODES) {
for (let n = 1; n <= maxCodes; n++) {
if (n > 1) logWarn(`That code expired \u2014 issuing a fresh one (${n}/${maxCodes})\u2026`);
if (await attempt(n) === "success") return;
}
throw new Error(
`Device login not completed after ${maxCodes} codes. Re-run the command when you're ready to authorize.`
);
}
async function deviceCodeLogin(context, config) {
await deviceLoginWithReissue(() => attemptDeviceCode(context, config));
}
async function attemptDeviceCode(context, config) {
const resp = await fetch(`${config.issuer}/oauth/v2/device_authorization`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: config.clientId,
scope: SCOPES
}).toString()
});
if (!resp.ok) {
const text = await resp.text();
throw new Error(`Device authorization request failed (${resp.status}): ${text}`);
}
const deviceAuth = await resp.json();
const openUrl = deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri;
console.log();
logInfo(`Open this URL in your browser:
`);
logInfo(` ${openUrl}
`);
logInfo(`Code: ${deviceAuth.user_code}`);
console.log();
logInfo("Waiting for authorization...");
try {
if (process.platform === "darwin") {
execFileSync10("open", [openUrl], { stdio: "ignore" });
} else if (process.platform === "linux") {
execFileSync10("xdg-open", [openUrl], { stdio: "ignore" });
}
} catch {
}
const deadline = Date.now() + deviceAuth.expires_in * 1e3;
let interval = deviceAuth.interval * 1e3;
while (Date.now() < deadline) {
await new Promise((r) => setTimeout(r, interval));
const tokenResp = await fetch(`${config.issuer}/oauth/v2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: config.clientId,
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
device_code: deviceAuth.device_code
}).toString()
});
if (tokenResp.ok) {
const tokens = await tokenResp.json();
const idPayload = decodeJwtPayload(tokens.id_token);
const email = idPayload.email ?? idPayload.preferred_username ?? "unknown";
const sub = idPayload.sub;
let tenants = [];
try {
const accessPayload = decodeJwtPayload(tokens.access_token);
tenants = accessPayload["urn:mesh:tenants"] ?? [];
} catch {
}
writeCredentials(context, {
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: new Date(Date.now() + tokens.expires_in * 1e3).toISOString(),
email,
sub
});
logSuccess(`Logged in as ${email} (${context})`);
if (tenants.length > 0) {
logInfo(`Deployable tenants: ${tenants.join(", ")}`);
}
hintVpnIfDisconnected(context);
return "success";
}
const error = await tokenResp.json();
switch (error.error) {
case "authorization_pending":
continue;
case "slow_down":
interval += 1e3;
continue;
case "expired_token":
return "expired";
// caller re-issues a fresh code
case "access_denied":
throw new Error("Authorization denied by user.");
default:
throw new Error(`Token exchange failed: ${error.error} \u2014 ${error.error_description ?? ""}`);
}
}
return "expired";
}
function loginTimeoutMessage(context) {
const base = "Login timed out (2 minutes)";
if (context !== "local") return base;
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.`;
}
function isRemoteEnvironment() {
if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES) return true;
if (fs9.existsSync("/.dockerenv")) return true;
if (process.env.SSH_CLIENT || process.env.SSH_TTY) return true;
if (!process.stdout.isTTY || !process.stdin.isTTY) return true;
return false;
}
function hintVpnIfDisconnected(context) {
if (context === RESERVED_REGISTRY_CONTEXT) return;
try {
const state = tailscaleBackendState();
if (state !== "Running") {
console.log("");
logInfo(`VPN not connected. To access dev services, run:`);
logInfo(` mesh vpn connect ${context}`);
}
} catch {
}
}
function accountIdFromRoleArn(roleArn) {
return roleArn?.match(/^arn:aws:iam::(\d{12}):/)?.[1];
}
function renderAwsFallbackStatus(context, reason, identity, expectedAccountId) {
if (!identity) {
return {
lines: [
{
level: "warn",
text: `${reason}, and no working AWS credentials were found either.`
},
{ level: "info", text: "Authenticate with whichever you use:" },
{ level: "info", text: ` mesh login ${context} (Zitadel SSO)` },
{
level: "info",
text: " aws sso login --profile \u2026 (AWS SSO profile)"
},
{
level: "info",
text: " export AWS_PROFILE=\u2026 (profile with a key/secret pair)"
}
],
exitCode: 1
};
}
const lines = [
{
level: "success",
text: `AWS credentials are working (${identity.source})`
},
{ level: "info", text: `Identity: ${identity.arn}` },
{ level: "info", text: `Account: ${identity.accountId}` }
];
if (expectedAccountId && identity.accountId !== expectedAccountId) {
lines.push({
level: "warn",
text: `This account does not match ${context}'s configured role account (${expectedAccountId}) \u2014 commands against ${context} will likely fail with AccessDenied. Check AWS_PROFILE / your exported credentials.`
});
} else if (expectedAccountId) {
lines.push({
level: "info",
text: `Account matches ${context}'s configured role account.`
});
} else {
lines.push({
level: "info",
text: `This is a working AWS identity, not a verified connection to ${context} \u2014 nothing here checked that this account is ${context}'s.`
});
}
lines.push({
level: "info",
text: `${reason} \u2014 that only matters for the commands that need a *user* identity (Hub SSO, \`mesh dev\` test users, zero-touch VPN join). Deploys, registry auth and secrets work off these credentials.`
});
return { lines, exitCode: 0 };
}
async function showAwsCredentialStatus(context, reason, config) {
const identity = await probeAwsIdentity();
const { lines, exitCode } = renderAwsFallbackStatus(
context,
reason,
identity,
accountIdFromRoleArn(config.defaultRole ?? config.adminRole)
);
for (const line of lines) {
if (line.level === "success") logSuccess(line.text);
else if (line.level === "warn") logWarn(line.text);
else logInfo(line.text);
}
if (exitCode !== 0) process.exit(exitCode);
}
async function showStatus(context, config) {
const creds = readCredentials(context);
if (!creds) {
await showAwsCredentialStatus(context, "No cached Zitadel session", config);
return;
}
const expired = new Date(creds.expiresAt) < /* @__PURE__ */ new Date();
if (expired && creds.refreshToken) {
logInfo("Token expired, attempting refresh...");
try {
const tokens = await refreshTokens(
config.issuer,
config.clientId,
creds.refreshToken
);
const idPayload = decodeJwtPayload(tokens.id_token);
writeCredentials(context, {
...creds,
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token ?? creds.refreshToken,
expiresAt: new Date(
Date.now() + tokens.expires_in * 1e3
).toISOString(),
email: idPayload.email ?? creds.email
});
logSuccess(`Token refreshed for ${creds.email ?? "unknown"}`);
return;
} catch {
await showAwsCredentialStatus(
context,
"Zitadel token expired and refresh failed",
config
);
return;
}
}
if (expired) {
await showAwsCredentialStatus(
context,
`Zitadel token expired at ${creds.expiresAt}`,
config
);
return;
}
let tenants = [];
try {
const accessPayload = decodeJwtPayload(creds.accessToken);
tenants = accessPayload["urn:mesh:tenants"] ?? [];
} catch {
}
logSuccess(`Logged in as ${creds.email ?? "unknown"} (${context})`);
logInfo(`Subject: ${creds.sub}`);
logInfo(`Expires: ${creds.expiresAt}`);
if (tenants.length > 0) {
logInfo(`Deployable tenants: ${tenants.join(", ")}`);
}
}
function registerLoginCommand(program2) {
program2.command("login").description("Authenticate with Zitadel (OIDC PKCE or Device Code)").argument("<context>", 'Platform context (e.g., "mesh.dev")').option(
"--status",
"Show current authentication status \u2014 the Zitadel session if one is cached, otherwise the AWS identity the credential chain resolves (SSO profile or key pair). Exits non-zero only when neither works."
).option("--device", "Force device code flow (no callback server needed)").option(
"--export",
"After login, print a self-refreshing AWS credential_process profile as shell `export` statements (use with `eval`). The resulting shell auto-refreshes credentials via `mesh login`. Requires --role or MESH_AWS_ROLE."
).option(
"--static",
"With --export: print raw temporary AWS credentials (a frozen ~1h triple) instead of the default self-refreshing credential_process profile"
).option(
"--credential-process",
"Print AWS credential_process JSON (used by mesh dev's temp profile); refreshes the Zitadel token as needed. Requires --role or a cached defaultRole."
).option(
"--role <arn>",
"IAM role ARN to assume via web identity (used with --export). Saved as the default for this context so future --export runs can omit it. Resolution order: --role > MESH_AWS_ROLE > cached defaultRole."
).option(
"--region <region>",
"AWS region to include in the exported AWS_REGION. Defaults to AWS_REGION env or us-east-2."
).option(
"--no-registry",
"Deprecated and ignored \u2014 mesh login no longer touches the package registry (that is `mesh registry login`)"
).action(async (context, opts) => {
refuseReservedRegistryContext(context, "login");
let config = getContextConfig(context);
if (!config) {
config = await discoverConfigGuarded(context);
if (!config) {
logError(renderNoConfigHelp(context));
process.exit(1);
}
}
if (opts.status) {
await showStatus(context, config);
return;
}
if (opts.export) {
await exportAwsCredentials(context, config, opts);
return;
}
if (opts.credentialProcess) {
await credentialProcessAwsCredentials(context, config, opts);
return;
}
if (opts.registry === false) {
logWarn(NO_REGISTRY_DEPRECATED);
}
try {
await runLoginFlow(context, config, opts);
} catch (err) {
logError(`Login failed: ${err.message}`);
process.exit(1);
}
await hintRegistryIfMissing();
});
program2.command("logout").description("Clear cached Zitadel credentials").argument("<context>", 'Platform context (e.g., "mesh.dev")').action((context) => {
refuseReservedRegistryContext(context, "logout");
clearCredentials(context);
logSuccess(`Logged out of ${context}. Credentials cleared.`);
});
}
function refuseReservedRegistryContext(context, verb) {
if (context !== RESERVED_REGISTRY_CONTEXT) return;
throw new MeshCliError(
`"registry" is the package registry's own session, not a platform context.`,
{ remediation: { command: `mesh registry ${verb}` } }
);
}
async function hintRegistryIfMissing() {
try {
const { readRegistrySession: readRegistrySession2 } = await Promise.resolve().then(() => (init_registry_identity(), registry_identity_exports));
if (readRegistrySession2()) return;
const { probeRegistryToken: probeRegistryToken2 } = await Promise.resolve().then(() => (init_auth_preflight(), auth_preflight_exports));
const probe = await probeRegistryToken2();
if (probe.state === "missing" || probe.state === "expired") {
logInfo(REGISTRY_SEPARATE_HINT);
}
} catch {
}
}
function renderNoConfigHelp(context) {
const pinned = firstPartyDomainFor(context);
return `No configuration found for "${context}".
Tried:
1. SSM: /mesh-platform/${context.split(".")[0]}/${context.split(".")[1]}/platform/zitadel
(requires AWS SSO login + read access)
` + (pinned ? ` 2. HTTPS: https://cli.${pinned}/.well-known/mesh.json (pinned first-party domain)
` : ` 2. HTTPS: https://cli.${context}/.well-known/mesh.json
(only attempted for a full domain, or a known first-party context)
`) + `To fix:
- Use the platform's full domain: mesh login <env>.<tenant>.meshtech.io
- Or, if you have AWS access, ensure SSO is active: aws sso login --profile <profile>
- Or add config manually to ${CONFIG_FILE}:
{
"${context}": {
"issuer": "https://identity.<env>.<your-platform-domain>",
"clientId": "<cli-oidc-client-id>"
}
}
`;
}
function tokenStillValid(expiresAt, marginMs = 0, now = Date.now()) {
return new Date(expiresAt).getTime() - marginMs > now;
}
async function getValidToken(context, opts = {}) {
const config = getContextConfig(context);
if (!config) return null;
const creds = readCredentials(context);
if (!creds) return null;
if (tokenStillValid(creds.expiresAt, opts.marginMs ?? 0)) {
return creds.idToken;
}
return remintToken(context, config, creds);
}
async function remintToken(context, config, creds) {
if (!creds.refreshToken) return null;
let tokens;
try {
tokens = await refreshTokens(config.issuer, config.clientId, creds.refreshToken);
} catch {
return null;
}
let email = creds.email;
try {
email = decodeJwtPayload(tokens.id_token).email ?? creds.email;
} catch {
}
try {
writeCredentials(context, {
...creds,
idToken: tokens.id_token,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token ?? creds.refreshToken,
expiresAt: new Date(Date.now() + tokens.expires_in * 1e3).toISOString(),
email
});
} catch (err) {
logWarn(
`The refreshed ${context} session could not be cached (${err instanceof Error ? err.message : String(err)}) \u2014 this run continues on the new token, but the next one may need a fresh sign-in.`
);
}
return tokens.id_token;
}
async function forceRefreshToken(context) {
const config = getContextConfig(context);
if (!config) return null;
const creds = readCredentials(context);
if (!creds) return null;
return remintToken(context, config, creds);
}
async function probeCredentials(context, roleArn) {
const cached = readCredentials(context);
if (!cached) return { state: "no-session" };
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SESSION_TOKEN) {
return { state: "stale-env-override" };
}
const token = await getValidToken(context);
if (!token) return { state: "expired-session" };
const sessionName = (cached.email ?? "mesh-cli-doctor").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
const sts = await assumeRoleCredentials(roleArn, token, sessionName);
if (!sts) {
return {
state: "assume-denied",
detail: describeAssumeFailure({ roleArn, context, issuer: tokenIssuer(token) }, "inline")
};
}
const ttlSeconds = Math.max(
0,
Math.round((Date.parse(sts.Expiration) - Date.now()) / 1e3)
);
return { state: "ok", ttlSeconds, expiresAt: sts.Expiration, email: cached.email };
}
function shellSingleQuote(value) {
return `'${value.replace(/'/g, "'\\''")}'`;
}
function renderExportStaticLines(args) {
return `export AWS_ACCESS_KEY_ID=${shellSingleQuote(args.accessKey)}
export AWS_SECRET_ACCESS_KEY=${shellSingleQuote(args.secretKey)}
export AWS_SESSION_TOKEN=${shellSingleQuote(args.sessionToken)}
export AWS_REGION=${shellSingleQuote(args.region)}
`;
}
function renderExportProfileLines(args) {
return `unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
export AWS_PROFILE=${shellSingleQuote(args.profileName)}
export AWS_REGION=${shellSingleQuote(args.region)}
`;
}
function resolveUserAwsConfigPath(env = process.env) {
return env.AWS_CONFIG_FILE ?? path9.join(env.HOME ?? "~", ".aws", "config");
}
function canWriteDir(dir) {
try {
fs9.mkdirSync(dir, { recursive: true });
fs9.accessSync(dir, fs9.constants.W_OK);
return true;
} catch {
return false;
}
}
function resolveAwsConfigTarget(env = process.env) {
if (env.AWS_CONFIG_FILE) return { configPath: env.AWS_CONFIG_FILE, redirected: false };
const real = path9.join(env.HOME ?? "~", ".aws", "config");
if (canWriteDir(path9.dirname(real))) return { configPath: real, redirected: false };
return { configPath: path9.join(CONFIG_DIR, "aws-config"), redirected: true };
}
function resolveRoleOrExplain(opts, config) {
const role = opts.role ?? process.env.MESH_AWS_ROLE ?? config.defaultRole;
if (!role) {
return {
error: "No IAM role available: pass --role <arn>, set MESH_AWS_ROLE, or run `mesh login <context> --export --role <arn>` once to cache a default role for this context."
};
}
return { role };
}
async function runLoginFlow(context, config, opts = {}) {
const useDevice = opts.device || isRemoteEnvironment();
if (useDevice) {
logInfo("Using device code flow");
await deviceCodeLogin(context, config);
} else {
await login(context, config);
}
}
async function ensureValidToken(context, config, opts) {
let token = await getValidToken(context);
if (token) return token;
logInfo(`No valid Zitadel session for ${context} \u2014 running login flow`);
try {
await runLoginFlow(context, config, opts);
} catch (err) {
logError(`Login failed: ${err.message}`);
return null;
}
return await getValidToken(context);
}
async function ensureLogin(context, opts = {}) {
const existing = readCredentials(context);
if (existing && new Date(existing.expiresAt) > /* @__PURE__ */ new Date()) return existing;
if (opts.interactive === false) return null;
const config = getContextConfig(context) ?? await discoverConfigGuarded(context);
if (!config) {
logWarn(`No login config for "${context}". Run: mesh login ${context}`);
return null;
}
const token = await ensureValidToken(context, config, opts);
if (!token) return null;
return readCredentials(context);
}
async function discoverConfigGuarded(context) {
let config = null;
if (parseTenantEnv(context)) {
config = await discoverConfigFromSsm(context);
}
if (!config) {
const pinned = firstPartyDomainFor(context);
if (pinned) {
logInfo(`"${context}" is a known Mesh platform \u2014 resolving via ${pinned} (no AWS needed)`);
config = await discoverConfig(pinned, context);
}
}
if (!config && context.split(".").length - 1 >= 2) {
config = await discoverConfig(context);
}
return config;
}
async function exportAwsCredentials(context, config, opts) {
const token = await ensureValidToken(context, config, opts);
if (!token) {
process.exit(1);
}
const resolvedRole = resolveRoleOrExplain(opts, config);
if ("error" in resolvedRole) {
logError(
"--export requires --role <arn> (or MESH_AWS_ROLE env var, or a cached defaultRole).\n Example: mesh login mesh.dev --export --role arn:aws:iam::123456789012:role/mesh-developer\n After the first run, the role is saved to ~/.config/mesh/config.json and --role can be omitted."
);
process.exit(1);
}
let roleArn = resolvedRole.role;
if (!opts.role && !process.env.MESH_AWS_ROLE && config.defaultRole) {
roleArn = selectRoleForCaller(token, {
defaultRole: config.defaultRole,
adminRole: config.adminRole,
adminClaimRoles: config.adminClaimRoles
});
}
if (config.adminRole && roleArn === config.adminRole && !opts.role) {
logInfo(
`Caller has admin Zitadel role \u2014 assuming ${roleArn.split("/").pop()} (admin variant)`
);
}
if (opts.role && opts.role !== config.defaultRole) {
writeContextConfig(context, { ...config, defaultRole: opts.role });
logInfo(`Saved default role for ${context}`);
}
const region = opts.region ?? process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
const roleName = roleArn.split("/").pop() ?? roleArn;
if (opts.static) {
const creds = readCredentials(context);
const sessionName = (creds?.email ?? "mesh-cli-export").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
const env = await assumeRoleWithWebIdentity(roleArn, token, sessionName);
if (!env) {
logError(
describeAssumeFailure({ roleArn, context, issuer: tokenIssuer(token) })
);
process.exit(1);
}
const accessKey = env.AWS_ACCESS_KEY_ID;
const secretKey = env.AWS_SECRET_ACCESS_KEY;
const sessionToken = env.AWS_SESSION_TOKEN;
if (!accessKey || !secretKey || !sessionToken) {
logError("AWS STS returned incomplete credentials");
process.exit(1);
}
process.stdout.write(
renderExportStaticLines({ accessKey, secretKey, sessionToken, region })
);
logSuccess(`Exported static AWS credentials for ${roleName} in ${region}`);
return;
}
const meshBin = resolveStableMeshBin(process.argv[1]);
const sanitized = context.replace(/[^A-Za-z0-9_-]/g, "-");
const profileName = `mesh-${sanitized}`;
const { configPath, redirected } = resolveAwsConfigTarget();
fs9.mkdirSync(path9.dirname(configPath), { recursive: true });
const existedBefore = fs9.existsSync(configPath);
const existing = existedBefore ? fs9.readFileSync(configPath, "utf-8") : "";
const mode = existedBefore ? fs9.statSync(configPath).mode & 511 : 384;
atomicWriteFileSync(
configPath,
upsertManagedAwsConfigSection(
existing,
context,
renderCredentialProcessProfile({
profileName,
context,
roleArn,
region,
meshBin
})
),
mode
);
process.stdout.write(renderExportProfileLines({ profileName, region }));
if (redirected) {
process.stdout.write(`export AWS_CONFIG_FILE=${shellSingleQuote(configPath)}
`);
}
logSuccess(
`Exported self-refreshing AWS profile ${profileName} for ${roleName} in ${region} (managed section in ${configPath})`
);
}
async function credentialProcessAwsCredentials(context, config, opts) {
const token = await getValidToken(context);
if (!token) {
logError(
`No valid Zitadel session for ${context} (missing, expired, or refresh failed). Run: mesh login ${context}`
);
process.exit(1);
}
const resolvedRole = resolveRoleOrExplain(opts, config);
if ("error" in resolvedRole) {
logError(resolvedRole.error);
process.exit(1);
}
const creds = readCredentials(context);
const sessionName = (creds?.email ?? "mesh-cli-credential-process").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
const stsCreds = await assumeRoleCredentials(resolvedRole.role, token, sessionName);
if (!stsCreds) {
logError(
describeAssumeFailure({
roleArn: resolvedRole.role,
context,
issuer: tokenIssuer(token)
})
);
process.exit(1);
}
process.stdout.write(toCredentialProcessJson(stsCreds) + "\n");
}
function readAllContextConfigs() {
return readConfig();
}
async function discoverConfigForContext(context) {
return getContextConfig(context) ?? await discoverConfigGuarded(context);
}
var CONFIG_DIR, CONFIG_FILE, CREDENTIALS_FILE, REDIRECT_PORT, REDIRECT_URI, SCOPES, atomicWriteCounter, MAX_DEVICE_CODES, RESERVED_REGISTRY_CONTEXT, NO_REGISTRY_DEPRECATED, REGISTRY_SEPARATE_HINT;
var init_login = __esm({
"libs/mesh-cli/src/commands/login.ts"() {
"use strict";
init_log();
init_errors();
init_aws_auth();
init_first_party_contexts();
init_vpn2();
CONFIG_DIR = path9.join(
process.env.XDG_CONFIG_HOME ?? path9.join(process.env.HOME ?? "~", ".config"),
"mesh"
);
CONFIG_FILE = path9.join(CONFIG_DIR, "config.json");
CREDENTIALS_FILE = path9.join(CONFIG_DIR, "credentials.json");
REDIRECT_PORT = 9876;
REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
SCOPES = "openid email profile offline_access urn:zitadel:iam:org:project:id:zitadel:aud";
atomicWriteCounter = 0;
MAX_DEVICE_CODES = 3;
RESERVED_REGISTRY_CONTEXT = "registry";
NO_REGISTRY_DEPRECATED = "--no-registry is deprecated and ignored: mesh login no longer touches the registry.";
REGISTRY_SEPARATE_HINT = "Package registry access is separate from platform sign-in \u2014 run: mesh registry login";
}
});
// libs/mesh-cli/src/utils/registry-broker.ts
function deriveRegistryBroker(context, getCfg = getContextConfig) {
const cfg = getCfg(context);
if (!cfg?.issuer) return null;
try {
const u = new URL(cfg.issuer);
const parts = u.hostname.split(".");
parts[0] = "registry-auth";
return `https://${parts.join(".")}`;
} catch {
return null;
}
}
async function classifyBrokerResponse(res) {
if (res.status === 401) return { ok: false, failure: { kind: "unauthenticated" } };
if (res.status === 403) {
const body2 = await res.json().catch(() => ({}));
return {
ok: false,
failure: {
kind: "not-authorized",
email: typeof body2.email === "string" ? body2.email : void 0,
requiredRoles: Array.isArray(body2.requiredRoles) ? body2.requiredRoles.filter((r) => typeof r === "string") : void 0,
requestUrl: typeof body2.requestUrl === "string" ? body2.requestUrl : void 0
}
};
}
if (res.status !== 200) {
return { ok: false, failure: { kind: "unavailable", detail: `broker returned ${res.status}` } };
}
let body;
try {
body = await res.json();
} catch (err) {
return {
ok: false,
failure: { kind: "unavailable", detail: `malformed broker response: ${err.message}` }
};
}
const { endpoint, authorizationToken, scope, expiresAt } = body;
if (typeof endpoint !== "string" || !endpoint || typeof authorizationToken !== "string" || !authorizationToken) {
return {
ok: false,
failure: { kind: "unavailable", detail: "broker response missing endpoint or authorizationToken" }
};
}
return {
ok: true,
grant: {
scope: typeof scope === "string" && scope ? scope : "@mesh-tech",
endpoint,
authorizationToken,
expiresAt: typeof expiresAt === "string" ? expiresAt : void 0
}
};
}
async function fetchRegistryGrant(context, brokerUrl, deps) {
const token = await deps.getValidToken(context);
if (!token) return { ok: false, failure: { kind: "no-session" } };
const doFetch = deps.fetchFn ?? fetch;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), deps.timeoutMs ?? PUBLISHED_BROKER_TIMEOUT_MS);
try {
const res = await doFetch(`${brokerUrl.replace(/\/+$/, "")}/token`, {
method: "POST",
headers: { authorization: `Bearer ${token}` },
signal: controller.signal
});
return await classifyBrokerResponse(res);
} catch (err) {
return {
ok: false,
failure: { kind: "unavailable", detail: err.message }
};
} finally {
clearTimeout(timer);
}
}
function realGrantDeps() {
return { getValidToken: (c) => getValidToken(c) };
}
var PUBLISHED_BROKER_TIMEOUT_MS;
var init_registry_broker = __esm({
"libs/mesh-cli/src/utils/registry-broker.ts"() {
"use strict";
init_login();
PUBLISHED_BROKER_TIMEOUT_MS = 15e3;
}
});
// libs/mesh-cli/src/utils/registry-identity.ts
var registry_identity_exports = {};
__export(registry_identity_exports, {
REGISTRY_ALIAS: () => REGISTRY_ALIAS,
REGISTRY_CREDENTIAL_KEY: () => REGISTRY_CREDENTIAL_KEY,
REGISTRY_DOMAIN: () => REGISTRY_DOMAIN,
RegistryIdentityError: () => RegistryIdentityError,
clearRegistrySession: () => clearRegistrySession,
describeSessionExpiry: () => describeSessionExpiry,
ensureRegistrySession: () => ensureRegistrySession,
identityChanged: () => identityChanged,
isReservedRegistryContext: () => isReservedRegistryContext,
logRegistryIdentity: () => logRegistryIdentity,
readRegistrySession: () => readRegistrySession,
refreshRegistrySession: () => refreshRegistrySession,
registrySessionCandidates: () => registrySessionCandidates,
registryTokenRoles: () => registryTokenRoles,
resolveRegistryIdentity: () => resolveRegistryIdentity
});
function isReservedRegistryContext(context) {
return context === REGISTRY_CREDENTIAL_KEY;
}
function toIdentity(cfg) {
if (!cfg?.issuer || !cfg.clientId) return null;
const registryBroker = cfg.registryBroker ?? deriveRegistryBroker(REGISTRY_CREDENTIAL_KEY, () => cfg);
if (!registryBroker) return null;
return {
issuer: cfg.issuer,
clientId: cfg.clientId,
registryBroker,
discoveredAt: cfg.discoveredAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
};
}
async function resolveRegistryIdentity(opts = {}) {
if (!opts.refresh) {
const cached = toIdentity(getContextConfig(REGISTRY_CREDENTIAL_KEY));
if (cached) return cached;
}
const discovered = await discoverConfigFromWellKnown(REGISTRY_DOMAIN, REGISTRY_CREDENTIAL_KEY, {
quiet: true
});
if (!discovered) {
throw new RegistryIdentityError(
`discovery on ${REGISTRY_DOMAIN} failed \u2014 are you online?`
);
}
const stored = { ...discovered, discoveredAt: (/* @__PURE__ */ new Date()).toISOString() };
const identity = toIdentity(stored);
if (!identity) {
throw new RegistryIdentityError(`the discovery document names no registry broker`);
}
writeContextConfig(REGISTRY_CREDENTIAL_KEY, stored);
return identity;
}
function registrySessionCandidates(issuer, configs = readAllContextConfigs(), credentials = readAllCredentials()) {
const names = issuer ? [REGISTRY_CREDENTIAL_KEY, REGISTRY_ALIAS, REGISTRY_DOMAIN] : [REGISTRY_CREDENTIAL_KEY];
return names.filter((name) => {
if (!(name in credentials)) return false;
const cfg = configs[name];
if (!cfg?.issuer) return false;
return issuer ? cfg.issuer === issuer : true;
});
}
function readRegistrySession() {
const identity = toIdentity(getContextConfig(REGISTRY_CREDENTIAL_KEY));
for (const context of registrySessionCandidates(identity?.issuer ?? null)) {
const creds = readCredentials(context);
if (creds && tokenStillValid(creds.expiresAt)) {
return { context, email: creds.email ?? "unknown", expiresAt: creds.expiresAt };
}
}
return null;
}
async function ensureRegistrySession(opts) {
let identity;
try {
identity = await resolveRegistryIdentity();
} catch (err) {
return { ok: false, reason: "no-identity", detail: err.message };
}
for (const context of registrySessionCandidates(identity.issuer)) {
const token = await getValidToken(context);
if (!token) continue;
const creds2 = readCredentials(context);
if (creds2) return { ok: true, context, email: creds2.email ?? "unknown", expiresAt: creds2.expiresAt };
}
if (!opts.interactive) {
return {
ok: false,
reason: "no-tty",
detail: "no registry session is cached and this run cannot open a sign-in"
};
}
const stored = getContextConfig(REGISTRY_CREDENTIAL_KEY);
if (!stored) {
writeContextConfig(REGISTRY_CREDENTIAL_KEY, {
issuer: identity.issuer,
clientId: identity.clientId,
registryBroker: identity.registryBroker
});
}
logInfo(
opts.device ? "No registry session \u2014 sign in with the device code below\u2026" : "No registry session \u2014 opening your browser to sign in\u2026 (headless? use --device)"
);
try {
await runLoginFlow(REGISTRY_CREDENTIAL_KEY, identity, { device: opts.device });
} catch (err) {
return { ok: false, reason: "login-failed", detail: err.message };
}
const creds = readCredentials(REGISTRY_CREDENTIAL_KEY);
if (!creds) {
return { ok: false, reason: "login-failed", detail: "sign-in completed but no session was cached" };
}
return {
ok: true,
context: REGISTRY_CREDENTIAL_KEY,
email: creds.email ?? "unknown",
expiresAt: creds.expiresAt
};
}
function registryTokenRoles(idToken) {
if (!idToken) return [];
try {
return projectRoleKeys(decodeJwtPayload(idToken)).sort();
} catch {
return [];
}
}
async function refreshRegistrySession(context) {
const before = registryTokenRoles(readCredentials(context)?.idToken);
const idToken = await forceRefreshToken(context);
if (!idToken) return { reminted: false, rolesChanged: false };
const after = registryTokenRoles(idToken);
const same = after.length === before.length && after.every((role, i) => role === before[i]);
return { reminted: true, rolesChanged: !same };
}
function clearRegistrySession() {
clearCredentials(REGISTRY_CREDENTIAL_KEY);
clearContextConfig(REGISTRY_CREDENTIAL_KEY);
}
function identityChanged(before, after) {
return before.issuer !== after.issuer || before.clientId !== after.clientId || before.registryBroker !== after.registryBroker;
}
function describeSessionExpiry(expiresAt, now = Date.now()) {
const ms = new Date(expiresAt).getTime() - now;
if (!(ms > 0)) return "expired";
const totalMinutes = Math.floor(ms / 6e4);
const h = Math.floor(totalMinutes / 60);
const m = totalMinutes % 60;
return h > 0 ? `${h}h ${m}m` : `${m}m`;
}
function logRegistryIdentity(identity) {
let host = identity.registryBroker;
try {
host = new URL(identity.registryBroker).host;
} catch {
}
logInfo(`Mesh package registry: mesh-platform (broker ${host})`);
}
var REGISTRY_ALIAS, REGISTRY_DOMAIN, REGISTRY_CREDENTIAL_KEY, RegistryIdentityError;
var init_registry_identity = __esm({
"libs/mesh-cli/src/utils/registry-identity.ts"() {
"use strict";
init_login();
init_aws_auth();
init_first_party_contexts();
init_registry_broker();
init_errors();
init_log();
REGISTRY_ALIAS = "mesh.dev";
REGISTRY_DOMAIN = (() => {
const domain = firstPartyDomainFor(REGISTRY_ALIAS);
if (!domain) {
throw new Error(`FIRST_PARTY_CONTEXTS has no entry for the registry alias "${REGISTRY_ALIAS}"`);
}
return domain;
})();
REGISTRY_CREDENTIAL_KEY = RESERVED_REGISTRY_CONTEXT;
RegistryIdentityError = class extends MeshCliError {
constructor(detail) {
super(
`Could not resolve the Mesh package registry (${detail}).
Discovery endpoint: https://cli.${REGISTRY_DOMAIN}/.well-known/mesh.json`,
{ remediation: { command: "mesh registry login # retry once you are online" } }
);
this.name = "RegistryIdentityError";
}
};
}
});
// libs/mesh-cli/src/utils/auth-preflight.ts
var auth_preflight_exports = {};
__export(auth_preflight_exports, {
REGISTRY_LOGIN_FIX: () => REGISTRY_LOGIN_FIX,
REGISTRY_LOGIN_FIX_AWS: () => REGISTRY_LOGIN_FIX_AWS,
SSO_LOGIN_FIX: () => SSO_LOGIN_FIX,
SSO_SESSION_SNIPPET: () => SSO_SESSION_SNIPPET,
appUsesMeshPackages: () => appUsesMeshPackages,
awsConfigHasProfile: () => awsConfigHasProfile,
classifyRegistryPreflight: () => classifyRegistryPreflight,
classifyRegistryStatus: () => classifyRegistryStatus,
findUnscopedCodeArtifactRegistry: () => findUnscopedCodeArtifactRegistry,
homeNpmrcPath: () => homeNpmrcPath,
isExpiredAwsTokenMessage: () => isExpiredAwsTokenMessage,
isNpmAuthError: () => isNpmAuthError,
isNpmAuthErrorText: () => isNpmAuthErrorText,
npmrcAuthKeyForEndpoint: () => npmrcAuthKeyForEndpoint,
parseNpmrcRegistryAuth: () => parseNpmrcRegistryAuth,
parseSsoSessionNames: () => parseSsoSessionNames,
probeRegistryToken: () => probeRegistryToken,
readAwsConfig: () => readAwsConfig,
registryLoginFix: () => registryLoginFix,
registryPreflight: () => registryPreflight,
stripUnscopedCodeArtifactRegistry: () => stripUnscopedCodeArtifactRegistry,
upsertNpmrcLines: () => upsertNpmrcLines
});
import * as fs10 from "fs";
import * as os3 from "os";
import * as path10 from "path";
function registryLoginFix() {
return REGISTRY_LOGIN_FIX;
}
function homeNpmrcPath() {
return path10.join(os3.homedir(), ".npmrc");
}
function parseNpmrcRegistryAuth(content) {
for (const line of content.split("\n")) {
const match = line.trim().match(/^\/\/([^:]*\.codeartifact\.[^:]*):_authToken=(.+)$/);
if (match && match[1] && match[2]) {
const endpoint = `https://${match[1]}${match[1].endsWith("/") ? "" : "/"}`;
return { endpoint, token: match[2].trim() };
}
}
return null;
}
function findUnscopedCodeArtifactRegistry(content) {
for (const line of content.split("\n")) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) continue;
const match = trimmed.match(/^registry\s*=\s*(\S+)$/);
if (!match || !match[1]) continue;
if (isCodeArtifactRegistryUrl(match[1])) return line;
}
return null;
}
function isCodeArtifactRegistryUrl(value) {
try {
return new URL(value).hostname.includes(".codeartifact.");
} catch {
return value.includes(".codeartifact.");
}
}
function stripUnscopedCodeArtifactRegistry(content) {
const offender = findUnscopedCodeArtifactRegistry(content);
if (offender === null) return { content, removed: null };
const kept = content.split("\n").filter((line) => findUnscopedCodeArtifactRegistry(line) === null);
return { content: kept.join("\n"), removed: offender };
}
function npmrcAuthKeyForEndpoint(endpoint) {
const withoutScheme = endpoint.replace(/^https?:\/\//, "");
return `//${withoutScheme.endsWith("/") ? withoutScheme : `${withoutScheme}/`}`;
}
function upsertNpmrcLines(content, entries) {
let lines = content.length ? content.split("\n") : [];
const trailingNewline = content.endsWith("\n");
if (trailingNewline) lines = lines.slice(0, -1);
for (const { key, value } of entries) {
const line = `${key}=${value}`;
const idx = lines.findIndex((l) => {
const trimmed = l.trim();
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return false;
const eq = trimmed.indexOf("=");
return eq > 0 && trimmed.slice(0, eq).trim() === key;
});
if (idx >= 0) lines[idx] = line;
else lines.push(line);
}
return lines.join("\n") + "\n";
}
function classifyRegistryStatus(status) {
return status === 401 || status === 403 ? "expired" : "fresh";
}
async function probeRegistryToken(opts) {
const npmrcPath2 = opts?.npmrcPath ?? homeNpmrcPath();
const timeoutMs = opts?.timeoutMs ?? 8e3;
const fetchFn = opts?.fetchFn ?? fetch;
let auth = null;
try {
auth = parseNpmrcRegistryAuth(fs10.readFileSync(npmrcPath2, "utf-8"));
} catch {
}
if (!auth) return { state: "missing" };
try {
const res = await fetchFn(`${auth.endpoint}${encodeURIComponent("@mesh-tech/hub")}`, {
method: "GET",
headers: { authorization: `Bearer ${auth.token}` },
signal: AbortSignal.timeout(timeoutMs)
});
const state = classifyRegistryStatus(res.status);
return state === "expired" ? { state, detail: `registry answered HTTP ${res.status} for an authenticated request` } : { state };
} catch (err) {
return {
state: "unreachable",
detail: err instanceof Error ? err.message : String(err)
};
}
}
function classifyRegistryPreflight(probe, session) {
const email = session?.email;
const fix = "mesh init";
if (probe.state === "fresh") return { state: "valid", email, fix };
return { state: probe.state, email, fix, detail: probe.detail };
}
async function registryPreflight(opts) {
const probe = await probeRegistryToken(opts);
let session = null;
try {
const { readRegistrySession: readRegistrySession2 } = await Promise.resolve().then(() => (init_registry_identity(), registry_identity_exports));
session = readRegistrySession2();
} catch {
}
return classifyRegistryPreflight(probe, session);
}
function isNpmAuthErrorText(text) {
return /\bE?40[13]\b|unauthenticated|unable to authenticate|authentication (required|failed)/i.test(
text
);
}
function isNpmAuthError(err) {
const e = err;
const text = [e?.stderr, e?.message].map((v) => v === null || v === void 0 ? "" : String(v)).join("\n");
return isNpmAuthErrorText(text);
}
function isExpiredAwsTokenMessage(message) {
return /expired|invalid.*(security )?token|token.*(is )?invalid|no credential|could not load credentials|could not be found|sso session/i.test(
message
);
}
function parseSsoSessionNames(content) {
const names = [];
for (const line of content.split("\n")) {
const match = line.trim().match(/^\[sso-session\s+([^\]]+)\]$/);
if (match && match[1]) names.push(match[1].trim());
}
return names;
}
function awsConfigHasProfile(content, profile) {
const pattern = new RegExp(
`^\\[(?:profile\\s+)?${profile.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\]$`
);
return content.split("\n").some((line) => pattern.test(line.trim()));
}
function readAwsConfig() {
const file = process.env.AWS_CONFIG_FILE ?? path10.join(os3.homedir(), ".aws", "config");
try {
return fs10.readFileSync(file, "utf-8");
} catch {
return "";
}
}
function appUsesMeshPackages(appRoot) {
try {
const pkg = JSON.parse(fs10.readFileSync(path10.join(appRoot, "package.json"), "utf-8"));
return [pkg.dependencies, pkg.devDependencies, pkg.optionalDependencies].some(
(deps) => deps && Object.keys(deps).some((name) => name.startsWith("@mesh-tech/"))
);
} catch {
return false;
}
}
var REGISTRY_LOGIN_FIX, REGISTRY_LOGIN_FIX_AWS, SSO_LOGIN_FIX, SSO_SESSION_SNIPPET;
var init_auth_preflight = __esm({
"libs/mesh-cli/src/utils/auth-preflight.ts"() {
"use strict";
REGISTRY_LOGIN_FIX = "mesh registry login";
REGISTRY_LOGIN_FIX_AWS = "mesh registry login --profile mesh-dev";
SSO_LOGIN_FIX = "aws sso login --sso-session=mesh # or: pnpm sso";
SSO_SESSION_SNIPPET = `[sso-session mesh]
sso_start_url = https://trabian.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access`;
}
});
// libs/mesh-cli/src/utils/aws-auth.ts
import * as fs11 from "fs";
import * as path11 from "path";
import {
AssumeRoleCommand,
AssumeRoleWithWebIdentityCommand,
GetCallerIdentityCommand,
STSClient
} from "@aws-sdk/client-sts";
function derivePlatformContext(appRoot, stack) {
const configFile = path11.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs11.existsSync(configFile)) return null;
const content = fs11.readFileSync(configFile, "utf-8");
const jsonMatch = content.match(/mesh:platform:\s*'(\{[^']+\})'/);
if (jsonMatch) {
try {
const parsed = JSON.parse(jsonMatch[1]);
if (parsed.name && parsed.env) return `${parsed.name}.${parsed.env}`;
} catch {
}
}
const nameMatch = content.match(/mesh:platform:[\s\S]*?name:\s*(\S+)/);
const envMatch = content.match(/mesh:platform:[\s\S]*?env:\s*(\S+)/);
if (nameMatch && envMatch) {
return `${nameMatch[1]}.${envMatch[1]}`;
}
return null;
}
function quoteCredProcessToken(token) {
return /\s/.test(token) ? `"${token}"` : token;
}
function resolveStableMeshBin(argv1, deps = {}) {
const exists = deps.exists ?? fs11.existsSync;
const warn = deps.warn ?? logWarn;
let resolved = argv1 ? path11.resolve(argv1) : void 0;
if (resolved && /\.ts$/.test(resolved)) {
resolved = path11.join(path11.dirname(resolved), path11.basename(resolved, ".ts") + ".mjs");
}
const transient = resolved !== void 0 && (/[\\/]_npx[\\/]/.test(resolved) || /^\/(?:private\/)?(?:tmp|var\/folders)\//.test(resolved));
if (!resolved || !exists(resolved) || transient) {
warn(
`credential_process cannot reference a stable mesh binary` + (resolved ? ` (${resolved} is transient or missing)` : "") + ` \u2014 falling back to \`mesh\` on PATH. Install mesh-cli globally so the profile keeps refreshing.`
);
return "mesh";
}
return resolved;
}
function renderCredentialProcessProfile(args) {
const lines = [
`[profile ${args.profileName}]`,
`credential_process = ${quoteCredProcessToken(args.meshBin)} login ${quoteCredProcessToken(args.context)} --credential-process --role ${quoteCredProcessToken(args.roleArn)}`
];
if (args.region) lines.push(`region = ${args.region}`);
return lines.join("\n") + "\n";
}
function orphanedSectionTail(existing, afterStart, boundIdx) {
const limit = boundIdx === -1 ? existing.length : boundIdx;
let idx = afterStart;
if (idx >= limit || existing[idx] !== "\n") return Math.min(idx, limit);
idx++;
let claimedHeader = false;
while (idx < limit) {
const nl = existing.indexOf("\n", idx);
const lineEnd = nl === -1 || nl > limit ? limit : nl;
const line = existing.slice(idx, lineEnd);
const ours = claimedHeader ? /^[A-Za-z_][A-Za-z0-9_]*\s*=/.test(line) : /^\[profile [^\]]+\]\s*$/.test(line);
if (!ours) return idx;
claimedHeader = true;
idx = lineEnd >= limit ? limit : lineEnd + 1;
}
return limit;
}
function upsertManagedAwsConfigSection(existing, context, profileBlock) {
const body = profileBlock.endsWith("\n") ? profileBlock : profileBlock + "\n";
const section = `${MANAGED_START(context)}
${body}${MANAGED_END}
`;
const startIdx = existing.indexOf(MANAGED_START(context));
if (startIdx !== -1) {
const afterStart = startIdx + MANAGED_START(context).length;
const endIdx = existing.indexOf(MANAGED_END, afterStart);
const foreignIdx = existing.indexOf(MANAGED_START_PREFIX, afterStart);
let tail;
if (endIdx !== -1 && (foreignIdx === -1 || endIdx < foreignIdx)) {
const tailIdx = endIdx + MANAGED_END.length;
tail = existing[tailIdx] === "\n" ? tailIdx + 1 : tailIdx;
} else {
tail = orphanedSectionTail(existing, afterStart, foreignIdx);
}
return existing.slice(0, startIdx) + section + existing.slice(tail);
}
if (existing.length === 0) return section;
const sep7 = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
return existing + sep7 + section;
}
function stripBareProfile(existing, profileName) {
const header = `[profile ${profileName}]`;
const lines = existing.split("\n");
const out = [];
let managed = false;
let dropping = false;
for (const line of lines) {
if (line.startsWith(MANAGED_START_PREFIX)) managed = true;
if (managed) {
out.push(line);
if (line === MANAGED_END) managed = false;
continue;
}
if (line.trim() === header) {
dropping = true;
continue;
}
if (dropping && line.startsWith("[")) dropping = false;
if (!dropping) out.push(line);
}
return out.join("\n");
}
function newStsClient() {
return new STSClient({ region: DEFAULT_REGION });
}
function toEnv(credentials) {
const { AccessKeyId, SecretAccessKey, SessionToken } = credentials;
if (!AccessKeyId || !SecretAccessKey || !SessionToken) return null;
return {
AWS_ACCESS_KEY_ID: AccessKeyId,
AWS_SECRET_ACCESS_KEY: SecretAccessKey,
AWS_SESSION_TOKEN: SessionToken
};
}
function isMaxSessionDurationError(err) {
if (!(err instanceof Error)) return false;
const message = err.message ?? "";
return message.includes("MaxSessionDuration") || message.includes("DurationSeconds");
}
async function assumeRole(roleArn, sessionName = "mesh-cli", durationSeconds = 43200) {
const sts = newStsClient();
for (const duration of [durationSeconds, 3600]) {
try {
const resp = await sts.send(
new AssumeRoleCommand({
RoleArn: roleArn,
RoleSessionName: sessionName,
DurationSeconds: duration
})
);
if (!resp.Credentials) return null;
return toEnv(resp.Credentials);
} catch (err) {
if (!isMaxSessionDurationError(err) || duration === 3600) {
const message = err instanceof Error ? err.message : String(err);
logWarn(`AssumeRole failed: ${message}`);
if (isExpiredAwsTokenMessage(message)) {
logWarn(` Your AWS SSO session looks expired or missing \u2014 refresh with: ${SSO_LOGIN_FIX}`);
}
return null;
}
}
}
return null;
}
async function assumeRoleCredentials(roleArn, idToken, sessionName, durationSeconds = 43200) {
const sts = newStsClient();
for (const duration of [durationSeconds, 3600]) {
try {
const resp = await sts.send(
new AssumeRoleWithWebIdentityCommand({
RoleArn: roleArn,
RoleSessionName: sessionName,
WebIdentityToken: idToken,
DurationSeconds: duration
})
);
if (!resp.Credentials) return null;
const env = toEnv(resp.Credentials);
if (!env) return null;
return {
// `env` is a `Record<string, string>` (index-signature typed), so
// `noUncheckedIndexedAccess` widens these to `string | undefined`
// even though `toEnv` guarantees they're set — assert accordingly.
AccessKeyId: env.AWS_ACCESS_KEY_ID,
SecretAccessKey: env.AWS_SECRET_ACCESS_KEY,
SessionToken: env.AWS_SESSION_TOKEN,
Expiration: resp.Credentials.Expiration.toISOString()
};
} catch (err) {
if (!isMaxSessionDurationError(err) || duration === 3600) {
logWarn(
`AssumeRoleWithWebIdentity failed: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
}
return null;
}
async function assumeRoleWithWebIdentity(roleArn, idToken, sessionName, durationSeconds = 43200) {
const creds = await assumeRoleCredentials(roleArn, idToken, sessionName, durationSeconds);
if (!creds) return null;
return toEnv(creds);
}
function tokenIssuer(token) {
const iss = decodeJwtPayload2(token)?.iss;
return typeof iss === "string" && iss ? iss : void 0;
}
function describeAssumeFailure(opts, format = "block") {
const account = /^arn:aws:iam::(\d+):/.exec(opts.roleArn)?.[1];
const head = `AssumeRoleWithWebIdentity failed for ${opts.roleArn}` + (account ? ` (account ${account})` : "");
const session = opts.context ? `mesh login context '${opts.context}'` + (opts.issuer ? ` (issuer ${opts.issuer})` : "") : opts.issuer ? `issuer ${opts.issuer}` : void 0;
if (format === "inline") {
return head + (session ? ` via ${session}` : "") + " \u2014 if the role's account doesn't trust this issuer, this is a context mismatch (wrong `mesh:platform`), not an IAM permission problem";
}
const lines = [head];
if (session) lines.push(` Session: ${session}`);
lines.push(
" If the role's account does not trust this issuer, you are logged into the",
" wrong context for this stack \u2014 check `mesh:platform` in the app's Pulumi",
" config and `mesh login <platform>.<env>` accordingly. (This is a context",
" mismatch, not an IAM permission problem.)"
);
return lines.join("\n");
}
function toCredentialProcessJson(creds) {
return JSON.stringify({
Version: 1,
AccessKeyId: creds.AccessKeyId,
SecretAccessKey: creds.SecretAccessKey,
SessionToken: creds.SessionToken,
Expiration: creds.Expiration
});
}
async function probeAwsIdentity() {
try {
const out = await newStsClient().send(new GetCallerIdentityCommand({}));
if (!out.Arn || !out.Account) return null;
return {
arn: out.Arn,
accountId: out.Account,
source: process.env.AWS_ACCESS_KEY_ID ? "environment" : "profile"
};
} catch {
return null;
}
}
function decodeJwtPayload2(token) {
const parts = token.split(".");
if (parts.length !== 3 || !parts[1]) return null;
try {
return JSON.parse(Buffer.from(parts[1], "base64url").toString());
} catch {
return null;
}
}
function selectRoleForCaller(idToken, options) {
if (!options.adminRole || !idToken) return options.defaultRole;
const adminRoles = options.adminClaimRoles ?? ["mesh.platform:admin"];
const claims = decodeJwtPayload2(idToken);
if (!claims) return options.defaultRole;
const claimedRoles = projectRoleKeys(claims);
return adminRoles.some((target) => claimedRoles.includes(target)) ? options.adminRole : options.defaultRole;
}
function projectRoleKeys(claims) {
const names = /* @__PURE__ */ new Set();
for (const [key, value] of Object.entries(claims)) {
if (!key.startsWith("urn:zitadel:iam:org:project:")) continue;
if (!key.endsWith(":roles")) continue;
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
for (const name of Object.keys(value)) {
if (name) names.add(name);
}
}
return [...names];
}
async function resolveAwsCredentials(roleArn, appRoot, stack) {
const roleName = roleArn.split("/").pop() ?? roleArn;
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SESSION_TOKEN) {
logInfo("Using existing AWS credentials from environment");
return {
env: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY ?? "",
AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN
},
method: "environment"
};
}
const platformContext = derivePlatformContext(appRoot, stack);
if (platformContext) {
const meshCreds = await ensureLogin(platformContext, { interactive: false });
if (meshCreds) {
const sessionName = (meshCreds.email ?? "mesh-cli").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
logInfo(`Assuming ${roleName} via Zitadel (${meshCreds.email ?? platformContext})`);
const creds2 = await assumeRoleWithWebIdentity(roleArn, meshCreds.idToken, sessionName);
if (creds2) {
logSuccess(`Assumed ${roleName} via Zitadel`);
return { env: creds2, method: "zitadel" };
}
logWarn(
`Zitadel JWT auth failed \u2014 falling back to AWS SSO.
(Try: mesh login ${platformContext})`
);
}
}
logInfo(`Assuming ${roleName} via AWS SSO`);
const creds = await assumeRole(roleArn, "mesh-cli");
if (creds) {
logSuccess(`Assumed ${roleName}`);
return { env: creds, method: "sso" };
}
if (platformContext && process.stdout.isTTY) {
const meshCreds = await ensureLogin(platformContext);
if (meshCreds) {
const sessionName = (meshCreds.email ?? "mesh-cli").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
logInfo(`Assuming ${roleName} via Zitadel (${meshCreds.email ?? platformContext})`);
const zCreds = await assumeRoleWithWebIdentity(roleArn, meshCreds.idToken, sessionName);
if (zCreds) {
logSuccess(`Assumed ${roleName} via Zitadel`);
return { env: zCreds, method: "zitadel" };
}
}
}
return null;
}
var DEFAULT_REGION, MANAGED_START_PREFIX, MANAGED_START, MANAGED_END;
var init_aws_auth = __esm({
"libs/mesh-cli/src/utils/aws-auth.ts"() {
"use strict";
init_log();
init_auth_preflight();
init_login();
DEFAULT_REGION = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
MANAGED_START_PREFIX = `# >>> mesh-cli managed profile `;
MANAGED_START = (context) => `${MANAGED_START_PREFIX}${context} >>>`;
MANAGED_END = `# <<< mesh-cli managed <<<`;
}
});
// libs/mesh-cli/src/utils/vpn-join.ts
function resolveVpnJoinBroker(context, getCfg = getContextConfig) {
const cfg = getCfg(context);
if (cfg?.vpnJoinBroker) return cfg.vpnJoinBroker;
return deriveVpnJoinBroker(context, getCfg);
}
function deriveVpnJoinBroker(context, getCfg = getContextConfig) {
const cfg = getCfg(context);
if (!cfg?.issuer) return null;
try {
const u = new URL(cfg.issuer);
const parts = u.hostname.split(".");
parts[0] = "vpn-join";
return `https://${parts.join(".")}`;
} catch {
return null;
}
}
async function mintPreAuthKey(context, brokerUrl, deps) {
const token = await deps.getValidToken(context);
if (!token) return null;
const doFetch = deps.fetchFn ?? fetch;
try {
const res = await doFetch(`${brokerUrl}/preauth`, {
method: "POST",
headers: { authorization: `Bearer ${token}` }
});
if (!res.ok) return null;
const body = await res.json();
if (!body.authKey) return null;
return { authKey: body.authKey, loginServer: body.loginServer ?? "" };
} catch {
return null;
}
}
var init_vpn_join = __esm({
"libs/mesh-cli/src/utils/vpn-join.ts"() {
"use strict";
init_login();
}
});
// libs/mesh-cli/src/utils/pulumi-run.ts
function credProbeToPreflightError(probe, context) {
switch (probe.state) {
case "ok":
return null;
case "no-session":
return {
fatal: true,
message: `Not logged in for ${context}. Run:
mesh login ${context} --device`
};
case "expired-session":
return {
fatal: true,
message: `Your mesh login session for ${context} expired. Run:
mesh login ${context} --device`
};
case "assume-denied":
return {
fatal: true,
message: `Logged in, but couldn't assume the deployer role \u2014 ${probe.detail}
Check mesh:deployerRole in the stack config and your IAM access.`
};
case "stale-env-override":
return {
fatal: false,
message: `Stale AWS_* env vars are set and will override your mesh login.
If pulumi fails with an auth error, clear them first:
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN`
};
}
}
async function resolvePulumiEnv(opts) {
const { appRoot, stack } = opts;
const roleArn = readStackConfig(appRoot, stack, "mesh:deployerRole");
if (!roleArn) {
if (process.env.AWS_ACCESS_KEY_ID) {
return {};
}
logError(
`No mesh:deployerRole in Pulumi.${stack}.yaml \u2014 mesh needs it to assume a role for the Pulumi backend.
Set it (copy the value from a base stack), e.g.:
mesh deploy config set mesh:deployerRole arn:aws:iam::<account>:role/<tenant>-<stage>-apps-deployer --stack ${stack}
(or provide operator AWS credentials in the environment for platform stacks).`
);
process.exit(1);
}
const hasAmbientCreds = !!process.env.AWS_ACCESS_KEY_ID;
const context = derivePlatformContext(appRoot, stack);
if (!hasAmbientCreds && context) {
const probe = await probeCredentials(context, roleArn);
const preflight = credProbeToPreflightError(probe, context);
if (preflight?.fatal) {
logError(preflight.message);
process.exit(1);
}
if (preflight) logWarn(preflight.message);
}
const resolved = await resolveAwsCredentials(roleArn, appRoot, stack);
if (!resolved) {
logError(
`Failed to obtain AWS credentials for ${roleArn}.
` + (context ? ` Try: mesh login ${context} --device
` : "") + ` (or provide AWS credentials in the environment).`
);
process.exit(1);
}
return { ...resolved.env };
}
var init_pulumi_run = __esm({
"libs/mesh-cli/src/utils/pulumi-run.ts"() {
"use strict";
init_log();
init_pulumi();
init_aws_auth();
init_login();
}
});
// libs/mesh-cli/src/utils/kubeconfig.ts
import * as fs12 from "fs";
import * as os4 from "os";
import * as path12 from "path";
function buildKubeconfig(cluster, region) {
return `apiVersion: v1
kind: Config
clusters:
- cluster:
server: ${cluster.clusterEndpoint}
certificate-authority-data: ${cluster.clusterCaData}
name: eks-cluster
contexts:
- context:
cluster: eks-cluster
user: eks-user
name: eks-context
current-context: eks-context
users:
- name: eks-user
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
command: aws
args:
- eks
- get-token
- --cluster-name
- ${cluster.clusterName}
- --region
- ${region}
`;
}
function sessionKubeconfigPath(sessionName) {
return path12.join(os4.tmpdir(), "mesh-dev-sessions", `${sessionName}.kubeconfig`);
}
function resolveHubPlatformName(platform) {
return platform?.name ?? "mesh";
}
async function ssmGetParameter(name) {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
return resp.Parameter?.Value;
}
async function ensureKubeconfig(platformName, env, sessionName, deps = {}) {
const parameter = `/mesh-platform/${platformName}/${env}/core/eks`;
const getParameter = deps.getParameter ?? ssmGetParameter;
let cluster;
try {
const value = await getParameter(parameter);
if (!value) throw new Error("parameter is empty or missing");
const parsed = JSON.parse(value);
if (!parsed.clusterEndpoint || !parsed.clusterCaData || !parsed.clusterName) {
throw new Error("cluster data is missing clusterEndpoint/clusterCaData/clusterName");
}
cluster = {
clusterEndpoint: parsed.clusterEndpoint,
clusterCaData: parsed.clusterCaData,
clusterName: parsed.clusterName
};
} catch (error) {
deps.onError?.({ parameter, error });
return null;
}
const region = process.env.AWS_REGION || "us-east-2";
const kubeconfigPath = sessionKubeconfigPath(sessionName);
if (!fs12.existsSync(path12.dirname(kubeconfigPath))) {
fs12.mkdirSync(path12.dirname(kubeconfigPath), { recursive: true });
}
fs12.writeFileSync(kubeconfigPath, buildKubeconfig(cluster, region), { mode: 384 });
return kubeconfigPath;
}
var init_kubeconfig = __esm({
"libs/mesh-cli/src/utils/kubeconfig.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/temporal-auth.ts
import { execFileSync as execFileSync11 } from "node:child_process";
async function resolveTemporalAuth(tenant, env, platformName = tenant) {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
const results = {};
async function trySSM(name) {
try {
const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
return resp.Parameter?.Value ?? void 0;
} catch {
return void 0;
}
}
try {
const workerPath = `/mesh-platform/${tenant}/${env}/temporal/worker-service-account-key`;
const exchangePath = `/mesh-platform/${tenant}/${env}/temporal/token-exchange-app`;
const workerJson = await trySSM(workerPath);
if (workerJson) {
const worker = JSON.parse(workerJson);
results.TEMPORAL_AUTH_CLIENT_ID = worker.clientId;
results.TEMPORAL_AUTH_CLIENT_SECRET = worker.clientSecret;
}
const exchangeJson = await trySSM(exchangePath);
if (exchangeJson) {
const exchange = JSON.parse(exchangeJson);
results.ZITADEL_TOKEN_EXCHANGE_CLIENT_ID = exchange.clientId;
results.ZITADEL_TOKEN_EXCHANGE_CLIENT_SECRET = exchange.clientSecret;
}
const platformJson = await trySSM(`/mesh-platform/${platformName}/${env}/platform/zitadel`);
if (platformJson) {
const platform = JSON.parse(platformJson);
if (platform.endpoint) results.ZITADEL_ISSUER = platform.endpoint;
if (platform.projectId) results.ZITADEL_PROJECT_ID = platform.projectId;
}
if (!results.TEMPORAL_AUTH_CLIENT_ID) {
const legacyClientId = await trySSM("/platform/temporal/auth/CLIENT_ID");
const legacyClientSecret = await trySSM("/platform/temporal/auth/CLIENT_SECRET");
if (legacyClientId && legacyClientSecret) {
results.TEMPORAL_AUTH_CLIENT_ID = legacyClientId;
results.TEMPORAL_AUTH_CLIENT_SECRET = legacyClientSecret;
}
}
if (!results.ZITADEL_ISSUER) {
try {
const podEnv = execFileSync11(
"kubectl",
[
"get",
"pods",
"-n",
`temporal-${env}`,
"-l",
"app.kubernetes.io/component=frontend",
"-o",
"jsonpath={.items[0].spec.containers[0].env}"
],
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
).trim();
if (podEnv) {
const envVars = JSON.parse(podEnv);
for (const v of envVars) {
if (v.name === "ZITADEL_ISSUER_URL" && v.value && !results.ZITADEL_ISSUER) {
results.ZITADEL_ISSUER = v.value;
}
if (v.name === "ZITADEL_PROJECT_ID" && v.value && !results.ZITADEL_PROJECT_ID) {
results.ZITADEL_PROJECT_ID = v.value;
}
}
}
} catch (err) {
const detail = err instanceof Error ? err.message : String(err);
const stderr = String(err?.stderr ?? "");
if (/\.kube[/\\]config|error loading config file/i.test(`${detail} ${stderr}`)) {
logWarn(
"kubectl could not read a kubeconfig and fell back to ~/.kube/config (macOS blocks it for shells without Full Disk Access). Run inside the mesh dev tmux session, or export KUBECONFIG=$TMPDIR/mesh-dev-sessions/<session>.kubeconfig."
);
}
}
}
if (results.TEMPORAL_AUTH_CLIENT_ID && results.ZITADEL_ISSUER) {
logSuccess(`Temporal auth resolved (client: ${results.TEMPORAL_AUTH_CLIENT_ID})`);
} else if (Object.keys(results).length > 0) {
logWarn("Partial Temporal auth resolved (some credentials missing)");
}
} catch (err) {
logWarn(
`Could not resolve Temporal auth from SSM: ${err instanceof Error ? err.message : String(err)}`
);
}
return results;
}
async function acquireTemporalBearer(vars = {}) {
const issuerUrl = vars.ZITADEL_ISSUER ?? process.env.ZITADEL_ISSUER;
const projectId = vars.ZITADEL_PROJECT_ID ?? process.env.ZITADEL_PROJECT_ID;
const clientId = vars.TEMPORAL_AUTH_CLIENT_ID ?? process.env.TEMPORAL_AUTH_CLIENT_ID;
const clientSecret = vars.TEMPORAL_AUTH_CLIENT_SECRET ?? process.env.TEMPORAL_AUTH_CLIENT_SECRET;
if (!issuerUrl || !clientId || !clientSecret) {
return void 0;
}
const scopes = ["openid"];
if (projectId) {
scopes.push(`urn:zitadel:iam:org:project:id:${projectId}:aud`);
if (process.env.TEMPORAL_M2M_SKIP_ROLES_SCOPE !== "true") {
scopes.push("urn:zitadel:iam:org:projects:roles");
}
}
const response = await fetch(`${issuerUrl}/oauth/v2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
scope: scopes.join(" ")
})
});
if (!response.ok) {
const text = await response.text();
throw new Error(
`Temporal token acquisition failed: ${response.status} ${response.statusText} \u2014 ${text}`
);
}
const data = await response.json();
return data.access_token;
}
var init_temporal_auth = __esm({
"libs/mesh-cli/src/utils/temporal-auth.ts"() {
"use strict";
init_log();
}
});
// libs/mesh-cli/src/utils/reachability.ts
import { createConnection } from "node:net";
function probeTcpReachable(host, port, timeoutMs = 1500) {
return new Promise((resolve19) => {
let settled = false;
const socket = createConnection({ host, port });
const finish = (ok) => {
if (settled) return;
settled = true;
socket.destroy();
resolve19(ok);
};
socket.setTimeout(timeoutMs);
socket.once("connect", () => finish(true));
socket.once("timeout", () => finish(false));
socket.once("error", () => finish(false));
});
}
function probeConnectionHolds(host, port, timeoutMs = 800, holdMs = 25) {
return new Promise((resolve19) => {
let settled = false;
let holdTimer;
const socket = createConnection({ host, port });
const finish = (ok) => {
if (settled) return;
settled = true;
if (holdTimer) clearTimeout(holdTimer);
socket.destroy();
resolve19(ok);
};
socket.setTimeout(timeoutMs);
socket.once("connect", () => {
holdTimer = setTimeout(() => finish(true), holdMs);
});
socket.once("close", () => finish(false));
socket.once("timeout", () => finish(false));
socket.once("error", () => finish(false));
});
}
var init_reachability = __esm({
"libs/mesh-cli/src/utils/reachability.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/workflow-fingerprint.ts
import { createHash as createHash2 } from "node:crypto";
import { existsSync as existsSync11, readFileSync as readFileSync11, readdirSync as readdirSync4 } from "node:fs";
import { dirname as dirname11, isAbsolute as isAbsolute2, join as join11, relative as relative2 } from "node:path";
function findRepoRoot(startDir) {
let dir = startDir;
for (; ; ) {
if (existsSync11(join11(dir, "pnpm-workspace.yaml"))) return dir;
const parent = dirname11(dir);
if (parent === dir) return startDir;
dir = parent;
}
}
function workflowSourceDirs(repoRoot2, extraDirs = []) {
const pkgDirs = WORKFLOW_PACKAGE_DIRS.map((d) => join11(repoRoot2, d)).filter((d) => existsSync11(d));
if (pkgDirs.length > 0) return pkgDirs;
return extraDirs.filter((d) => existsSync11(d));
}
function resolveWorkerSourceDirs(repoRoot2, services = {}) {
const dirs = [];
for (const [name, svc] of Object.entries(services)) {
if (/worker/i.test(name) && svc?.src) {
dirs.push(isAbsolute2(svc.src) ? svc.src : join11(repoRoot2, svc.src));
}
}
return dirs;
}
function computeWorkflowFingerprint(dirs, relativeTo) {
const files = [];
for (const dir of dirs) {
let rels;
try {
rels = readdirSync4(dir, { recursive: true });
} catch {
continue;
}
for (const rel2 of rels) {
const p = String(rel2);
if (/\.(ts|tsx)$/.test(p) && !EXCLUDE.test(p)) files.push(join11(dir, p));
}
}
files.sort();
if (files.length === 0) return "";
const h = createHash2("sha256");
for (const f of files) {
try {
const content = readFileSync11(f);
h.update(relativeTo ? relative2(relativeTo, f) : f);
h.update("\0");
h.update(content);
h.update("\0");
} catch {
}
}
return h.digest("hex").slice(0, 16);
}
function fingerprintWorkflowSource(appRoot, services = {}) {
const repoRoot2 = findRepoRoot(appRoot);
const workerDirs = resolveWorkerSourceDirs(repoRoot2, services);
return computeWorkflowFingerprint(workflowSourceDirs(repoRoot2, workerDirs), repoRoot2);
}
var WORKFLOW_PACKAGE_DIRS, EXCLUDE;
var init_workflow_fingerprint = __esm({
"libs/mesh-cli/src/utils/workflow-fingerprint.ts"() {
"use strict";
WORKFLOW_PACKAGE_DIRS = [
"packages/agent-sdk/src",
"packages/workflow-interpreter/src",
"packages/agent-contracts/src"
];
EXCLUDE = /(^|\/)(node_modules|dist|__tests__)(\/|$)|\.(test|spec)\.[cm]?tsx?$/;
}
});
// libs/mesh-cli/src/commands/dev-launch.ts
import * as fs13 from "fs";
import * as net7 from "net";
import * as path13 from "path";
function quoteShellValue(value) {
return `'${value.replace(/'/g, "'\\''")}'`;
}
function renderEnvFile(env) {
const lines = [
"# Auto-generated by mesh dev \u2014 do not edit.",
"# Sourced by service launch/restart commands; this file IS the",
"# session's env contract for the service (restart fidelity)."
];
for (const [key, value] of Object.entries(env)) {
if (!ENV_KEY_RE.test(key)) {
throw new Error(`Invalid env var name for env file: ${JSON.stringify(key)}`);
}
lines.push(`export ${key}=${quoteShellValue(value)}`);
}
return lines.join("\n") + "\n";
}
function envFileName(serviceName) {
return `${serviceName.replace(/[^A-Za-z0-9._-]/g, "-")}.env.sh`;
}
function writeEnvFile(filePath, env) {
fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
fs13.writeFileSync(filePath, renderEnvFile(env), { mode: ENV_FILE_MODE });
fs13.chmodSync(filePath, ENV_FILE_MODE);
}
function buildLaunchCommand(envFilePath, dir, command, logShipper) {
const base = `cd ${quoteShellValue(dir)} && source ${quoteShellValue(envFilePath)} && `;
if (!logShipper) return `${base}${command}`;
return `${base}{ ${command}; } 2>&1 | NODE_OPTIONS= node ${quoteShellValue(logShipper)}`;
}
async function waitForPort(host, port, timeoutMs, intervalMs = 500) {
const deadline = Date.now() + timeoutMs;
for (; ; ) {
const remaining = deadline - Date.now();
const attemptTimeout = Math.max(250, Math.min(1e3, remaining));
if (await tryConnect(host, port, attemptTimeout)) return true;
if (Date.now() + intervalMs >= deadline) return false;
await new Promise((resolve19) => setTimeout(resolve19, intervalMs));
}
}
function tryConnect(host, port, timeoutMs) {
return new Promise((resolve19) => {
const socket = net7.connect({ host, port });
const done = (ok) => {
socket.removeAllListeners();
socket.destroy();
resolve19(ok);
};
socket.setTimeout(timeoutMs);
socket.once("connect", () => done(true));
socket.once("timeout", () => done(false));
socket.once("error", () => done(false));
});
}
var ENV_FILE_MODE, ENV_KEY_RE;
var init_dev_launch = __esm({
"libs/mesh-cli/src/commands/dev-launch.ts"() {
"use strict";
ENV_FILE_MODE = 384;
ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
}
});
// libs/mesh-cli/src/commands/local/mocks.ts
import { execFileSync as execFileSync12 } from "child_process";
import * as fs14 from "fs";
import * as path14 from "path";
function externalMode(name, decl) {
const mode = decl.mode ?? (decl.compose ? "local" : decl.openapi || decl.src ? "mock" : decl.sandbox || decl.credentials || decl.remote ? "remote" : void 0);
if (!mode || !EXTERNAL_MODES.includes(mode)) {
throw new MeshCliError(
`External '${name}': cannot determine mode \u2014 set "mode" to mock | local | remote (or declare openapi/src, compose, or credentials).`,
{ remediation: DECL_DOCS }
);
}
if (mode === "local") {
if (!decl.compose) {
throw new MeshCliError(
`External '${name}': local mode requires "compose" \u2014 the docker compose file running the local version of the service.`,
{ remediation: DECL_DOCS }
);
}
if (typeof decl.port !== "number") {
throw new MeshCliError(
`External '${name}': local (compose) mode requires "port" \u2014 the port the compose file publishes on localhost.`,
{ remediation: DECL_DOCS }
);
}
}
if (mode === "mock" && !decl.openapi && !decl.src) {
throw new MeshCliError(
`External '${name}': mock mode emulates the service \u2014 declare "openapi" (spec \u2192 Prism mock) or "src" (mock process).`,
{ remediation: DECL_DOCS }
);
}
if (mode === "remote" && !remoteInlineCredentials(decl) && !decl.external) {
throw new MeshCliError(
`External '${name}': remote mode needs a credential source \u2014 inline "credentials", "remote": { "env": \u2026 }, or "external" (pulls that environment's configured secret).`,
{ remediation: DECL_DOCS }
);
}
return mode;
}
function parseExternalsSelection(input2) {
const names = [];
const overrides = /* @__PURE__ */ new Map();
for (const entry of input2.split(",").map((s) => s.trim()).filter(Boolean)) {
const [rawName, mode, ...rest] = entry.split("=").map((s) => s.trim());
const name = rawName ?? "";
if (!name) {
throw new MeshCliError(
`Bad --externals entry '${entry}' \u2014 expected name or name=mode with mode \u2208 ${EXTERNAL_MODES.join(" | ")}.`,
{ remediation: { command: "mesh dev --externals plaid-db=remote,plaid" } }
);
}
if (mode !== void 0) {
if (rest.length > 0 || !EXTERNAL_MODES.includes(mode)) {
throw new MeshCliError(
`Bad --externals entry '${entry}' \u2014 expected name or name=mode with mode \u2208 ${EXTERNAL_MODES.join(" | ")}.`,
{ remediation: { command: "mesh dev --externals plaid-db=remote,plaid" } }
);
}
overrides.set(name, mode);
}
if (!names.includes(name)) names.push(name);
}
return { names, overrides };
}
function remoteInlineCredentials(decl) {
if (decl.remote?.credentials) return decl.remote.credentials;
if (decl.credentials && !decl.compose && !decl.openapi && !decl.src) return decl.credentials;
return void 0;
}
function isServiceMode(name, decl) {
return externalMode(name, decl) === "mock";
}
function interpolateCredentialValue(raw, endpoint, env = process.env) {
let value = raw.replace(/\{\{env:([A-Za-z_][A-Za-z0-9_]*)\}\}/g, (_, name) => {
const resolved = env[name];
if (resolved === void 0) {
throw new MeshCliError(
`Credential value references {{env:${name}}} but ${name} is not set in your environment.`,
{ remediation: { command: `export ${name}=\u2026 # then re-run mesh dev` } }
);
}
return resolved;
});
if (endpoint) {
value = value.replaceAll("{{url}}", endpoint.url).replaceAll("{{host}}", endpoint.host).replaceAll("{{port}}", String(endpoint.port));
}
return value;
}
function openapiMockCommand(decl) {
return decl.command ?? [
"npx",
"-y",
"@stoplight/prism-cli@5",
"mock",
"-p",
"$PORT",
"-h",
"0.0.0.0",
decl.openapi
];
}
function composeProjectName(sessionName, mockName) {
return `mesh-ext-${sessionName}-${mockName}`.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
}
async function waitForPort2(port, timeoutMs) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (await probeTcp(port, { timeoutMs: 2e3 })) return true;
await new Promise((r) => setTimeout(r, 2e3));
}
return false;
}
async function planComposeExternal(composeFile, project, port, probes) {
if (probes.ownsRunning(composeFile, project)) return { action: "up" };
if (await probes.portServed(port)) {
const publisher = probes.portPublisher(composeFile, port);
if (!publisher || !publisher.definedByFile) {
return { action: "conflict", ...publisher ? { container: publisher.container } : {} };
}
return { action: "adopt-served", container: publisher.container };
}
const foreign = probes.foreignPinned(composeFile, project);
if (foreign) return { action: "adopt-stopped", container: foreign };
return { action: "up" };
}
async function composeExternalUp(appRoot, sessionName, name, decl, probes = dockerProbes) {
const composeFile = path14.resolve(appRoot, decl.compose);
if (!fs14.existsSync(composeFile)) {
throw new MeshCliError(`External '${name}': compose file not found at ${composeFile}.`, {
remediation: { docs: 'package.json \u2192 "mesh": { "externals": { \u2026 } }' }
});
}
const project = composeProjectName(sessionName, name);
const plan = await planComposeExternal(composeFile, project, decl.port, probes);
if (plan.action === "conflict") {
throw new MeshCliError(
`External '${name}': localhost:${decl.port} is already served by ${plan.container ? `container '${plan.container}'` : "a process outside docker"}, which ${decl.compose} does not define \u2014 refusing to seed '${name}' credentials against it.`,
{
remediation: {
command: `docker ps --filter publish=${decl.port} # stop it, or change the declared port`
}
}
);
}
if (plan.action === "adopt-served") {
logInfo(
`External '${name}' already served on localhost:${decl.port} by container '${plan.container}' \u2014 adopting it (started outside this checkout; \`mesh dev --kill\` won't touch it).`
);
return void 0;
}
if (plan.action === "adopt-stopped") {
const { container } = plan;
logInfo(`External '${name}': container '${container}' exists from another checkout \u2014 starting and adopting it.`);
execFileSync12("docker", ["start", container], { stdio: ["ignore", "ignore", "inherit"] });
if (!await waitForPort2(decl.port, 3e5)) {
throw new MeshCliError(
`External '${name}': adopted container '${container}' never served localhost:${decl.port}.`,
{ remediation: { command: `docker logs ${container} # then: docker rm -f ${container} and re-run` } }
);
}
return void 0;
}
execFileSync12(
"docker",
["compose", "-p", project, "-f", composeFile, "up", "-d", "--wait", "--wait-timeout", "300"],
{ stdio: ["ignore", "inherit", "inherit"] }
);
return { name, composeFile, project };
}
function ownsRunningRealization(composeFile, project) {
try {
const out = execFileSync12(
"docker",
["compose", "-p", project, "-f", composeFile, "ps", "--format", "json"],
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
).trim();
return out.length > 0 && out !== "[]";
} catch {
return false;
}
}
function composeServices(composeFile) {
try {
const config = JSON.parse(
execFileSync12("docker", ["compose", "-f", composeFile, "config", "--format", "json"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
})
);
const services = config?.services;
return services && typeof services === "object" ? services : void 0;
} catch {
return void 0;
}
}
function portPublisher(port) {
try {
const out = execFileSync12(
"docker",
[
"ps",
"--filter",
`publish=${port}`,
"--format",
'{{.Names}} {{.Image}} {{.Label "com.docker.compose.project"}} {{.Label "com.docker.compose.service"}}'
],
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
);
const line = out.split("\n").map((l) => l.trim()).filter(Boolean)[0];
if (!line) return void 0;
const [container, image, project, service] = line.split(" ");
if (!container) return void 0;
return {
container,
image: image || void 0,
project: project || void 0,
service: service || void 0
};
} catch {
return void 0;
}
}
function composeFileOwns(composeFile, publisher) {
if (!publisher) return false;
const services = composeServices(composeFile);
if (!services) return false;
for (const [key, svc] of Object.entries(services)) {
if (svc?.container_name && svc.container_name === publisher.container) return true;
if (publisher.service && publisher.service === key) {
if (!svc?.image || !publisher.image || svc.image === publisher.image) return true;
}
}
return false;
}
function composeFilePortPublisher(composeFile, port) {
const publisher = portPublisher(port);
if (!publisher) return void 0;
return { container: publisher.container, definedByFile: composeFileOwns(composeFile, publisher) };
}
function foreignPinnedContainer(composeFile, project) {
for (const svc of Object.values(composeServices(composeFile) ?? {})) {
const pinned = svc?.container_name;
if (!pinned) continue;
try {
const owner = execFileSync12(
"docker",
["inspect", pinned, "--format", '{{ index .Config.Labels "com.docker.compose.project" }}'],
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
).trim();
if (owner !== project) return pinned;
} catch {
}
}
return void 0;
}
function composeExternalsDown(refs) {
for (const ref of refs ?? []) {
try {
execFileSync12("docker", ["compose", "-p", ref.project, "-f", ref.composeFile, "down", "--remove-orphans"], {
stdio: ["ignore", "inherit", "inherit"]
});
} catch {
}
}
}
function readLocalMocks(appRoot) {
try {
const pkg = JSON.parse(fs14.readFileSync(path14.join(appRoot, "package.json"), "utf-8"));
const mocks = pkg?.mesh?.mocks;
const externals = pkg?.mesh?.externals;
return {
...mocks && typeof mocks === "object" ? mocks : {},
...externals && typeof externals === "object" ? externals : {}
};
} catch {
return {};
}
}
function externalSecretPath(tenant, external) {
return `mesh/${tenant}/${LOCAL_ENV}/external/${external}`;
}
async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
const secretId = `mesh/${tenant}/${env}/external/${external}`;
const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE ? process.env.MESH_AWS_PROFILE ?? profile : void 0;
try {
const { SecretsManagerClient: SecretsManagerClient10, GetSecretValueCommand: GetSecretValueCommand10 } = await import("@aws-sdk/client-secrets-manager");
if (fallbackProfile) process.env.AWS_PROFILE = fallbackProfile;
const sm = new SecretsManagerClient10({ region });
let res;
try {
res = await sm.send(new GetSecretValueCommand10({ SecretId: secretId }));
} finally {
if (fallbackProfile) delete process.env.AWS_PROFILE;
}
const parsed = JSON.parse(res.SecretString ?? "{}");
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("secret value is not a JSON object");
}
return Object.fromEntries(Object.entries(parsed).map(([key, v]) => [key, String(v)]));
} catch (err) {
throw new MeshCliError(
`External '${external}': could not pull remote credentials from the ${env} environment (${secretId}, region ${region}): ${err instanceof Error ? err.message : err}`,
{
remediation: {
command: `AWS_PROFILE=<${tenant}-${env} profile> mesh dev \u2026 # or set "remote": { "profile": \u2026 } on the declaration / MESH_AWS_PROFILE`
}
}
);
}
}
function hostForProber(target) {
return target.replace(/\b(?:localhost|127\.0\.0\.1)\b/, "host.docker.internal");
}
function httpProbeUrl(raw) {
if (!raw) return void 0;
let parsed;
try {
parsed = new URL(raw);
} catch {
return void 0;
}
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
parsed.username = "";
parsed.password = "";
return hostForProber(parsed.toString());
}
function derivedTcpTarget(creds) {
if (creds.host && creds.port) return `${hostForProber(creds.host)}:${creds.port}`;
if (creds.bucket) return `${creds.bucket}.s3.${creds.region || "us-east-1"}.amazonaws.com:443`;
return void 0;
}
function resolveProbeTarget(mode, decl, endpoint, creds) {
if (mode === "remote") {
const url = httpProbeUrl(creds.endpoint ?? creds.url ?? creds.baseUrl);
const tcpTarget = derivedTcpTarget(creds);
if (decl.probe === "tcp" || !url && tcpTarget) {
if (!tcpTarget && decl.probe === "tcp") {
logWarn(
`External '${decl.external ?? ""}': probe: "tcp" declared but the remote credentials carry no host/port (or bucket) to probe \u2014 no uptime probe registered.`
);
}
return tcpTarget ? { target: tcpTarget, module: "tcp_connect" } : void 0;
}
return url ? { target: url.replace(/\/+$/, "") + (decl.healthPath ?? "") } : void 0;
}
if (!endpoint) return void 0;
if (decl.probe === "tcp") {
return { target: `host.docker.internal:${endpoint.port}`, module: "tcp_connect" };
}
return {
target: hostForProber(endpoint.url).replace(/\/+$/, "") + (decl.healthPath ?? "/health")
};
}
function externalDisplayName(name, decl) {
return decl.displayName ?? name.split(/[-_\s]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
}
function externalDocs(decl) {
if (!decl.docs) return void 0;
return typeof decl.docs === "string" ? { url: decl.docs } : decl.docs;
}
function localProbeFile(tenant, app, external) {
return path14.join(localProbesDir(), `${tenant}-${app}-${external}.json`);
}
function localProbesRemove(files) {
for (const file of files ?? []) {
try {
fs14.rmSync(file, { force: true });
} catch {
}
}
}
async function seedLocalMock(args) {
const { tenant, app, name, decl, endpoint } = args;
if (!decl.external) return void 0;
const mode = externalMode(name, decl);
let value;
if (mode === "remote") {
const inline = remoteInlineCredentials(decl);
value = inline ? Object.fromEntries(
Object.entries(inline).map(([key, raw]) => [
key,
interpolateCredentialValue(raw, void 0)
])
) : await fetchRemoteExternalCredentials(
tenant,
decl.remote?.env ?? "dev",
decl.external,
decl.remote?.profile
);
} else {
if (!decl.credentials) return void 0;
value = Object.fromEntries(
Object.entries(decl.credentials).map(([key, raw]) => [
key,
interpolateCredentialValue(raw, endpoint)
])
);
}
const secretName = externalSecretPath(tenant, decl.external);
await upsertLocalSecret(secretName, value);
await upsertLocalSecret(
`${secretName}/.config`,
Object.fromEntries(Object.entries(value).filter(([key]) => !SECRET_KEY_RE.test(key)))
);
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/external-services/${decl.external}`;
await ssm.send(
new PutParameterCommand({
Name: `${base}/meta`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({
name: decl.external,
// The integration keeps its REAL name in the Hub ("Plaid", not a
// generated phrase); the description leads with it too, so display
// heuristics that promote description heads still land on the name.
displayName: externalDisplayName(name, decl),
type: decl.type ?? "other",
description: mode === "local" ? `${externalDisplayName(name, decl)} \u2014 local replica via mesh dev (docker compose)` : mode === "remote" ? remoteInlineCredentials(decl) ? `${externalDisplayName(name, decl)} \u2014 remote service (vendor credentials)` : `${externalDisplayName(name, decl)} \u2014 remote service (${decl.remote?.env ?? "dev"} environment credentials)` : `${externalDisplayName(name, decl)} \u2014 emulated by mesh dev (mock)`,
secretPrefix: secretName,
...externalDocs(decl) ? { docs: externalDocs(decl) } : {}
}),
Description: `External service registration (local mock ${name})`
})
);
await ssm.send(
new PutParameterCommand({
Name: `${base}/credentials`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({
fields: Object.fromEntries(
Object.keys(value).map((key) => [
key,
{ type: "field", name: key, description: "", secret: SECRET_KEY_RE.test(key), optional: false }
])
),
keyedBy: null
}),
Description: `External service credential schema (local mock ${name})`
})
);
const probe = resolveProbeTarget(mode, decl, endpoint, value);
await ssm.send(
new PutParameterCommand({
Name: `${base}/healthCheck`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ intervalSeconds: 30, timeoutSeconds: 10, hasCustomCheck: false, hasProbe: !!probe }),
Description: `External service health check config (local mock ${name})`
})
);
const probeFile = localProbeFile(tenant, app, decl.external);
if (probe) {
fs14.writeFileSync(
probeFile,
JSON.stringify(
[
{
targets: [probe.target],
labels: {
type: "external-service",
tenant,
env: LOCAL_ENV,
app,
external_service: decl.external,
target: decl.external,
// "__"-prefixed labels drop after relabeling, so the module
// selector never pollutes the stored series.
...probe.module ? { __probe_module: probe.module } : {}
}
}
],
null,
2
)
);
} else {
localProbesRemove([probeFile]);
}
const endpointShown = endpoint?.url ?? value.endpoint ?? value.url ?? value.baseUrl ?? (value.host ? `${value.host}${value.port ? `:${value.port}` : ""}` : "credentials");
logSuccess(
`External '${name}' wired (${mode}): ${secretName} \u2192 ${endpointShown} (Hub registration${probe ? " + uptime probe" : ""})`
);
return probe ? probeFile : void 0;
}
var EXTERNAL_MODES, DECL_DOCS, dockerProbes, SECRET_KEY_RE;
var init_mocks = __esm({
"libs/mesh-cli/src/commands/local/mocks.ts"() {
"use strict";
init_log();
init_errors();
init_seed();
init_helpers();
init_stack();
EXTERNAL_MODES = ["mock", "local", "remote"];
DECL_DOCS = { docs: 'package.json \u2192 "mesh": { "externals": { \u2026 } }' };
dockerProbes = {
ownsRunning: ownsRunningRealization,
portServed: (port) => probeTcp(port, { timeoutMs: 2e3 }),
foreignPinned: foreignPinnedContainer,
portPublisher: composeFilePortPublisher
};
SECRET_KEY_RE = /key|secret|password|token/i;
}
});
// libs/api-registry/src/hub-roles.ts
function restrictedRoleBase(roleKey) {
const sep7 = roleKey.indexOf(HUB_ROLE_KEY_SEPARATOR);
const base = sep7 === -1 ? roleKey : roleKey.slice(0, sep7);
return RESTRICTED.has(base) ? base : null;
}
function hubRoleKeyTenant(roleKey) {
const sep7 = roleKey.indexOf(HUB_ROLE_KEY_SEPARATOR);
if (sep7 === -1) return null;
const scope = roleKey.slice(sep7 + 1);
const appSep = scope.indexOf(HUB_ROLE_APP_SEPARATOR);
return appSep === -1 ? scope : scope.slice(0, appSep);
}
function hubOperatorRoleKeys(tenants) {
const cleaned = [...new Set(tenants.filter((t) => t !== ""))].sort();
for (const tenant of cleaned) {
for (const sep7 of [HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR]) {
if (tenant.includes(sep7)) {
throw new Error(
`hubOperatorRoleKeys: tenant name "${tenant}" contains the role-key separator "${sep7}" \u2014 it would parse as a different tenant's or app's grant`
);
}
}
}
return [
...HUB_BASE_ROLES,
...cleaned.flatMap(
(tenant) => HUB_RESTRICTED_ROLES.map((role) => `${role}${HUB_ROLE_KEY_SEPARATOR}${tenant}`)
)
];
}
var HUB_STAFF_ROLES, HUB_RESTRICTED_ROLES, HUB_BASE_ROLES, HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR, RESTRICTED;
var init_hub_roles = __esm({
"libs/api-registry/src/hub-roles.ts"() {
"use strict";
HUB_STAFF_ROLES = ["ops", "admin"];
HUB_RESTRICTED_ROLES = ["developer", "auditor", "vendor"];
HUB_BASE_ROLES = [
...HUB_STAFF_ROLES,
...HUB_RESTRICTED_ROLES
];
HUB_ROLE_KEY_SEPARATOR = ":";
HUB_ROLE_APP_SEPARATOR = "/";
RESTRICTED = new Set(HUB_RESTRICTED_ROLES);
}
});
// libs/api-registry/src/index.ts
import { z } from "zod";
var rateLimitSpecSchema, rateLimitDefaultsSchema, integrationHealthSchema, apiSurfaceKindSchema, apiSurfaceSchema, apiRegistryEntrySchema, integrationStatusSchema;
var init_src = __esm({
"libs/api-registry/src/index.ts"() {
"use strict";
init_hub_roles();
rateLimitSpecSchema = z.object({
rps: z.number().int().min(1),
burst: z.number().int().min(1)
});
rateLimitDefaultsSchema = z.record(z.string().min(1), rateLimitSpecSchema);
integrationHealthSchema = z.union([
z.object({ op: z.string().trim().min(1) }),
z.object({ unavailable: z.string().trim().min(1) })
]);
apiSurfaceKindSchema = z.enum(["app", "vendor"]);
apiSurfaceSchema = z.object({
http: z.object({ url: z.string().min(1) }).optional(),
nexus: z.object({ endpoint: z.string().min(1), taskQueue: z.string().min(1) }).optional()
});
apiRegistryEntrySchema = z.object({
schemaVersion: z.number().int().positive().default(1),
name: z.string().min(1),
kind: apiSurfaceKindSchema.optional(),
provider: z.string().optional(),
version: z.string().optional(),
title: z.string().optional(),
description: z.string().optional(),
surfaces: apiSurfaceSchema,
credentials: z.object({ keyedBy: z.string().nullish() }).optional(),
docs: z.object({
url: z.string().min(1),
contentHash: z.string().min(1),
siteUrl: z.string().min(1).optional(),
siteInternalUrl: z.string().min(1).optional()
}),
enabledOps: z.array(z.string()),
// The consumer schema strips unknown keys, so a producer-side field that is
// not mirrored here never reaches a consumer — mirror every addition.
status: z.object({ url: z.string().min(1) }).optional(),
appVersion: z.string().min(1).optional(),
rateLimits: rateLimitDefaultsSchema.optional(),
health: integrationHealthSchema.optional(),
producedRateClasses: z.array(z.string().min(1)).optional()
});
integrationStatusSchema = z.object({
contract: z.literal("v1"),
name: z.string().min(1),
provider: z.string().optional(),
type: z.string().optional(),
title: z.string().optional(),
definitionVersion: z.string().optional(),
packageVersion: z.string().min(1).optional(),
tenant: z.string().min(1),
instanceKey: z.string().optional(),
surfaces: z.array(z.enum(["http", "nexus"])),
coreMode: z.enum(["mock", "live"]),
coreModeSource: z.string().optional(),
enabledOps: z.array(z.string()),
credentialsWired: z.boolean(),
rateLimits: rateLimitDefaultsSchema.optional(),
health: integrationHealthSchema.optional(),
startedAt: z.string().min(1)
});
}
});
// libs/mesh-cli/src/commands/local/seed-zitadel.ts
import * as fs15 from "fs";
import * as os5 from "os";
import * as path15 from "path";
function buildCliAppPayload() {
return {
name: CLI_APP_NAME,
redirectUris: [CLI_REDIRECT_URI],
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_DEVICE_CODE"],
appType: "OIDC_APP_TYPE_NATIVE",
authMethodType: "OIDC_AUTH_METHOD_TYPE_NONE",
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
accessTokenRoleAssertion: true,
idTokenRoleAssertion: true,
idTokenUserinfoAssertion: true,
devMode: true
};
}
function readSeederPat() {
const tmp = path15.join(fs15.mkdtempSync(path15.join(os5.tmpdir(), "mesh-zitadel-")), "pat.txt");
try {
compose(["cp", "zitadel:/machinekey/pat.txt", tmp]);
const pat = fs15.readFileSync(tmp, "utf-8").trim();
if (!pat) throw new Error("empty PAT file");
return pat;
} catch {
throw new MeshCliError(
"Zitadel seeder PAT not found \u2014 the zitadel volume predates the bootstrap machine user (first-instance settings only apply on the first init).",
{ remediation: { command: "mesh stop --destroy && mesh start" } }
);
} finally {
fs15.rmSync(path15.dirname(tmp), { recursive: true, force: true });
}
}
async function api(pat, method, apiPath, body, orgId) {
const res = await fetch(`${ZITADEL_ISSUER}${apiPath}`, {
method,
headers: {
Authorization: `Bearer ${pat}`,
"Content-Type": "application/json",
...orgId ? { "x-zitadel-orgid": orgId } : {}
},
body: body === void 0 ? void 0 : JSON.stringify(body),
signal: AbortSignal.timeout(15e3)
});
const text = await res.text();
const data = text ? JSON.parse(text) : {};
if (!res.ok) {
const err = new Error(
`Zitadel ${method} ${apiPath} \u2192 ${res.status}: ${data?.message ?? text}`
);
err.status = res.status;
err.zitadelCode = data?.code;
throw err;
}
return data;
}
async function ensureZitadelProject(pat, name, opts = {}) {
const search = await api(
pat,
"POST",
"/management/v1/projects/_search",
{ queries: [{ nameQuery: { name, method: "TEXT_QUERY_METHOD_EQUALS" } }] },
opts.orgId
);
const existing = search?.result?.[0]?.id;
if (existing) {
if (opts.logExisting) logInfo(`Zitadel project '${name}' already exists`);
return existing;
}
const created = await api(
pat,
"POST",
"/management/v1/projects",
{ name, projectRoleAssertion: true },
opts.orgId
);
logSuccess(`Created Zitadel project '${name}'${opts.describe ? ` (${opts.describe})` : ""}`);
return created.id;
}
async function searchZitadelApp(pat, projectId, name, orgId) {
const search = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/_search`,
{ queries: [{ nameQuery: { name, method: "TEXT_QUERY_METHOD_EQUALS" } }] },
orgId
);
return search?.result?.[0];
}
async function ensureCliApp(pat, projectId) {
const existing = (await searchZitadelApp(pat, projectId, CLI_APP_NAME))?.oidcConfig?.clientId;
if (existing) {
logInfo(`Zitadel app '${CLI_APP_NAME}' already exists`);
return existing;
}
const created = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/oidc`,
buildCliAppPayload()
);
logSuccess(`Created Zitadel application '${CLI_APP_NAME}' (PKCE + device code)`);
return created.clientId;
}
async function ensureTestUsers(pat) {
for (const user of TEST_USERS) {
try {
await api(pat, "POST", "/management/v1/users/human/_import", {
userName: user.email,
profile: {
firstName: user.firstName,
lastName: user.lastName,
displayName: `${user.firstName} ${user.lastName}`
},
email: { email: user.email, isEmailVerified: true },
password: user.password,
passwordChangeRequired: false
});
logSuccess(`Created test user ${user.email}`);
} catch (err) {
if (isAlreadyExists(err)) {
logInfo(`Test user ${user.email} already exists`);
} else {
throw err;
}
}
}
}
function hubAuthPath() {
return path15.join(meshCacheDir("mesh-local"), "hub-auth.json");
}
function readHubAuth() {
try {
const parsed = JSON.parse(fs15.readFileSync(hubAuthPath(), "utf-8"));
if (parsed?.clientId && parsed?.clientSecret && parsed?.cookieSecret) return parsed;
return null;
} catch {
return null;
}
}
function hubRoleDisplayName(roleKey) {
const tenant = hubRoleKeyTenant(roleKey);
return tenant === null ? roleKey : `${restrictedRoleBase(roleKey) ?? roleKey} (${tenant})`;
}
async function ensureHubRoles(pat, projectId) {
for (const role of HUB_ROLES) {
try {
await api(pat, "POST", `/management/v1/projects/${projectId}/roles`, {
roleKey: role,
displayName: hubRoleDisplayName(role)
});
logSuccess(`Created Hub role '${role}'`);
} catch (err) {
if (!isAlreadyExists(err)) throw err;
}
}
}
async function reconcileHubRedirectUris(pat, projectId, app) {
const cfg = app?.oidcConfig ?? {};
const redirectUris = [
.../* @__PURE__ */ new Set([...cfg.redirectUris ?? [], hubRedirectUri(), HUB_DEFAULT_REDIRECT_URI])
];
const postLogoutRedirectUris = [
.../* @__PURE__ */ new Set([
...cfg.postLogoutRedirectUris ?? [],
`http://localhost:${hubPort()}`,
`http://localhost:${hubPort()}/`,
`http://localhost:${DEFAULT_HUB_PORT}`,
`http://localhost:${DEFAULT_HUB_PORT}/`
])
];
if (redirectUris.length === (cfg.redirectUris ?? []).length && postLogoutRedirectUris.length === (cfg.postLogoutRedirectUris ?? []).length) {
return;
}
await api(pat, "PUT", `/management/v1/projects/${projectId}/apps/${app.id}/oidc_config`, {
redirectUris,
postLogoutRedirectUris,
responseTypes: cfg.responseTypes ?? ["OIDC_RESPONSE_TYPE_CODE"],
grantTypes: cfg.grantTypes ?? [
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE",
"OIDC_GRANT_TYPE_REFRESH_TOKEN"
],
appType: "OIDC_APP_TYPE_WEB",
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
accessTokenType: cfg.accessTokenType ?? "OIDC_TOKEN_TYPE_JWT",
accessTokenRoleAssertion: cfg.accessTokenRoleAssertion ?? true,
idTokenRoleAssertion: cfg.idTokenRoleAssertion ?? true,
idTokenUserinfoAssertion: cfg.idTokenUserinfoAssertion ?? true,
devMode: cfg.devMode ?? true
});
logSuccess(
`Registered Hub redirect URI http://localhost:${hubPort()}/oauth2/callback (MESH_HUB_PORT)`
);
}
async function ensureHubApp(pat, projectId) {
const existing = await searchZitadelApp(pat, projectId, HUB_APP_NAME);
if (existing) {
await reconcileHubRedirectUris(pat, projectId, existing);
const clientId = existing?.oidcConfig?.clientId;
const persisted = readHubAuth();
if (persisted && persisted.clientId === clientId) return { clientId };
const regenerated = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/${existing.id}/oidc_config/_generate_client_secret`,
{}
);
logSuccess(`Regenerated Hub UI client secret (local copy was missing)`);
return { clientId, clientSecret: regenerated.clientSecret };
}
const created = await api(pat, "POST", `/management/v1/projects/${projectId}/apps/oidc`, {
name: HUB_APP_NAME,
redirectUris: [.../* @__PURE__ */ new Set([hubRedirectUri(), HUB_DEFAULT_REDIRECT_URI])],
postLogoutRedirectUris: [
// Both slash forms — see reconcileHubRedirectUris; Zitadel exact-matches
// and the Hub's /logout sends the trailing-slash form.
.../* @__PURE__ */ new Set([
`http://localhost:${hubPort()}`,
`http://localhost:${hubPort()}/`,
`http://localhost:${DEFAULT_HUB_PORT}`,
`http://localhost:${DEFAULT_HUB_PORT}/`
])
],
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"],
appType: "OIDC_APP_TYPE_WEB",
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
accessTokenRoleAssertion: true,
idTokenRoleAssertion: true,
idTokenUserinfoAssertion: true,
devMode: true
});
logSuccess(`Created Zitadel application '${HUB_APP_NAME}' (Hub UI web client, code flow)`);
return { clientId: created.clientId, clientSecret: created.clientSecret };
}
async function ensureHubGrants(pat, projectId) {
const grants = [
{ email: "admin@local.mesh", roles: ["ops"] },
{ email: "dev@local.mesh", roles: ["ops"] },
{ email: "ops@local.mesh", roles: ["developer"] }
];
for (const grant of grants) {
const search = await api(pat, "POST", "/management/v1/users/_search", {
queries: [{ userNameQuery: { userName: grant.email, method: "TEXT_QUERY_METHOD_EQUALS" } }]
});
const userId = search?.result?.[0]?.id;
if (!userId) continue;
try {
await api(pat, "POST", `/management/v1/users/${userId}/grants`, {
projectId,
roleKeys: grant.roles
});
logSuccess(`Granted Hub role(s) ${grant.roles.join(", ")} \u2192 ${grant.email}`);
} catch (err) {
if (!isAlreadyExists(err)) throw err;
}
}
}
async function seedHubAuth(pat) {
const projectId = await ensureHubProject(pat);
await ensureHubRoles(pat, projectId);
const app = await ensureHubApp(pat, projectId);
await ensureHubGrants(pat, projectId);
const persisted = readHubAuth();
const persistedCookie = persisted?.cookieSecret && [16, 24, 32].includes(persisted.cookieSecret.length) ? persisted.cookieSecret : void 0;
const config = {
clientId: app.clientId,
projectId,
clientSecret: app.clientSecret ?? persisted?.clientSecret ?? "",
cookieSecret: persistedCookie ?? (await import("crypto")).randomBytes(16).toString("hex")
};
fs15.writeFileSync(hubAuthPath(), JSON.stringify(config, null, 2), { mode: 384 });
return config;
}
async function publishHubAuthzPointer(projectId, orgId, awsConfig) {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(awsConfig);
const name = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/apps/hub/stacks/local/authz`;
let current = {};
try {
const existing = await ssm.send(new GetParameterCommand2({ Name: name }));
current = JSON.parse(existing.Parameter?.Value ?? "{}");
} catch {
}
const value = {
spicedb: { instanceRefs: [] },
...current,
zitadel: { projectId, orgId, issuer: ZITADEL_ISSUER }
};
await ssm.send(
new PutParameterCommand({
Name: name,
Type: "String",
Overwrite: true,
Value: JSON.stringify(value),
Description: "Hub authz pointer (local analog of the platform Pulumi program)"
})
);
logSuccess(`Hub authz pointer published \u2192 project ${projectId} in org ${orgId}`);
}
async function writeRegistryParams(cliClientId, awsConfig) {
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(awsConfig);
await ssm.send(
new PutParameterCommand({
Name: ZITADEL_SSM_PARAM,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ endpoint: ZITADEL_ISSUER, cliClientId }),
Description: "Zitadel platform export (seeded by mesh start)"
})
);
for (const user of TEST_USERS) {
await ssm.send(
new PutParameterCommand({
Name: `${TEST_USERS_SSM_PREFIX}/${user.name}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ email: user.email, password: user.password, roles: [] }),
Description: `Local test user '${user.name}' (dev-grade, seeded by mesh start)`
})
);
}
}
async function ensureOpsHubAdmin(pat, awsConfig) {
const {
SecretsManagerClient: SecretsManagerClient10,
GetSecretValueCommand: GetSecretValueCommand10,
CreateSecretCommand: CreateSecretCommand4,
PutSecretValueCommand: PutSecretValueCommand4
} = await import("@aws-sdk/client-secrets-manager");
const sm = new SecretsManagerClient10(awsConfig);
const existing = await sm.send(new GetSecretValueCommand10({ SecretId: OPS_HUB_SECRET_ID })).catch(() => null);
if (existing?.SecretString) {
logSuccess("Hub admin key already provisioned (Zitadel writes enabled)");
return;
}
const userName = "hub-opshub";
const found = await api(pat, "POST", "/management/v1/users/_search", {
queries: [{ userNameQuery: { userName, method: "TEXT_QUERY_METHOD_EQUALS" } }]
});
let userId = found?.result?.[0]?.id;
if (!userId) {
const created = await api(pat, "POST", "/management/v1/users/machine", {
userName,
name: "Hub Ops Hub admin",
description: "Local Hub Zitadel admin plane (seeded by mesh start)"
});
userId = created?.userId;
}
if (!userId) throw new MeshCliError("could not create the Hub's Zitadel admin user");
await api(pat, "POST", "/admin/v1/members", { userId, roles: ["IAM_OWNER"] }).catch(
() => void 0
);
const key = await api(pat, "POST", `/management/v1/users/${userId}/keys`, {
type: "KEY_TYPE_JSON"
});
if (!key?.keyDetails)
throw new MeshCliError("Zitadel did not return a machine key for the Hub admin user");
const raw = JSON.parse(Buffer.from(key.keyDetails, "base64").toString("utf8"));
const secretString = JSON.stringify({ keyId: raw.keyId, key: raw.key, userId: raw.userId });
await sm.send(new CreateSecretCommand4({ Name: OPS_HUB_SECRET_ID, SecretString: secretString })).catch(async () => {
await sm.send(
new PutSecretValueCommand4({ SecretId: OPS_HUB_SECRET_ID, SecretString: secretString })
);
});
logSuccess(`Hub admin key provisioned \u2192 ${OPS_HUB_SECRET_ID} (Zitadel writes enabled)`);
}
async function ensureLocalSmtp(pat) {
try {
const existing = await api(pat, "POST", "/admin/v1/smtp/_search", {}).catch(() => null);
const configs = existing?.result ?? [];
const match = configs.find((c) => c.host === LOCAL_SMTP.host);
if (!match) {
const created = await api(pat, "POST", "/admin/v1/smtp", {
senderAddress: LOCAL_SMTP.from,
senderName: LOCAL_SMTP.fromName,
tls: false,
host: LOCAL_SMTP.host,
user: "",
password: ""
});
if (created?.id) await api(pat, "POST", `/admin/v1/smtp/${created.id}/_activate`, {});
logSuccess(
`Local mailbox wired to Zitadel \u2192 ${LOCAL_SMTP.host} (view at http://localhost:8025)`
);
return;
}
if (match.state !== "SMTP_CONFIG_ACTIVE" && match.id) {
await api(pat, "POST", `/admin/v1/smtp/${match.id}/_activate`, {});
}
} catch (err) {
logWarn(
`Could not wire the local mailbox to Zitadel (${err.message}). Activation and password-reset mail will not be delivered locally.`
);
}
}
async function seedZitadel(awsConfig) {
const pat = readSeederPat();
const org = await api(pat, "POST", "/admin/v1/orgs/_search", {
queries: [{ nameQuery: { name: PLATFORM_ORG, method: "TEXT_QUERY_METHOD_EQUALS" } }]
});
if (!org?.result?.[0]) {
throw new MeshCliError(
`Platform org '${PLATFORM_ORG}' not found \u2014 the zitadel volume predates the org naming.`,
{ remediation: { command: "mesh stop --destroy && mesh start" } }
);
}
logSuccess(
`Platform org '${PLATFORM_ORG}' ready (platform tenant root \u2014 holds platform-service auth config)`
);
try {
await api(pat, "PUT", "/v2/features/instance", { loginV2: { required: false } });
} catch {
}
await ensureLocalSmtp(pat);
const projectId = await ensureCliProject(pat);
const cliClientId = await ensureCliApp(pat, projectId);
await ensureTestUsers(pat);
const hubAuth = await seedHubAuth(pat);
await publishHubAuthzPointer(hubAuth.projectId, org.result[0].id, awsConfig);
await writeRegistryParams(cliClientId, awsConfig);
await ensureOpsHubAdmin(pat, awsConfig);
writeContextConfig(LOGIN_CONTEXT, { issuer: ZITADEL_ISSUER, clientId: cliClientId });
logSuccess(`Login context '${LOGIN_CONTEXT}' configured \u2192 try: mesh login ${LOGIN_CONTEXT}`);
return { projectId, cliClientId, hubAuth };
}
var ZITADEL_ISSUER, LOGIN_CONTEXT, PLATFORM_ORG, CLI_PROJECT_NAME, CLI_APP_NAME, CLI_REDIRECT_URI, HUB_PROJECT_NAME, HUB_APP_NAME, hubRedirectUri, HUB_DEFAULT_REDIRECT_URI, HUB_ROLES, ZITADEL_SSM_PARAM, TEST_USERS_SSM_PREFIX, TEST_USERS, isAlreadyExists, ensureCliProject, ensureHubProject, OPS_HUB_SECRET_ID, LOCAL_SMTP;
var init_seed_zitadel = __esm({
"libs/mesh-cli/src/commands/local/seed-zitadel.ts"() {
"use strict";
init_src();
init_log();
init_errors();
init_login();
init_stack();
init_seed();
init_cache_home();
ZITADEL_ISSUER = "http://localhost:8080";
LOGIN_CONTEXT = "local";
PLATFORM_ORG = "mesh";
CLI_PROJECT_NAME = "mesh-cli";
CLI_APP_NAME = "cli";
CLI_REDIRECT_URI = "http://localhost:9876/callback";
HUB_PROJECT_NAME = "hub";
HUB_APP_NAME = "ui";
hubRedirectUri = () => `http://localhost:${hubPort()}/oauth2/callback`;
HUB_DEFAULT_REDIRECT_URI = `http://localhost:${DEFAULT_HUB_PORT}/oauth2/callback`;
HUB_ROLES = hubOperatorRoleKeys([LOCAL_TENANT]);
ZITADEL_SSM_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/platform/zitadel`;
TEST_USERS_SSM_PREFIX = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/temporal/test-users`;
TEST_USERS = [
{
name: "dev",
email: "dev@local.mesh",
firstName: "Dev",
lastName: "User",
password: "LocalDev1!"
},
{
name: "ops",
email: "ops@local.mesh",
firstName: "Ops",
lastName: "User",
password: "LocalDev1!"
}
];
isAlreadyExists = (err) => err?.status === 409 || err?.zitadelCode === 6 || /already exists/i.test(err?.message ?? "");
ensureCliProject = (pat) => ensureZitadelProject(pat, CLI_PROJECT_NAME, {
describe: "platform client feature",
logExisting: true
});
ensureHubProject = (pat) => ensureZitadelProject(pat, HUB_PROJECT_NAME, { describe: "Hub platform app" });
OPS_HUB_SECRET_ID = "mesh/local/dev/zitadel/ops-hub";
LOCAL_SMTP = {
host: "mailpit:1025",
from: "no-reply@local.mesh",
fromName: "Mesh (local)"
};
}
});
// libs/mesh-cli/src/commands/local/auth-provision.ts
function authSecretPath(tenant, app, service) {
return `mesh/${tenant}/${LOCAL_ENV}/apps/${app}/zitadel/${service}`;
}
async function ensureOrg(pat, name) {
const search = await api(pat, "POST", "/admin/v1/orgs/_search", {
queries: [{ nameQuery: { name, method: "TEXT_QUERY_METHOD_EQUALS" } }]
});
const existing = search?.result?.[0]?.id;
if (existing) return existing;
const created = await api(pat, "POST", "/v2/organizations", { name });
logSuccess(`Created Zitadel org '${name}' (app tenant)`);
return created.organizationId;
}
async function ensureServiceApp(pat, orgId, projectId, service) {
const existing = await searchZitadelApp(pat, projectId, service, orgId);
if (existing) {
return { created: false, appId: existing.id, clientId: existing?.apiConfig?.clientId };
}
const created = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/api`,
{ name: service, authMethodType: "API_AUTH_METHOD_TYPE_BASIC" },
orgId
);
logSuccess(`Created Zitadel application '${service}' (service)`);
return { created: true, clientId: created.clientId, clientSecret: created.clientSecret };
}
async function ensureProjectRoles(pat, orgId, projectId, roles) {
if (roles.length === 0) return;
const existing = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/roles/_search`,
{},
orgId
);
const have = new Set((existing?.result ?? []).map((r) => r.key));
for (const key of roles) {
if (have.has(key)) continue;
await api(
pat,
"POST",
`/management/v1/projects/${projectId}/roles`,
{ roleKey: key, displayName: key },
orgId
);
logSuccess(`Created project role '${key}'`);
}
}
async function ensureM2mCaller(pat, tenant, app, orgId, projectId, roles) {
const userName = `${app}-m2m`;
const search = await api(
pat,
"POST",
"/management/v1/users/_search",
{ queries: [{ userNameQuery: { userName, method: "TEXT_QUERY_METHOD_EQUALS" } }] },
orgId
);
let userId = search?.result?.[0]?.id;
let createdUser = false;
if (!userId) {
const created = await api(
pat,
"POST",
"/management/v1/users/machine",
{ userName, name: `${app} local M2M caller`, accessTokenType: "ACCESS_TOKEN_TYPE_BEARER" },
orgId
);
userId = created.userId;
createdUser = true;
logSuccess(`Created machine user '${userName}' (local M2M caller)`);
}
try {
await api(pat, "POST", `/management/v1/users/${userId}/grants`, { projectId, roleKeys: roles }, orgId);
} catch (err) {
const e = err;
if (e.status !== 409 && !/already exists/i.test(String(e.message))) throw err;
const grants = await api(
pat,
"POST",
"/management/v1/users/grants/_search",
{ queries: [{ userIdQuery: { userId } }, { projectIdQuery: { projectId } }] },
orgId
);
const grantId = grants?.result?.[0]?.id;
if (grantId && roles.length > 0) {
try {
await api(pat, "PUT", `/management/v1/users/${userId}/grants/${grantId}`, { roleKeys: roles }, orgId);
} catch (updateErr) {
const ue = updateErr;
if (!/has not been changed/i.test(String(ue.message))) throw updateErr;
}
}
}
if (createdUser || !await authSecretExists(tenant, app, "m2m")) {
const secret = await api(pat, "PUT", `/management/v1/users/${userId}/secret`, {}, orgId);
await writeAuthSecret(tenant, app, "m2m", {
clientId: secret.clientId,
clientSecret: secret.clientSecret,
issuer: "http://localhost:8080",
orgId,
projectId
});
return true;
}
return false;
}
async function authSecretExists(tenant, app, service) {
const { SecretsManagerClient: SecretsManagerClient10, GetSecretValueCommand: GetSecretValueCommand10 } = await import("@aws-sdk/client-secrets-manager");
const sm = new SecretsManagerClient10(LOCAL_AWS_CONFIG);
return sm.send(new GetSecretValueCommand10({ SecretId: authSecretPath(tenant, app, service) })).then(
() => true,
() => false
);
}
async function writeAuthSecret(tenant, app, service, value) {
const name = authSecretPath(tenant, app, service);
await upsertLocalSecret(name, value);
logSuccess(`Stored service credentials \u2192 ${name}`);
}
async function registerLocalApp(args) {
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
const put2 = (name, value, description) => ssm.send(
new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: JSON.stringify(value), Description: description })
);
await registerTenantEnv(args.tenant);
const namespace = `${args.tenant}-${LOCAL_ENV}-${args.app}`;
const base = `/mesh-platform/${args.tenant}/${LOCAL_ENV}/apps/${args.app}/stacks/local`;
await put2(
base,
{
name: args.app,
tenant: args.tenant,
stack: "local",
namespace,
platform: { name: "local", env: LOCAL_ENV }
},
`Local app registration for ${args.tenant}/${args.app} (mesh dev --local)`
);
for (const service of args.services) {
const port = args.ports?.[service];
const isMock = service.startsWith("mock-");
await put2(
`${base}/services/${service}`,
{
...port ? { url: `http://localhost:${port}`, port } : {},
replicas: 1,
tier: "core",
...!isMock && args.links?.length ? { links: args.links } : {}
},
`Local service registration (${service})`
);
}
for (const service of args.services) {
if (args.kinds?.[service] !== "dagster") continue;
const port = args.ports?.[service];
if (!port) continue;
await put2(
`${base}/dagster`,
{
kind: "dagster",
graphqlUrl: `http://host.docker.internal:${port}/graphql`,
uiUrl: `http://localhost:${port}`
},
`Local Dagster discovery record (${service})`
);
}
for (const service of args.services) {
if (!/worker/i.test(service)) continue;
await put2(
`${base}/workers/${service}`,
{ temporalNamespace: namespace, taskQueue: args.app },
`Local worker registration (${service})`
);
}
await put2(
`${base}/meta`,
{
runtime: "mesh-dev-local",
services: args.services,
ports: args.ports ?? {},
// The same two fields a deployed stack stamps, so the Hub's app list and
// its activity timeline read a local run exactly as they read a deploy —
// which version of this app is running here, and since when. Without them
// a local app shows a blank version and never appears on the timeline,
// and "deployments aren't tracked" is indistinguishable from "no deploys".
...args.version ? { version: args.version } : {},
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
},
"Local run metadata (mesh dev --local)"
);
logSuccess(`Registered app in the local registry \u2192 ${base}`);
return base;
}
async function reconcileRegistryFromZitadel() {
const pat = readSeederPat();
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
const orgs = await api(pat, "POST", "/admin/v1/orgs/_search", { query: { limit: 200 } });
const tenants = [];
let apps = 0;
for (const org of orgs?.result ?? []) {
const tenant = org?.name;
if (!tenant || tenant === PLATFORM_ORG) continue;
await registerTenantEnv(tenant);
tenants.push(tenant);
const projects = await api(
pat,
"POST",
"/management/v1/projects/_search",
{ query: { limit: 200 } },
org.id
);
for (const project of projects?.result ?? []) {
const app = project?.name;
if (!app) continue;
apps++;
const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local`;
const exists = await ssm.send(new GetParameterCommand2({ Name: base })).then(
() => true,
() => false
);
if (exists) continue;
await ssm.send(
new PutParameterCommand({
Name: base,
Type: "String",
Overwrite: true,
Value: JSON.stringify({
name: app,
tenant,
stack: "local",
namespace: `${tenant}-${LOCAL_ENV}-${app}`,
platform: { name: "local", env: LOCAL_ENV }
}),
Description: `App registration reconciled from Zitadel (${tenant}/${app})`
})
);
const applications = await api(
pat,
"POST",
`/management/v1/projects/${project.id}/apps/_search`,
{ query: { limit: 100 } },
org.id
);
for (const application of applications?.result ?? []) {
const service = application?.name;
if (!service) continue;
await ssm.send(
new PutParameterCommand({
Name: `${base}/services/${service}`,
Type: "String",
Overwrite: true,
Value: JSON.stringify({ replicas: 1, tier: "core" }),
Description: `Service registration reconciled from Zitadel (${service})`
})
);
}
}
}
return { tenants, apps };
}
async function ensureSignInApp(args) {
const pat = readSeederPat();
const orgId = await ensureOrg(pat, args.tenant);
const projectId = await ensureProject(pat, orgId, args.app);
const name = `${args.service}-web`;
const base = args.baseUrl.replace(/\/+$/, "");
const redirectUris = [`${base}/oauth2/callback`];
const postLogoutRedirectUris = [base, `${base}/`];
const config = {
redirectUris,
postLogoutRedirectUris,
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"],
appType: "OIDC_APP_TYPE_WEB",
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
// Roles must ride in the token: the app authorizes on them, and the Hub
// shows which roles a person holds by reading the same grants.
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
accessTokenRoleAssertion: true,
idTokenRoleAssertion: true,
idTokenUserinfoAssertion: true,
// http:// callbacks are only permitted in dev mode.
devMode: true
};
const existing = await searchZitadelApp(pat, projectId, name, orgId);
if (existing) {
await api(
pat,
"PUT",
`/management/v1/projects/${projectId}/apps/${existing.id}/oidc_config`,
config,
orgId
);
return;
}
const created = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/oidc`,
{ name, ...config },
orgId
);
await writeAuthSecret(args.tenant, args.app, name, {
clientId: created.clientId,
clientSecret: created.clientSecret,
issuer: "http://localhost:8080",
orgId,
projectId
});
logSuccess(`Created Zitadel application '${name}' (browser sign-in \u2192 ${base}/oauth2/callback)`);
}
async function ensureAppTenantAuth(args) {
const pat = readSeederPat();
const orgId = await ensureOrg(pat, args.tenant);
const projectId = await ensureProject(pat, orgId, args.app);
const roles = args.roles ?? [];
await ensureProjectRoles(pat, orgId, projectId, roles);
const created = [];
for (const service of args.services) {
const result = await ensureServiceApp(pat, orgId, projectId, service);
if (result.created) {
await writeAuthSecret(args.tenant, args.app, service, {
clientId: result.clientId,
clientSecret: result.clientSecret,
issuer: "http://localhost:8080",
orgId,
projectId
});
created.push(service);
} else if (!await authSecretExists(args.tenant, args.app, service)) {
const regenerated = await api(
pat,
"POST",
`/management/v1/projects/${projectId}/apps/${result.appId}/api_config/_generate_client_secret`,
{},
orgId
);
await writeAuthSecret(args.tenant, args.app, service, {
clientId: result.clientId ?? "",
clientSecret: regenerated.clientSecret,
issuer: "http://localhost:8080",
orgId,
projectId
});
logSuccess(`Regenerated credentials for '${service}' (local secret store was reset)`);
created.push(service);
}
}
if (await ensureM2mCaller(pat, args.tenant, args.app, orgId, projectId, roles)) {
created.push("m2m");
}
if (created.length === 0) {
logInfo(`Auth config for ${args.tenant}/${args.app} already provisioned`);
}
return { orgId, projectId, created };
}
var ensureProject;
var init_auth_provision = __esm({
"libs/mesh-cli/src/commands/local/auth-provision.ts"() {
"use strict";
init_log();
init_seed();
init_seed_zitadel();
init_helpers();
ensureProject = (pat, orgId, name) => ensureZitadelProject(pat, name, { orgId, describe: "app" });
}
});
// libs/mesh-cli/src/utils/mesh-json.ts
import * as fs16 from "fs";
import * as path16 from "path";
function isValidTenantName(value) {
return /^[a-z][a-z0-9-]*$/.test(value);
}
function parseMeshJson(content) {
try {
const data = JSON.parse(content);
if (typeof data?.tenant !== "string" || !isValidTenantName(data.tenant)) return null;
const platform = typeof data.platform === "string" && data.platform ? data.platform : "local";
return { tenant: data.tenant, platform };
} catch {
return null;
}
}
function findMeshJson(startDir) {
let dir = path16.resolve(startDir);
while (true) {
const candidate = path16.join(dir, MESH_JSON);
if (fs16.existsSync(candidate)) {
let content = "";
try {
content = fs16.readFileSync(candidate, "utf-8");
} catch {
return null;
}
const data = parseMeshJson(content);
return data ? { path: candidate, data } : null;
}
if (fs16.existsSync(path16.join(dir, ".git"))) return null;
const parent = path16.dirname(dir);
if (parent === dir) return null;
dir = parent;
}
}
function writeMeshJson(rootDir, data) {
const target = path16.join(rootDir, MESH_JSON);
fs16.writeFileSync(target, `${JSON.stringify({ tenant: data.tenant, platform: data.platform }, null, 2)}
`);
return target;
}
var MESH_JSON;
var init_mesh_json = __esm({
"libs/mesh-cli/src/utils/mesh-json.ts"() {
"use strict";
MESH_JSON = "mesh.json";
}
});
// libs/mesh-cli/src/commands/local/dev-local.ts
import * as fs17 from "fs";
import * as path17 from "path";
function localAppNamespace(tenant, appName) {
return `${tenant}-${LOCAL_ENV}-${appName}`;
}
function logShipperPath() {
return path17.join(findPackageRoot(), "assets", "log-shipper.mjs");
}
function autoInstrumentationEnv(appRoot, src) {
const pkgDir = path17.join("node_modules", "@opentelemetry", "auto-instrumentations-node");
const present = fs17.existsSync(path17.join(appRoot, src, pkgDir)) || fs17.existsSync(path17.join(appRoot, pkgDir));
if (!present) return {};
return {
NODE_OPTIONS: { value: "--import @opentelemetry/auto-instrumentations-node/register" },
OTEL_TRACES_EXPORTER: { value: "otlp" },
// Logs ship via the launch pipeline; metrics locally come from
// Prometheus scrapes — keep the SDK to traces.
OTEL_METRICS_EXPORTER: { value: "none" },
OTEL_LOGS_EXPORTER: { value: "none" },
OTEL_NODE_DISABLED_INSTRUMENTATIONS: { value: "fs,dns,net" }
};
}
function otelEnv(tenant, appName, serviceName) {
const namespace = localAppNamespace(tenant, appName);
return {
OTEL_EXPORTER_OTLP_ENDPOINT: { value: "http://localhost:4318" },
OTEL_SERVICE_NAME: { value: serviceName },
OTEL_RESOURCE_ATTRIBUTES: {
value: `k8s.namespace.name=${namespace},k8s.deployment.name=${serviceName},k8s.container.name=${serviceName},service.namespace=${appName}`
}
};
}
function localPlatformEnv(tenant, appName, serviceName) {
const namespace = localAppNamespace(tenant, appName);
const env = {
TEMPORAL_ADDRESS: { value: TEMPORAL_ADDRESS },
TEMPORAL_NAMESPACE: { value: namespace },
TEMPORAL_TASK_QUEUE: { value: appName },
// OTel wiring, two consumers: instrumented services ship traces/metrics
// themselves, and the launch pipeline's log shipper reads these to ship
// stdout logs — both with the in-cluster k8sattributes attribution.
...otelEnv(tenant, appName, serviceName),
DATABASE_URL: { value: "postgres://postgres:postgres@localhost:5433/app" },
ZITADEL_ISSUER: { value: ZITADEL_ISSUER },
// The service's own Zitadel M2M credentials (created by ensureAppTenantAuth
// on session start) — resolveCredentials("zitadel") picks this up, so apps
// can hydrate introspection/M2M config the same way they resolve
// ExternalService links instead of hand-carrying client ids in env files.
ZITADEL_SECRET_PREFIX: { value: authSecretPath(tenant, appName, serviceName) },
SPICEDB_ENDPOINT: { value: "localhost:50051" },
SPICEDB_HTTP_ENDPOINT: { value: "http://localhost:8443" },
SPICEDB_PRESHARED_KEY: { value: "local-dev-key" },
MEMCACHED_SERVERS: { value: "localhost:11211" },
MESH_LOCAL: { value: "1" },
MESH_SERVICE: { value: serviceName }
};
const cliClientId = getContextConfig(LOGIN_CONTEXT)?.clientId;
if (cliClientId) {
env.AUTH_AUDIENCE = { value: cliClientId };
}
for (const [key, value] of Object.entries(localAwsEnv())) {
env[key] = { value };
}
return env;
}
function isDagsterWorkspaceRoot(appRoot) {
if (fs17.existsSync(path17.join(appRoot, "dg.toml"))) return true;
const pyproject = path17.join(appRoot, "pyproject.toml");
if (!fs17.existsSync(pyproject)) return false;
try {
return /^\s*\[tool\.dg[\].]/m.test(fs17.readFileSync(pyproject, "utf-8"));
} catch {
return false;
}
}
function resolveDgBinary(appRoot) {
for (const candidate of ["deployments/local/.venv/bin/dg", ".venv/bin/dg"]) {
if (fs17.existsSync(path17.join(appRoot, candidate))) return candidate;
}
return "dg";
}
function hasDevScript(dir) {
const pkgPath = path17.join(dir, "package.json");
if (!fs17.existsSync(pkgPath)) return false;
try {
const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
return typeof pkg?.scripts?.dev === "string";
} catch {
return false;
}
}
function detectLocalServices(appRoot) {
const services = {};
for (const entry of fs17.readdirSync(appRoot, { withFileTypes: true })) {
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") continue;
if (hasDevScript(path17.join(appRoot, entry.name))) {
services[entry.name] = { src: entry.name };
}
}
if (Object.keys(services).length === 0 && hasDevScript(appRoot)) {
services[path17.basename(appRoot)] = { src: "." };
}
return services;
}
function buildLocalDevOutput(appRoot, tenant, opts = {}) {
const detected = detectLocalServices(appRoot);
const names = Object.keys(detected);
const isDagsterWorkspace = isDagsterWorkspaceRoot(appRoot);
if (names.length === 0 && !isDagsterWorkspace) {
throw new MeshCliError(
`No runnable services found under ${appRoot} \u2014 expected subdirectories (api/, worker/, \u2026) with a package.json 'dev' script, or a Dagster workspace (dg.toml, or [tool.dg] in pyproject.toml).`,
{ remediation: { command: "mesh create-app" } }
);
}
let appName = path17.basename(appRoot);
try {
const pkg = JSON.parse(fs17.readFileSync(path17.join(appRoot, "package.json"), "utf-8"));
if (typeof pkg?.name === "string" && pkg.name) appName = pkg.name.replace(/^@[^/]+\//, "");
} catch {
}
const pulumiYaml = path17.join(appRoot, "Pulumi.yaml");
if (fs17.existsSync(pulumiYaml)) {
const match = fs17.readFileSync(pulumiYaml, "utf-8").match(/^name:\s*(.+)$/m);
if (match?.[1]?.trim()) appName = match[1].trim();
}
const services = {};
const mocks = opts.mocks ?? {};
const mockNames = Object.keys(mocks).filter((name) => isServiceMode(name, mocks[name]));
const externalEnv = {};
for (const decl of Object.values(mocks)) {
if (!decl.external) continue;
const prefix2 = decl.external.toUpperCase().replace(/-/g, "_");
externalEnv[`${prefix2}_SECRET_PREFIX`] = {
value: `mesh/${tenant}/${LOCAL_ENV}/external/${decl.external}`
};
}
names.forEach((name, i) => {
const port = BASE_PORT + i;
services[name] = {
src: detected[name].src,
port,
command: ["pnpm", "dev"],
env: {
// allocatePorts keeps service.port and env.PORT in sync on reallocation
PORT: { value: String(port) },
...localPlatformEnv(tenant, appName, name),
...autoInstrumentationEnv(appRoot, detected[name].src),
...externalEnv
}
};
});
for (const name of names) {
const key = `SERVICE_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_URL`;
const url = `http://localhost:${services[name].port}`;
for (const other of names) {
services[other].env[key] = { value: url };
}
}
if (isDagsterWorkspace) {
const port = BASE_PORT + names.length + mockNames.length;
services["dagster"] = {
// Absolute on purpose: a relative src is MONOREPO-ROOT-relative by the
// ServiceDevOutput contract (rebaseServiceSrc), which for an app nested
// in the monorepo would cd the launch shell to the repo root — where
// neither dg.toml nor the venv path resolves. The absolute app root
// survives rebasing in both the monorepo and standalone-repo shapes.
src: appRoot,
kind: "dagster",
port,
command: [resolveDgBinary(appRoot), "dev", "-h", "127.0.0.1", "-p", '"$PORT"'],
env: {
PORT: { value: String(port) },
DAGSTER_HOME: { value: path17.join(appRoot, ".dagster") },
...localPlatformEnv(tenant, appName, "dagster")
}
};
}
mockNames.forEach((name, i) => {
const decl = mocks[name];
const serviceName = `mock-${name}`;
const port = decl.port ?? BASE_PORT + names.length + i;
services[serviceName] = {
// openapi mode runs Prism from the app root against the spec path.
src: decl.src ?? ".",
port,
command: decl.openapi ? openapiMockCommand(decl) : decl.command ?? ["pnpm", "dev"],
env: {
PORT: { value: String(port) },
...otelEnv(tenant, appName, serviceName),
...Object.fromEntries(
Object.entries(decl.env ?? {}).map(([key, value]) => [key, { value }])
)
}
};
});
return {
platform: { tenant: "local", env: "dev" },
app: appName,
stack: LOCAL_STACK_NAME,
services,
tunnels: {}
};
}
function hasStackBacking(appRoot) {
const hasOwn = fs17.existsSync(path17.join(appRoot, "Pulumi.yaml")) || fs17.readdirSync(appRoot).some((f) => /^Pulumi\..+\.yaml$/.test(f));
if (hasOwn) return true;
let dir = path17.dirname(appRoot);
while (true) {
if (fs17.existsSync(path17.join(dir, "Pulumi.yaml"))) return true;
if (fs17.existsSync(path17.join(dir, ".git")) || fs17.existsSync(path17.join(dir, "pnpm-workspace.yaml"))) {
return fs17.existsSync(path17.join(dir, "Pulumi.yaml"));
}
const parent = path17.dirname(dir);
if (parent === dir) return false;
dir = parent;
}
}
function detectLocalTenant(appRoot) {
for (const file of fs17.readdirSync(appRoot)) {
if (!/^Pulumi\..*\.yaml$/.test(file)) continue;
const match = fs17.readFileSync(path17.join(appRoot, file), "utf-8").match(/^\s*mesh:tenant:\s*["']?([A-Za-z0-9-]+)["']?\s*$/m);
if (match) return match[1];
}
const recorded = findMeshJson(appRoot);
if (recorded) return recorded.data.tenant;
return "local";
}
async function ensureLocalPlatformRunning() {
const [temporalUp, fabricUp] = await Promise.all([probeTcp(7233), probeTcp(4566)]);
if (!temporalUp || !fabricUp) {
throw new MeshCliError(
"The local Mesh platform is not running (mesh dev local mode wires services to it).",
{ remediation: { command: "mesh start" } }
);
}
}
var LOCAL_STACK_NAME, BASE_PORT;
var init_dev_local = __esm({
"libs/mesh-cli/src/commands/local/dev-local.ts"() {
"use strict";
init_mocks();
init_helpers();
init_errors();
init_seed();
init_seed();
init_seed_zitadel();
init_auth_provision();
init_stack();
init_login();
init_mesh_json();
LOCAL_STACK_NAME = "local";
BASE_PORT = 3e3;
}
});
// libs/mesh-cli/src/commands/local/docker-runner.ts
import { execFileSync as execFileSync13 } from "child_process";
import * as fs18 from "fs";
import * as path18 from "path";
function sessionDir(sessionName) {
return meshCacheDir("mesh-local", "dev", sessionName);
}
function renderDockerEnvFile(env) {
const lines = [];
for (const [key, entry] of Object.entries(env)) {
if (entry.value.includes("\n")) continue;
lines.push(`${key}=${entry.value}`);
}
return lines.join("\n") + "\n";
}
function writeDevCompose(sessionName, appRoot, services) {
const dir = sessionDir(sessionName);
const shipper = logShipperPath();
const blocks = [
`# Generated by \`mesh dev --local --runner docker\` \u2014 do not edit.`,
`name: ${sessionName}`,
`services:`
];
for (const [name, service] of Object.entries(services)) {
const env = service.env ?? {};
fs18.writeFileSync(path18.join(dir, `${name}.env`), renderDockerEnvFile(env), { mode: 384 });
const workdir = path18.posix.join("/workspace", service.src === "." ? "" : service.src);
const ship = env.OTEL_RESOURCE_ATTRIBUTES ? ` 2>&1 | NODE_OPTIONS= node /mesh-assets/log-shipper.mjs` : "";
blocks.push(
` ${name}:`,
` image: ${DEV_RUNNER_IMAGE}`,
` network_mode: host`,
` working_dir: ${workdir}`,
` volumes:`,
` - '${appRoot}:/workspace'`,
` - '${path18.dirname(shipper)}:/mesh-assets:ro'`,
` environment:`,
// node_modules is the HOST's bind-mounted install — pnpm inside the
// container must never "verify" it and try a purge/reinstall (pnpm
// ≥10.9's verify-deps-before-run prompts and dies headless; worse, a
// purge would clobber the host install).
` CI: 'true'`,
` npm_config_verify_deps_before_run: 'false'`,
` env_file:`,
` - './${name}.env'`,
// pipefail: the container's exit code must be the SERVICE's, not the
// log-shipper's — otherwise a crashed service reports success (CI).
` command: ["bash", "-lc", "set -o pipefail; corepack enable >/dev/null 2>&1; { ${service.command.join(" ")}; }${ship}"]`,
` restart: unless-stopped`
);
}
const composePath = path18.join(dir, "compose.yml");
fs18.writeFileSync(composePath, blocks.join("\n") + "\n");
return composePath;
}
function composeArgs(sessionName, args) {
return ["compose", "-p", sessionName, "-f", path18.join(sessionDir(sessionName), "compose.yml"), ...args];
}
function dockerDevUp(sessionName) {
execFileSync13("docker", composeArgs(sessionName, ["up", "-d"]), {
stdio: ["ignore", "inherit", "inherit"]
});
}
function dockerDevDown(sessionName) {
execFileSync13("docker", composeArgs(sessionName, ["down", "--remove-orphans"]), {
stdio: ["ignore", "inherit", "inherit"]
});
}
function dockerDevPs(sessionName) {
return execFileSync13("docker", composeArgs(sessionName, ["ps", "--format", "table {{.Service}} {{.State}} {{.Status}}"]), {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
});
}
function dockerDevLogs(sessionName, service, tail) {
return execFileSync13("docker", composeArgs(sessionName, ["logs", "--tail", String(tail), service]), {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
});
}
function dockerDevRestart(sessionName, service) {
execFileSync13("docker", composeArgs(sessionName, ["restart", service]), {
stdio: ["ignore", "inherit", "inherit"]
});
}
var DEV_RUNNER_IMAGE;
var init_docker_runner = __esm({
"libs/mesh-cli/src/commands/local/docker-runner.ts"() {
"use strict";
init_dev_local();
init_cache_home();
DEV_RUNNER_IMAGE = "node:22-bookworm@sha256:5647be709086c696ff32edaaf1c70cd26d1da6ab2b39c32f3c7b4c4a31957e37";
}
});
// libs/mesh-cli/src/utils/stack-flag.ts
function resolveStackSelector(opts) {
if (opts.stack != null) return { stack: opts.stack, usedDeprecatedStage: false };
if (opts.stage != null) return { stack: opts.stage, usedDeprecatedStage: true };
return { stack: void 0, usedDeprecatedStage: false };
}
function resolveStackOption(opts) {
const { stack, usedDeprecatedStage } = resolveStackSelector(opts);
if (usedDeprecatedStage && !warnedOnce) {
warnedOnce = true;
logWarn(
"--stage is deprecated; use --stack (the Pulumi-native name). --stage still works for now."
);
}
return stack;
}
var warnedOnce;
var init_stack_flag = __esm({
"libs/mesh-cli/src/utils/stack-flag.ts"() {
"use strict";
init_log();
warnedOnce = false;
}
});
// libs/mesh-cli/src/commands/peer-addressing.ts
function clusterHostPattern(serviceName) {
const name = serviceName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
return new RegExp(`https?://${name}\\.[a-z0-9-]+\\.svc\\.cluster\\.local(?::\\d+)?`, "g");
}
function rewriteClusterHostsToLocal(devOutput) {
const localPorts = /* @__PURE__ */ new Map();
for (const [name, service] of Object.entries(devOutput.services ?? {})) {
if (!service.command || service.command.length === 0) continue;
if (typeof service.port !== "number" || service.port <= 0) continue;
localPorts.set(name, service.port);
}
if (localPorts.size === 0) return;
for (const service of Object.values(devOutput.services ?? {})) {
if (!service.env) continue;
for (const envVar of Object.values(service.env)) {
if (typeof envVar.value !== "string" || !envVar.value.includes(".svc.cluster.local")) continue;
for (const [name, port] of localPorts) {
envVar.value = envVar.value.replace(clusterHostPattern(name), `http://localhost:${port}`);
}
}
}
}
var init_peer_addressing = __esm({
"libs/mesh-cli/src/commands/peer-addressing.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/worktree-identity.ts
import { execFileSync as execFileSync14 } from "node:child_process";
import * as crypto3 from "node:crypto";
import * as path19 from "node:path";
function sanitizeSlug(name) {
const s = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
return s || "wt";
}
function worktreeHash(worktreeRoot) {
return crypto3.createHash("sha256").update(worktreeRoot).digest("hex").slice(0, 4);
}
function portBlockFor(hash, isPrimary) {
if (isPrimary) return 0;
const n = parseInt(hash, 16);
return 1 + n % (NUM_PORT_BLOCKS - 1);
}
function blockBasePort(block) {
return PORT_BLOCK_BASE + block * PORT_BLOCK_SIZE;
}
function withAppScopedPortBlock(wt, appRoot) {
if (wt.isPrimary) return wt;
const appHash = worktreeHash(`${wt.worktreeRoot}#${path19.resolve(appRoot)}`);
return { ...wt, portBlock: portBlockFor(appHash, false) };
}
function worktreeStackName(baseName, wt) {
if (!wt.token) return baseName;
const cappedSlug = wt.slug.slice(0, STACK_SLUG_MAX).replace(/-+$/, "");
return `${baseName}-${cappedSlug}-${wt.hash}`;
}
function stackNeedsWorktreeIsolation(stackName, wt) {
return !!wt.token && !stackName.endsWith(`-${wt.hash}`);
}
function resolveWorktreeIdentity(cwd = process.cwd(), gitRunner = defaultGitRunner) {
let worktreeRoot;
let isPrimary = true;
try {
worktreeRoot = path19.resolve(gitRunner(["rev-parse", "--show-toplevel"], cwd));
const commonDir = gitRunner(["rev-parse", "--git-common-dir"], cwd);
const primaryRoot = path19.resolve(path19.dirname(path19.resolve(cwd, commonDir)));
isPrimary = worktreeRoot === primaryRoot;
} catch {
return {
worktreeRoot: path19.resolve(cwd),
isPrimary: true,
slug: sanitizeSlug(path19.basename(path19.resolve(cwd))),
hash: worktreeHash(path19.resolve(cwd)),
token: "",
portBlock: 0,
taskQueueSuffix: ""
};
}
const slug = sanitizeSlug(path19.basename(worktreeRoot));
const hash = worktreeHash(worktreeRoot);
const token = isPrimary ? "" : `${slug}-${hash}`;
return {
worktreeRoot,
isPrimary,
slug,
hash,
token,
portBlock: portBlockFor(hash, isPrimary),
taskQueueSuffix: token ? `-${token}` : ""
};
}
var NUM_PORT_BLOCKS, PORT_BLOCK_BASE, PORT_BLOCK_SIZE, PORT_BLOCK_SERVICE_SUBRANGE, STACK_SLUG_MAX, defaultGitRunner;
var init_worktree_identity = __esm({
"libs/mesh-cli/src/utils/worktree-identity.ts"() {
"use strict";
NUM_PORT_BLOCKS = 64;
PORT_BLOCK_BASE = 4e4;
PORT_BLOCK_SIZE = 40;
PORT_BLOCK_SERVICE_SUBRANGE = 24;
STACK_SLUG_MAX = 16;
defaultGitRunner = (args, cwd) => execFileSync14("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
}
});
// libs/mesh-cli/src/commands/dev-token-server.ts
var dev_token_server_exports = {};
__export(dev_token_server_exports, {
handleTokenRequest: () => handleTokenRequest,
startTokenServer: () => startTokenServer
});
import * as http2 from "node:http";
async function handleTokenRequest(context, deps) {
const idToken = await deps.getValidToken(context);
const creds = idToken ? deps.readCredentials(context) : null;
if (!idToken || !creds) return { status: 503, body: { error: "token-unavailable" } };
const payload = deps.decodeJwtPayload(idToken);
return {
status: 200,
body: {
idToken,
accessToken: creds.accessToken,
sub: payload.sub ?? void 0,
email: creds.email ?? payload.email ?? void 0,
expiresAt: creds.expiresAt
}
};
}
async function startTokenServer(port, context) {
const deps = {
getValidToken: (c) => getValidToken(c, { marginMs: REFRESH_MARGIN_MS }),
readCredentials,
decodeJwtPayload
};
const server = http2.createServer((_req, res) => {
void (async () => {
try {
const { status, body } = await handleTokenRequest(context, deps);
res.writeHead(status, { "content-type": "application/json" });
res.end(JSON.stringify(body));
} catch (e) {
res.writeHead(500, { "content-type": "application/json" });
res.end(JSON.stringify({ error: e.message }));
}
})();
});
server.on("error", (e) => logWarn(`token-server error: ${e.message}`));
await new Promise((resolve19) => server.listen(port, "127.0.0.1", resolve19));
logInfo(`dev-user token-server on http://127.0.0.1:${port} (context ${context})`);
await new Promise(() => {
});
}
var REFRESH_MARGIN_MS;
var init_dev_token_server = __esm({
"libs/mesh-cli/src/commands/dev-token-server.ts"() {
"use strict";
init_login();
init_log();
REFRESH_MARGIN_MS = 12e4;
}
});
// libs/mesh-cli/src/commands/dev.ts
import { execFileSync as execFileSync15, spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
import * as fs19 from "fs";
import * as net8 from "net";
import * as os6 from "os";
import * as path20 from "path";
import { Option } from "commander";
import { SecretsManagerClient as SecretsManagerClient2, GetSecretValueCommand as GetSecretValueCommand2 } from "@aws-sdk/client-secrets-manager";
function registerServiceProbes(devOutput, tenant, probeFiles) {
const services = {};
for (const [name, service] of Object.entries(devOutput.services)) {
if (name.startsWith("mock-")) continue;
services[name] = service.port;
}
try {
const file = writeAppServiceProbes({
tenant,
env: devOutput.platform?.env ?? "dev",
app: devOutput.app ?? "",
services
});
if (file) probeFiles.push(file);
} catch (err) {
logWarn(
`Could not register uptime probes for this session (${err instanceof Error ? err.message : err}) \u2014 the Hub will show no uptime for these services.`
);
}
}
function deriveSessionName(projectName, wt) {
return wt.isPrimary ? `${projectName}-dev` : `${projectName}-${wt.slug}`;
}
function printDevPlan(sessionName, appRoot, wt, devOutput) {
const kind = wt.isPrimary ? "primary checkout" : `linked worktree (block ${wt.portBlock})`;
console.log(`
mesh dev plan \u2014 ${kind}
`);
console.log(` worktree root : ${wt.worktreeRoot}`);
console.log(` app root : ${appRoot}`);
console.log(` token : ${wt.token || "(none \u2014 primary)"}`);
console.log(` tmux session : ${sessionName}`);
console.log(` session state : ${getSessionStatePath(sessionName)}`);
console.log(` env dir : ${getSessionEnvDir(sessionName)}`);
const tq = wt.taskQueueSuffix ? `<app-task-queue>${wt.taskQueueSuffix}` : "<app-task-queue> (primary \u2014 unchanged)";
console.log(` task queue : ${tq}`);
console.log(`
services (port \xB7 resolved source dir):`);
const locals = Object.entries(devOutput.services).filter(
([, s]) => (s.port ?? 0) >= 0 && s.command && s.command.length > 0
);
if (locals.length === 0) {
console.log(` (none \u2014 all services are deployed/K8s)`);
} else {
const monorepoRoot = findMonorepoRoot();
const wtWithSep = wt.worktreeRoot.endsWith(path20.sep) ? wt.worktreeRoot : wt.worktreeRoot + path20.sep;
for (const [name, s] of locals) {
const dir = path20.resolve(appRoot, rebaseServiceSrc(s.src, monorepoRoot));
const outside = dir !== wt.worktreeRoot && !dir.startsWith(wtWithSep);
const port = s.port > 0 ? `:${s.port}` : "(no port)";
console.log(` ${name.padEnd(18)} ${port.padEnd(7)} ${dir}${outside ? " \u26A0 OUTSIDE worktree" : ""}`);
}
}
console.log("");
}
function getSessionStatePath(sessionName) {
const dir = path20.join(os6.tmpdir(), "mesh-dev-sessions");
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
return path20.join(dir, `${sessionName}.json`);
}
function saveSessionState(sessionName, state) {
fs19.writeFileSync(getSessionStatePath(sessionName), JSON.stringify(state, null, 2));
}
function loadSessionState(sessionName) {
const filePath = getSessionStatePath(sessionName);
if (!fs19.existsSync(filePath)) return null;
try {
return JSON.parse(fs19.readFileSync(filePath, "utf-8"));
} catch {
return null;
}
}
function workflowChanged(prior, current) {
return Boolean(prior) && Boolean(current) && current !== prior;
}
function workflowChangeWouldStrand(priorState, appRoot, force) {
const prior = priorState?.workflowFingerprint;
if (!prior) return false;
const current = fingerprintWorkflowSource(appRoot, priorState?.devOutput?.services);
if (!workflowChanged(prior, current)) return false;
logWarn(
"Workflow code changed since this session started.\n Restarting the worker replays any in-flight conversations against the NEW code \u2014\n a replay-incompatible change strands them (they get stuck and become unviewable).\n Keep it replay-compatible: gate the change with wf.patched() and regenerate the replay\n goldens (see the temporal-workflow-safety skill), or accept the risk."
);
if (force) {
logWarn(" Proceeding anyway (--force).");
return false;
}
logError(" Refusing to restart the worker. Re-run with --force once the change is replay-safe.");
return true;
}
function getSessionEnvDir(sessionName) {
return path20.join(os6.tmpdir(), "mesh-dev-sessions", sessionName);
}
function getServiceEnvFilePath(sessionName, serviceName) {
return path20.join(getSessionEnvDir(sessionName), envFileName(serviceName));
}
function removeSessionState(sessionName) {
try {
fs19.unlinkSync(getSessionStatePath(sessionName));
} catch {
}
try {
fs19.rmSync(getSessionEnvDir(sessionName), { recursive: true, force: true });
} catch {
}
}
async function isPortFree(port) {
const bindSucceeds = (host) => new Promise((resolve19) => {
const server = net8.createServer();
server.once("error", () => resolve19(false));
const onListening = () => server.close(() => resolve19(true));
if (host === void 0) server.listen(port, onListening);
else server.listen(port, host, onListening);
});
if (!await bindSucceeds()) return false;
return bindSucceeds("127.0.0.1");
}
async function findFreePort3() {
return new Promise((resolve19, reject) => {
const server = net8.createServer();
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
const { port } = server.address();
server.close(() => resolve19(port));
});
});
}
function isPortListening(port) {
return new Promise((resolve19) => {
const s = new net8.Socket();
let done = false;
const fin = (ok) => {
if (done) return;
done = true;
s.destroy();
resolve19(ok);
};
s.setTimeout(300);
s.once("connect", () => fin(true));
s.once("timeout", () => fin(false));
s.once("error", () => fin(false));
s.connect(port, "127.0.0.1");
});
}
function rewriteOwnServicePort(service, oldPort, newPort) {
for (const [key, spec] of Object.entries(service.env ?? {})) {
if ((key === "PORT" || key.endsWith("_PORT")) && spec.value === String(oldPort)) {
spec.value = String(newPort);
}
}
}
async function allocatePorts(devOutput, worktree) {
const allocated = JSON.parse(JSON.stringify(devOutput));
const usedPorts = /* @__PURE__ */ new Set();
const portRemaps = /* @__PURE__ */ new Map();
const blockBase = worktree.isPrimary ? 0 : blockBasePort(worktree.portBlock);
let blockCursor = 0;
for (const [name, service] of Object.entries(allocated.services)) {
if (service.port <= 0) continue;
if (!service.command || service.command.length === 0) continue;
const preferred = service.port;
let target = -1;
if (worktree.isPrimary) {
if (!usedPorts.has(preferred) && await isPortFree(preferred)) {
target = preferred;
} else {
target = await findFreePort3();
logWarn(`Port ${preferred} busy for service '${name}', using ${target}`);
}
} else {
for (let i = blockCursor; i < PORT_BLOCK_SERVICE_SUBRANGE; i++) {
const candidate = blockBase + i;
if (!usedPorts.has(candidate) && await isPortFree(candidate)) {
target = candidate;
blockCursor = i + 1;
break;
}
}
if (target < 0) {
target = await findFreePort3();
logWarn(`Worktree port block exhausted for '${name}', using ${target}`);
}
}
if (target !== preferred) {
rewriteOwnServicePort(service, preferred, target);
portRemaps.set(preferred, target);
service.port = target;
}
usedPorts.add(target);
}
rewriteCrossServicePorts(allocated, portRemaps);
rewriteClusterHostsToLocal(allocated);
return allocated;
}
function rewriteCrossServicePorts(devOutput, portRemaps) {
if (portRemaps.size === 0) return;
for (const service of Object.values(devOutput.services)) {
if (!service.env) continue;
for (const envVar of Object.values(service.env)) {
for (const [oldPort, newPort] of portRemaps) {
if (envVar.value.includes(`localhost:${oldPort}`)) {
envVar.value = envVar.value.replace(`localhost:${oldPort}`, `localhost:${newPort}`);
}
}
}
}
}
function mergePinnedPorts(fresh, pinned) {
const merged = JSON.parse(JSON.stringify(fresh));
const pinnedTunnels = JSON.parse(JSON.stringify(pinned.tunnels ?? {}));
merged.tunnels = { ...merged.tunnels ?? {}, ...pinnedTunnels };
const portRemaps = /* @__PURE__ */ new Map();
for (const [name, mergedSvc] of Object.entries(merged.services ?? {})) {
const pinnedSvc = pinned.services?.[name];
if (!pinnedSvc || typeof pinnedSvc.port !== "number") continue;
const freshPort = mergedSvc.port;
mergedSvc.port = pinnedSvc.port;
if (mergedSvc.env?.PORT) {
mergedSvc.env.PORT.value = String(pinnedSvc.port);
}
if (typeof freshPort === "number" && freshPort > 0 && freshPort !== pinnedSvc.port) {
portRemaps.set(freshPort, pinnedSvc.port);
}
}
rewriteCrossServicePorts(merged, portRemaps);
rewriteClusterHostsToLocal(merged);
return merged;
}
function findMonorepoRoot() {
let dir = process.cwd();
const root = path20.parse(dir).root;
while (dir !== root) {
if (fs19.existsSync(path20.join(dir, "pnpm-workspace.yaml")) || fs19.existsSync(path20.join(dir, "turbo.json"))) {
return dir;
}
const gitPath = path20.join(dir, ".git");
if (fs19.existsSync(gitPath)) {
return dir;
}
dir = path20.dirname(dir);
}
return null;
}
function discoverApps(monorepoRoot) {
const apps = [];
function addApp(appDir, tenant) {
const projectName = getProjectName(appDir);
const stacks = fs19.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
apps.push({
name: projectName,
tenant,
appPath: appDir,
relativePath: path20.relative(monorepoRoot, appDir),
stacks
});
}
const searchDirs = ["tenants", "tests/tenants"];
for (const searchDir of searchDirs) {
const base = path20.join(monorepoRoot, searchDir);
if (!fs19.existsSync(base)) continue;
for (const tenant of fs19.readdirSync(base)) {
const appsDir = path20.join(base, tenant, "apps");
if (!fs19.existsSync(appsDir) || !fs19.statSync(appsDir).isDirectory()) continue;
for (const app of fs19.readdirSync(appsDir)) {
const appDir = path20.join(appsDir, app);
if (!fs19.existsSync(path20.join(appDir, "Pulumi.yaml"))) continue;
addApp(appDir, tenant);
}
}
}
const flatAppsDir = path20.join(monorepoRoot, "apps");
if (fs19.existsSync(flatAppsDir) && fs19.statSync(flatAppsDir).isDirectory()) {
for (const app of fs19.readdirSync(flatAppsDir)) {
const appDir = path20.join(flatAppsDir, app);
if (!fs19.statSync(appDir).isDirectory()) continue;
if (!fs19.existsSync(path20.join(appDir, "Pulumi.yaml"))) continue;
let tenant = "unknown";
const stackConfigs = fs19.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
if (stackConfigs.length > 0) {
try {
const content = fs19.readFileSync(path20.join(appDir, stackConfigs[0]), "utf-8");
const tenantMatch = content.match(/mesh:tenant:\s*(\S+)/);
if (tenantMatch) tenant = tenantMatch[1];
} catch {
}
}
addApp(appDir, tenant);
}
}
return apps;
}
function findAppRoot2(appPath) {
if (appPath) {
const resolved = path20.resolve(appPath);
if (fs19.existsSync(path20.join(resolved, "Pulumi.yaml"))) return resolved;
const mono = findMonorepoRoot();
if (mono) {
const fromMono = path20.resolve(mono, appPath);
if (fs19.existsSync(path20.join(fromMono, "Pulumi.yaml"))) return fromMono;
}
logError(`No Pulumi.yaml found at: ${appPath}`);
process.exit(1);
}
let dir = process.cwd();
const root = path20.parse(dir).root;
while (dir !== root) {
if (fs19.existsSync(path20.join(dir, "Pulumi.yaml"))) {
return dir;
}
dir = path20.dirname(dir);
}
return process.cwd();
}
function getProjectName(appRoot) {
const yamlPath = path20.join(appRoot, "Pulumi.yaml");
if (!fs19.existsSync(yamlPath)) return path20.basename(appRoot);
const content = fs19.readFileSync(yamlPath, "utf-8");
const match = content.match(/^name:\s*(.+)$/m);
return match?.[1]?.trim() ?? path20.basename(appRoot);
}
function detectStack(appRoot, stageArg) {
if (stageArg) return stageArg;
if (process.env.MESH_STAGE) return process.env.MESH_STAGE;
try {
const result = execFileSync15("pulumi", ["stack", "--show-name"], {
cwd: appRoot,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
}).trim();
if (result) return result;
} catch {
}
try {
const files = fs19.readdirSync(appRoot).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
if (files.length === 1) {
const match = files[0].match(/^Pulumi\.(.+)\.yaml$/);
if (match?.[1]) {
logInfo(`Auto-detected stack from ${files[0]}`);
return match[1];
}
}
} catch {
}
return "dev";
}
function stackArgs(appRoot, stack) {
try {
const selected = execFileSync15("pulumi", ["stack", "--show-name"], {
cwd: appRoot,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
}).trim();
if (selected === stack) return [];
} catch {
}
try {
execFileSync15("pulumi", ["stack", "select", stack], {
cwd: appRoot,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
return [];
} catch {
}
return ["--stack", stack];
}
function getDevOutput(appRoot, stack, awsEnv) {
const sa = stackArgs(appRoot, stack);
try {
const result = pulumiStackOutput(appRoot, "app", sa, awsEnv);
const appOutput = JSON.parse(result);
if (appOutput.dev) {
return appOutput.dev;
}
} catch {
}
try {
const result = pulumiStackOutput(appRoot, "dev", sa, awsEnv);
return JSON.parse(result);
} catch (err) {
throw new MissingStackOutputError(stack, err);
}
}
function hasTmux() {
try {
execFileSync15("which", ["tmux"], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function sessionExists(name) {
try {
execFileSync15("tmux", ["has-session", "-t", name], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function killSession(name) {
try {
execFileSync15("tmux", ["kill-session", "-t", name], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function applyAwsProfileOverride(profile) {
process.env.AWS_PROFILE = profile;
for (const key of AWS_STATIC_CREDENTIAL_ENV_KEYS) {
delete process.env[key];
}
}
function getAwsEnvVars() {
const result = {};
const vars = [
"AWS_PROFILE",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
"AWS_REGION",
"AWS_DEFAULT_REGION"
];
for (const name of vars) {
const value = process.env[name];
if (value) result[name] = value;
}
return result;
}
function buildChildAwsEnv(awsEnv, method, profile, opts) {
const result = {};
if (awsEnv.AWS_REGION) result.AWS_REGION = awsEnv.AWS_REGION;
if (awsEnv.AWS_DEFAULT_REGION) result.AWS_DEFAULT_REGION = awsEnv.AWS_DEFAULT_REGION;
if (method === "sso" && profile) {
result.AWS_PROFILE = profile;
return result;
}
if (method === "zitadel" && opts) {
const sanitizedContext = opts.context.replace(/[^A-Za-z0-9_-]/g, "-");
const profileName = `mesh-dev-${sanitizedContext}`;
const configPath = path20.join(opts.sessionScratchDir, "aws-config");
const region = awsEnv.AWS_REGION ?? awsEnv.AWS_DEFAULT_REGION ?? "";
fs19.mkdirSync(opts.sessionScratchDir, { recursive: true });
const profileBlock = renderCredentialProcessProfile({
profileName,
context: opts.context,
roleArn: opts.roleArn,
region,
meshBin: opts.meshBin
});
const existing = fs19.existsSync(configPath) ? fs19.readFileSync(configPath, "utf8") : "";
const next = upsertManagedAwsConfigSection(
stripBareProfile(existing, profileName),
`${opts.context} (mesh dev)`,
profileBlock
);
if (next !== existing) atomicWriteFileSync(configPath, next, 384);
const out = {
AWS_PROFILE: profileName,
AWS_CONFIG_FILE: configPath
};
if (region) out.AWS_REGION = region;
return out;
}
for (const key of ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]) {
if (awsEnv[key]) result[key] = awsEnv[key];
}
if (profile && !result.AWS_ACCESS_KEY_ID) {
result.AWS_PROFILE = profile;
}
return result;
}
function rebaseServiceSrc(src, monorepoRoot) {
if (!monorepoRoot) return src;
const rel2 = monorepoRelativeSrc(src);
if (rel2 === null) return src;
const rebased = path20.join(monorepoRoot, rel2);
return fs19.existsSync(rebased) ? rebased : src;
}
function isLinkedDependencyDir(serviceDir, appRoot) {
let pkgRoot = null;
let name = "";
for (let dir = serviceDir; ; dir = path20.dirname(dir)) {
try {
const manifest = JSON.parse(fs19.readFileSync(path20.join(dir, "package.json"), "utf8"));
if (typeof manifest?.name === "string" && manifest.name) {
pkgRoot = dir;
name = manifest.name;
break;
}
} catch {
}
const parent = path20.dirname(dir);
if (parent === dir) break;
}
if (!pkgRoot) return false;
for (let dir = appRoot; ; dir = path20.dirname(dir)) {
try {
if (fs19.realpathSync(path20.join(dir, "node_modules", name)) === pkgRoot) return true;
} catch {
}
const parent = path20.dirname(dir);
if (parent === dir) return false;
}
}
function monorepoRelativeSrc(src) {
if (!path20.isAbsolute(src)) return src.replace(/^\.\//, "") || ".";
const m = src.match(/^.*\/mesh-platform(?:\/(.*))?$/);
if (!m) return null;
const rest = (m[1] ?? "").replace(/^\.worktrees\/[^/]+(?:\/|$)/, "");
return rest === "" ? "." : rest;
}
function getServiceEnvVars(service, tunnels) {
const result = {};
if (service.env) {
for (const [key, spec] of Object.entries(service.env)) {
let value = spec.value;
if (spec.tunnel) {
const tunnel = tunnels[spec.tunnel];
if (tunnel) {
try {
const url = new URL(value);
if (url.hostname) {
url.hostname = tunnel.host;
url.port = String(tunnel.port);
value = url.toString();
} else {
value = `${tunnel.host}:${tunnel.port}`;
}
} catch {
value = `${tunnel.host}:${tunnel.port}`;
}
}
}
result[key] = value;
}
}
return result;
}
function setTmuxEnv(sessionName, vars) {
for (const [key, value] of Object.entries(vars)) {
execFileSync15("tmux", ["set-environment", "-t", sessionName, key, value], { stdio: "ignore" });
}
}
function envPrefix(vars) {
const entries = Object.entries(vars);
if (entries.length === 0) return "";
const parts = entries.map(([k, v]) => `${k}=${shellEscape(v)}`);
return `env ${parts.join(" ")} `;
}
function shellEscape(s) {
if (/^[a-zA-Z0-9_./:@=+,-]+$/.test(s)) return s;
return `'${s.replace(/'/g, "'\\''")}'`;
}
async function resolveSecrets(secrets) {
const client = new SecretsManagerClient2({});
const resolved = {};
for (const [name, secret] of Object.entries(secrets)) {
try {
const response = await client.send(
new GetSecretValueCommand2({ SecretId: secret.secretName })
);
if (!response.SecretString) continue;
const values = JSON.parse(response.SecretString);
for (const [secretKey, envVar] of Object.entries(secret.envMapping)) {
const value = values[secretKey];
if (value === void 0) continue;
resolved[envVar] = value;
}
logSuccess(`Secret resolved: ${name} (${secret.secretName})`);
} catch (err) {
logWarn(
`Could not resolve secret '${name}' (${secret.secretName}): ${err instanceof Error ? err.message : String(err)}`
);
}
}
return resolved;
}
function resolveTemporalEncodingKey(tenant, env, appName) {
const namespace = `${tenant}-${env}-${appName}`;
const secretName = `${namespace}-temporal-encoding-key`;
try {
const b64 = execFileSync15(
"kubectl",
[
"get",
"secret",
secretName,
"-n",
namespace,
"-o",
"jsonpath={.data.TEMPORAL_ENCODING_KEY}"
],
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
).trim();
if (b64) {
const key = Buffer.from(b64, "base64").toString("utf-8");
logSuccess(`Temporal encoding key resolved from K8s secret (${secretName})`);
return key;
}
} catch {
}
return void 0;
}
async function resolveTemporalAuthVars(sessionName, devOutput) {
const tenant = devOutput.platform?.tenant ?? "mesh";
const platformEnv = devOutput.platform?.env ?? "dev";
const platformName = resolveHubPlatformName(devOutput.platform);
const kubeconfigPath = await ensureKubeconfig(platformName, platformEnv, sessionName, {
onError: ({ parameter, error }) => logWarn(
`EKS cluster resolve failed (${parameter}): ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`
)
});
if (kubeconfigPath) {
process.env.KUBECONFIG = kubeconfigPath;
setTmuxEnv(sessionName, { KUBECONFIG: kubeconfigPath });
} else if (!process.env.KUBECONFIG) {
const existing = sessionKubeconfigPath(sessionName);
if (fs19.existsSync(existing)) {
process.env.KUBECONFIG = existing;
}
}
let temporalAuthVars = {};
if (devOutput.tunnels["temporal"]) {
temporalAuthVars = await resolveTemporalAuth(tenant, platformEnv, devOutput.platform?.name ?? tenant);
const appName = devOutput.app ?? "";
if (appName) {
const encodingKey = resolveTemporalEncodingKey(tenant, platformEnv, appName);
if (encodingKey) {
temporalAuthVars.TEMPORAL_ENCODING_KEY = encodingKey;
}
}
const temporalTunnel = devOutput.tunnels["temporal"];
temporalAuthVars.TEMPORAL_ADDRESS = tunnelClientAddress(temporalTunnel);
}
return temporalAuthVars;
}
function loginContextFor(platform) {
return `${platform.name ?? "mesh"}.${platform.env}`;
}
function resolveDevUserVars(devOutput) {
const credContext = devOutput.platform ? devOutput.platform.tenant === "local" ? "local" : loginContextFor(devOutput.platform) : null;
if (!credContext) return {};
const meshCreds = readCredentials(credContext);
if (!meshCreds?.idToken) return {};
const vars = { DEV_USER_ID_TOKEN: meshCreds.idToken };
try {
const payload = JSON.parse(
Buffer.from(meshCreds.idToken.split(".")[1], "base64url").toString()
);
if (payload.sub) vars.DEV_USER_ID = payload.sub;
if (payload.email) vars.DEV_USER_EMAIL = payload.email;
} catch {
}
if (meshCreds.accessToken) {
vars.DEV_USER_ACCESS_TOKEN = meshCreds.accessToken;
}
return vars;
}
function tunnelClientAddress(tunnel) {
const host = tunnel.host === "localhost" ? "127.0.0.1" : tunnel.host;
return `${host}:${tunnel.port}`;
}
function formatTunnelHealth(health) {
if (health.length === 0) return [];
const lines = ["Connections:"];
for (const h of health) {
const icon = h.reachable ? "\u2713" : "\u26A0";
const suffix = h.reachable ? "" : " DOWN";
lines.push(` ${icon} ${h.name.padEnd(16)} ${h.address}${suffix}`);
}
const down = health.filter((h) => !h.reachable).map((h) => h.name);
if (down.length > 0) {
const noun = down.length === 1 ? "tunnel" : "tunnels";
lines.push("");
lines.push(
` \u26A0 ${down.length} ${noun} down (${down.join(", ")}) \u2014 dependent services are retrying (ECONNREFUSED spam is expected). Relaunch: mesh dev`
);
}
return lines;
}
async function probeTunnelHealth(tunnels) {
return Promise.all(
Object.entries(tunnels).map(async ([name, tun]) => ({
name,
address: tunnelClientAddress(tun),
// Dial the SAME address the client uses (localhost→127.0.0.1) so the probe
// sees exactly what a dependent service would. Use the connection-HOLDS
// probe, not a bare accept: a forwarder binds its local port even when its
// upstream leg is dead (a dropped SSM session, or a Tailscale SOCKS bridge
// dialing a stale port), so "port accepts" would report a dead
// tunnel as healthy. Holding briefly catches the accept-then-close teardown.
reachable: await probeConnectionHolds(
tun.host === "localhost" ? "127.0.0.1" : tun.host,
tun.port,
800
)
}))
);
}
async function firstUnroutableTunnel(tunnels) {
const temporal = tunnels["temporal"];
if (!temporal || temporal.host !== "localhost") return null;
const holds = await probeConnectionHolds("127.0.0.1", temporal.port, 800);
return holds ? null : "temporal";
}
function resolveTransport(flag, ctx) {
if (flag === "tailscale" || flag === "ssm") return flag;
if (flag !== void 0 && flag !== "auto") {
throw new Error(`Invalid --transport '${flag}'. Valid values: auto (default), tailscale, ssm.`);
}
const legacy = ctx.vpnConnected ? "vpn-direct" : ctx.hasSsmPlugin ? "ssm" : "vpn-direct";
return ctx.tailscaleAvailable ? "tailscale" : legacy;
}
function reachabilityFallbackTransport(transport, temporalReachable, hasSsmPlugin) {
if (transport !== "vpn-direct") return { transport, reason: "not-vpn-direct" };
if (temporalReachable) return { transport, reason: "reachable" };
if (hasSsmPlugin) return { transport: "ssm", reason: "fallback-ssm" };
return { transport, reason: "unreachable-no-plugin" };
}
async function gateVpnDirectReachability(transport, devOutput) {
if (transport !== "vpn-direct") return transport;
const temporalTunnel = devOutput.tunnels?.["temporal"];
if (!temporalTunnel) return transport;
const { host, port } = temporalTunnel;
const reachable = await probeTcpReachable(host, port, 1500);
const decision = reachabilityFallbackTransport(transport, reachable, hasSessionManagerPlugin());
if (decision.reason === "reachable") {
logInfo(`Transport: VPN-direct \u2014 Temporal VPC endpoint ${host}:${port} is reachable.`);
} else if (decision.reason === "fallback-ssm") {
logWarn(
`Transport: VPN reports connected but the Temporal VPC endpoint ${host}:${port} is unreachable (tailnet-blind presence check) \u2014 falling back to SSM tunnels (2XXXX port range; VPN-only features like in-cluster kubectl are unavailable). Pin with --transport=ssm to skip this probe.`
);
} else if (decision.reason === "unreachable-no-plugin") {
logWarn(
`Transport: Temporal VPC endpoint ${host}:${port} is unreachable and the SSM session-manager plugin is missing \u2014 VPN-direct will likely fail. Fix VPN routing or install the plugin (see: mesh dev doctor).`
);
}
return decision.transport;
}
function deriveLoginServer2(context) {
const cfg = getContextConfig(context);
if (!cfg?.issuer) return null;
try {
const u = new URL(cfg.issuer);
const parts = u.hostname.split(".");
parts[0] = "vpn";
return `https://${parts.join(".")}`;
} catch {
return null;
}
}
function preferredSsmLocalPort(remotePort) {
const preferred = SSM_TUNNEL_BASE_PORT + remotePort;
if (preferred > 65535) {
throw new Error(
`Cannot allocate SSM tunnel port for remote port ${remotePort}: preferred local port ${preferred} exceeds 65535`
);
}
return preferred;
}
function reserveSsmLocalPortCandidate(remotePort, reservedPorts, startAt = preferredSsmLocalPort(remotePort)) {
for (let port = startAt; port <= 65535; port += 1) {
if (!reservedPorts.has(port)) {
reservedPorts.add(port);
return port;
}
}
throw new Error(
`Cannot allocate SSM tunnel port for remote port ${remotePort}: no free candidate ports remain`
);
}
async function allocateSsmLocalPort(remotePort, reservedPorts) {
let nextCandidate = preferredSsmLocalPort(remotePort);
while (nextCandidate <= 65535) {
const candidate = reserveSsmLocalPortCandidate(remotePort, reservedPorts, nextCandidate);
if (await isPortFree(candidate)) {
return candidate;
}
nextCandidate = candidate + 1;
}
throw new Error(
`Cannot allocate SSM tunnel port for remote port ${remotePort}: no local ports are available`
);
}
function hasSessionManagerPlugin() {
try {
execFileSync15("which", ["session-manager-plugin"], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
async function startSsmTunnels(sessionName, devOutput) {
const platformEnv = devOutput.platform?.env ?? "dev";
const bastionTenant = devOutput.platform?.name ?? "mesh";
const bastion = await getPlatformBastionInfo(bastionTenant, platformEnv).catch(() => null);
if (!bastion) {
logError("Could not read platform bastion info from SSM.");
logInfo("Make sure PlatformBastion is deployed in your platform stack.");
logInfo("If you don't have a bastion, connect via VPN instead: mesh vpn connect");
throw new Error("SSM tunnel fallback unavailable: no bastion found");
}
const rewritten = JSON.parse(JSON.stringify(devOutput));
const tunnelCount = Object.keys(devOutput.tunnels).length;
if (tunnelCount === 0) {
logInfo("No tunnels defined \u2014 SSM fallback not needed.");
return rewritten;
}
logInfo(`Starting ${tunnelCount} SSM tunnel(s) via bastion ${bastion.instanceId}...`);
const tunnelToBastionKey = {
temporal: "temporal-frontend",
"temporal-ui": "temporal-ui",
rds: "rds",
db: "rds",
database: "rds"
};
const OPTIONAL_TUNNEL_NAMES = /* @__PURE__ */ new Set([
"loki",
"prometheus",
"tempo",
"pushgateway",
"grafana"
]);
const missingEndpoints = [];
const skippedOptional = [];
const reservedPorts = /* @__PURE__ */ new Set();
const plannedTunnels = [];
for (const [tunnelName] of Object.entries(devOutput.tunnels)) {
const bastionKey = tunnelToBastionKey[tunnelName] ?? tunnelName;
const endpoint = bastion.services[bastionKey];
if (!endpoint) {
if (OPTIONAL_TUNNEL_NAMES.has(tunnelName)) {
skippedOptional.push(`${tunnelName} (bastion key: ${bastionKey})`);
} else {
missingEndpoints.push(`${tunnelName} (bastion key: ${bastionKey})`);
}
continue;
}
plannedTunnels.push({
tunnelName,
endpoint,
localPort: await allocateSsmLocalPort(endpoint.port, reservedPorts)
});
}
if (skippedOptional.length > 0) {
logWarn(
"Skipping optional observability tunnel(s) not exposed by the bastion (SSM fallback):"
);
for (const skipped of skippedOptional) {
logInfo(` Skipped: ${skipped}`);
}
logInfo(
" Monitoring views (logs/metrics/traces) stay unavailable until VPN is connected."
);
}
if (missingEndpoints.length > 0) {
logError("SSM tunnel fallback cannot cover every required dev tunnel.");
for (const missing of missingEndpoints) {
logInfo(` Missing: ${missing}`);
}
logInfo(
` Available bastion services: ${Object.keys(bastion.services).join(", ") || "(none)"}`
);
logInfo(
"Connect VPN instead or deploy/update PlatformBastion with the missing service endpoints."
);
throw new Error("SSM tunnel fallback unavailable: missing bastion endpoints");
}
for (const { tunnelName, endpoint, localPort } of plannedTunnels) {
const windowName = `tunnel-${tunnelName}`;
const ssmCmd = [
"aws",
"ssm",
"start-session",
"--target",
bastion.instanceId,
"--document-name",
"AWS-StartPortForwardingSessionToRemoteHost",
"--parameters",
`'${JSON.stringify({
host: [endpoint.host],
portNumber: [String(endpoint.port)],
localPortNumber: [String(localPort)]
})}'`
].join(" ");
execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", windowName]);
execFileSync15(
"tmux",
["set-option", "-t", `${sessionName}:${windowName}`, "remain-on-exit", "on"],
{ stdio: "ignore" }
);
execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:${windowName}`, ssmCmd, "Enter"]);
rewritten.tunnels[tunnelName] = {
host: "localhost",
port: localPort
};
logSuccess(
` ${tunnelName}: localhost:${localPort} \u2192 ${endpoint.host}:${endpoint.port} (via SSM)`
);
}
return rewritten;
}
async function updateDevboxProxy(devOutput, appRoot) {
const appName = devOutput.app ?? path20.basename(appRoot);
const tenant = devOutput.platform?.tenant ?? "mesh";
const env = devOutput.platform?.env ?? "dev";
const stack = devOutput.stack ?? "dev";
const baseDomain = `${tenant}-${env}.mesh.local`;
const services = {};
for (const [name, service] of Object.entries(devOutput.services)) {
if (service.port) {
services[`${name}.${stack}.${appName}.${baseDomain}`] = service.port;
}
}
for (const [name, tunnel] of Object.entries(devOutput.tunnels)) {
if (tunnel.port) {
services[`${name}.${stack}.${appName}.${baseDomain}`] = tunnel.port;
}
}
if (Object.keys(services).length === 0) return;
const lines = [
"# Auto-generated by mesh dev \u2014 do not edit manually.",
"",
":8080 {",
' respond /health "OK" 200',
"}",
""
];
for (const [hostname2, port] of Object.entries(services)) {
lines.push(`http://${hostname2} {`);
lines.push(` reverse_proxy localhost:${port}`);
lines.push("}");
lines.push("");
}
const caddyfile = lines.join("\n");
execFileSync15(
"docker",
[
"run",
"--rm",
"-v",
"/etc/caddy:/etc/caddy",
"busybox",
"sh",
"-c",
`cat > /etc/caddy/Caddyfile << 'CADDYEOF'
${caddyfile}
CADDYEOF`
],
{ stdio: "pipe" }
);
execFileSync15(
"docker",
[
"run",
"--rm",
"--pid=host",
"--privileged",
"busybox",
"nsenter",
"-t",
"1",
"-m",
"--",
"caddy",
"reload",
"--config",
"/etc/caddy/Caddyfile",
"--adapter",
"caddyfile"
],
{ stdio: "pipe" }
);
logSuccess("DevBox proxy updated:");
for (const [hostname2, port] of Object.entries(services)) {
logInfo(` http://${hostname2} \u2192 localhost:${port}`);
}
}
async function setupSubdomainRouting(devOutput, appRoot) {
const tsInfo = await getTailscaleInfo();
if (!tsInfo) return null;
const appName = devOutput.app ?? path20.basename(appRoot);
const stack = devOutput.stack ?? "dev";
const baseDomain = `${appName}.${stack}.${tsInfo.hostname}.vpn.internal`;
const platform = devOutput.platform ?? { tenant: "mesh", env: "dev" };
const dnsConfig = headscaleDnsConfig(platform.name ?? platform.tenant, platform.env);
const records = [];
for (const [name, service] of Object.entries(devOutput.services)) {
if (service.port) {
records.push({ name: `${name}.${baseDomain}`, type: "A", value: tsInfo.ip });
}
}
for (const [name] of Object.entries(devOutput.tunnels)) {
records.push({ name: `${name}.${baseDomain}`, type: "A", value: tsInfo.ip });
}
try {
unregisterDnsRecords(dnsConfig, baseDomain);
} catch {
}
const caddyDir = "/tmp/mesh-dev-caddy";
const caddyfile = path20.join(caddyDir, "Caddyfile");
if (!fs19.existsSync(caddyDir)) fs19.mkdirSync(caddyDir, { recursive: true });
const caddyLines = [`# mesh-dev: ${appName}/${stack} (${tsInfo.hostname})`, ""];
for (const [name, service] of Object.entries(devOutput.services)) {
if (service.port) {
caddyLines.push(`http://${name}.${baseDomain} {`);
caddyLines.push(` bind ${tsInfo.ip}`);
caddyLines.push(` reverse_proxy localhost:${service.port}`);
caddyLines.push("}");
caddyLines.push("");
}
}
for (const [name, tunnel] of Object.entries(devOutput.tunnels)) {
caddyLines.push(`http://${name}.${baseDomain} {`);
caddyLines.push(` bind ${tsInfo.ip}`);
caddyLines.push(` reverse_proxy ${tunnel.host}:${tunnel.port}`);
caddyLines.push("}");
caddyLines.push("");
}
fs19.writeFileSync(caddyfile, caddyLines.join("\n"));
let caddyRunning = false;
try {
execFileSync15("pgrep", ["-f", "caddy run.*mesh-dev-caddy"], { stdio: "pipe" });
caddyRunning = true;
} catch {
}
if (caddyRunning) {
try {
execFileSync15("caddy", ["reload", "--config", caddyfile, "--adapter", "caddyfile"], {
stdio: "pipe"
});
} catch {
logWarn("Caddy reload failed \u2014 check Caddyfile syntax");
return null;
}
} else {
try {
execFileSync15("which", ["caddy"], { stdio: "pipe" });
} catch {
logWarn(
"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"
);
return null;
}
const caddy = spawn3("caddy", ["run", "--config", caddyfile, "--adapter", "caddyfile"], {
stdio: "ignore",
detached: true,
cwd: caddyDir
});
caddy.unref();
}
try {
const existing = readDnsRecords(dnsConfig);
const allRecords = existing.filter((r) => !r.name.endsWith(baseDomain));
allRecords.push(...records);
registerDnsRecords(dnsConfig, allRecords);
} catch {
logWarn(
"DNS registration failed \u2014 Headscale pod may be unreachable. Dev routing may not work."
);
return null;
}
return { tsHostname: tsInfo.hostname, tsIp: tsInfo.ip, baseDomain, dnsConfig };
}
async function startServices(sessionName, appRoot, devOutput, headless, awsEnv, tunnelPlan = { transport: "vpn-direct" }, worktreeRoot, taskQueueSuffix = "") {
if (!hasTmux()) {
logError("tmux is required. Install with: brew install tmux");
process.exit(1);
}
if (sessionExists(sessionName)) {
logInfo(`Killing existing session: ${sessionName}`);
killSession(sessionName);
}
const serviceNames = Object.keys(devOutput.services);
if (serviceNames.length === 0) {
logWarn("No services defined in dev output.");
return devOutput;
}
logInfo(`Creating tmux session: ${sessionName}`);
execFileSync15("tmux", ["new-session", "-d", "-s", sessionName, "-n", "status", "-c", appRoot]);
setTmuxEnv(sessionName, awsEnv);
const hasTunnels = Object.keys(devOutput.tunnels).length > 0;
let effectiveTransport = tunnelPlan.transport;
if (hasTunnels && effectiveTransport === "tailscale" && tunnelPlan.tailscale) {
try {
const rewritten = await startTailscaleTunnels(devOutput, tunnelPlan.tailscale);
const dead = await firstUnroutableTunnel(rewritten.tunnels);
if (dead) {
throw new Error(
`forwarder for '${dead}' bound but does not route the VPC (dead SOCKS upstream or non-routing tailnet)`
);
}
devOutput = rewritten;
logInfo("Connected: userspace-Tailscale tunnels for VPC resources (shared per tenant).");
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
if (tunnelPlan.explicit) {
logError(`Tailscale transport failed: ${reason}`);
logInfo(
"If a VPN registration URL was shown above, open it to authorize this machine, then re-run."
);
logInfo("Or switch backing explicitly: mesh dev --transport ssm");
killSession(sessionName);
process.exit(1);
}
logWarn(`Tailscale transport unavailable (${reason}) \u2014 falling back to SSM tunnels.`);
effectiveTransport = "ssm";
}
}
if (hasTunnels && effectiveTransport === "ssm") {
devOutput = await startSsmTunnels(sessionName, devOutput);
}
if (hasTunnels && effectiveTransport !== "vpn-direct") {
for (const [tName, tunnel] of Object.entries(devOutput.tunnels)) {
if (tunnel.host !== "localhost") continue;
logInfo(`Waiting for tunnel ${tName} (localhost:${tunnel.port})...`);
const ready = await waitForPort("localhost", tunnel.port, TUNNEL_READY_TIMEOUT_MS);
if (!ready) {
logError(
`Tunnel '${tName}' did not become ready on localhost:${tunnel.port} within ${TUNNEL_READY_TIMEOUT_MS / 1e3}s.`
);
logInfo(` Check the tunnel window for errors: tmux attach -t ${sessionName} (window tunnel-${tName})`);
logInfo(" Common causes: expired AWS credentials, bastion stopped, session-manager-plugin errors.");
throw new Error(
`SSM tunnel '${tName}' not ready on localhost:${tunnel.port} after ${TUNNEL_READY_TIMEOUT_MS / 1e3}s`
);
}
logSuccess(` Tunnel ready: ${tName} (localhost:${tunnel.port})`);
}
}
const tunnelNames = Object.keys(devOutput.tunnels);
const subdomainCtx = await setupSubdomainRouting(devOutput, appRoot);
if (tunnelNames.length > 0) {
const isSSM = Object.values(devOutput.tunnels).some(
(t) => t.host === "localhost" && t.port >= SSM_TUNNEL_BASE_PORT
);
logSuccess(isSSM ? "SSM tunnel connections:" : "VPN direct connections:");
for (const [tName, tunnel] of Object.entries(devOutput.tunnels)) {
if (subdomainCtx) {
logInfo(` ${tName} \u2192 http://${tName}.${subdomainCtx.baseDomain}`);
} else {
logInfo(` ${tName} \u2192 ${tunnel.host}:${tunnel.port}`);
}
}
}
let secretEnvVars = {};
if (devOutput.secrets && Object.keys(devOutput.secrets).length > 0) {
secretEnvVars = await resolveSecrets(devOutput.secrets);
}
const tenant = devOutput.platform?.tenant ?? "mesh";
const platformEnv = devOutput.platform?.env ?? "dev";
const isLocalPlatform = tenant === "local";
if (!isLocalPlatform) {
const platformName = resolveHubPlatformName(devOutput.platform);
const kubeconfigPath = await ensureKubeconfig(platformName, platformEnv, sessionName, {
onError: ({ parameter, error }) => logWarn(
`EKS cluster resolve failed (${parameter}): ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`
)
});
if (kubeconfigPath) {
process.env.KUBECONFIG = kubeconfigPath;
setTmuxEnv(sessionName, { KUBECONFIG: kubeconfigPath });
logSuccess(`Kubeconfig resolved from SSM (hub ${platformName}/${platformEnv}) \u2192 ${kubeconfigPath}`);
} else {
logWarn("Could not resolve EKS kubeconfig from SSM; kubectl calls will use ambient config (if any).");
}
}
let temporalAuthVars = {};
if (devOutput.tunnels["temporal"]) {
temporalAuthVars = await resolveTemporalAuth(tenant, platformEnv, devOutput.platform?.name ?? tenant);
const appName = devOutput.app ?? "";
if (appName) {
const encodingKey = resolveTemporalEncodingKey(tenant, platformEnv, appName);
if (encodingKey) {
temporalAuthVars.TEMPORAL_ENCODING_KEY = encodingKey;
}
}
const temporalTunnel = devOutput.tunnels["temporal"];
temporalAuthVars.TEMPORAL_ADDRESS = tunnelClientAddress(temporalTunnel);
}
setTmuxEnv(sessionName, secretEnvVars);
setTmuxEnv(sessionName, temporalAuthVars);
const credContext = devOutput.platform ? isLocalPlatform ? "local" : loginContextFor(devOutput.platform) : null;
const devUserVars = resolveDevUserVars(devOutput);
if (Object.keys(devUserVars).length > 0) {
setTmuxEnv(sessionName, devUserVars);
logSuccess(`Dev user injected from mesh login: ${credContext}`);
const tokenPort = await findFreePort3();
const tokenUrl = `http://127.0.0.1:${tokenPort}`;
setTmuxEnv(sessionName, { DEV_USER_TOKEN_URL: tokenUrl });
execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", "token-server"]);
execFileSync15(
"tmux",
["set-option", "-t", `${sessionName}:token-server`, "remain-on-exit", "on"],
{ stdio: "ignore" }
);
execFileSync15("tmux", [
"send-keys",
"-t",
`${sessionName}:token-server`,
`npx mesh dev __token-server ${tokenPort} ${credContext}`,
"Enter"
]);
for (let i = 0; i < 15 && !await isPortListening(tokenPort); i++) {
await new Promise((r) => setTimeout(r, 200));
}
logSuccess(`Dev-user token-server: ${tokenUrl} (context ${credContext})`);
} else if (credContext) {
logWarn(`No mesh login credentials found for '${credContext}'.`);
logInfo(` Run: mesh login ${credContext}`);
logInfo(" Hub UI will show unauthenticated state without valid credentials.");
}
const monorepoRoot = findMonorepoRoot();
for (const [name, service] of Object.entries(devOutput.services)) {
const cmd = service.command.join(" ");
if (!cmd) {
logInfo(`Skipped: ${name} (no dev command, deployed to K8s)`);
continue;
}
const serviceDir = path20.resolve(appRoot, rebaseServiceSrc(service.src, monorepoRoot));
if (worktreeRoot && fs19.existsSync(serviceDir)) {
const wtRootWithSep = worktreeRoot.endsWith(path20.sep) ? worktreeRoot : worktreeRoot + path20.sep;
const outside = serviceDir !== worktreeRoot && !serviceDir.startsWith(wtRootWithSep);
if (outside && isLinkedDependencyDir(serviceDir, appRoot)) {
logInfo(`${name}: source is a dev-linked package (${serviceDir})`);
} else if (outside) {
logError(
`Refusing to launch '${name}': its source resolved to ${serviceDir}, outside this worktree (${worktreeRoot}), and it is not a package this app resolves there. The stack was likely deployed from another worktree. Re-run \`mesh deploy up\` here, or pass --app.`
);
process.exit(1);
}
}
if (!fs19.existsSync(serviceDir)) {
logWarn(
`Skipped: ${name} (source directory not found: ${serviceDir}${serviceDir !== service.src ? ` \u2014 rebased from ${service.src}` : ""})`
);
continue;
}
const serviceVars = {
...getServiceEnvVars(service, devOutput.tunnels),
...temporalAuthVars,
// Overlay the resolved dev-user identity for the same reason as the
// Temporal vars: the dev output may carry a placeholder (e.g. hub ui's
// DEV_USER_ACCESS_TOKEN="dev-local"), and since the sourced env file
// wins over the tmux session env, the placeholder would clobber the
// real token injected above — silently 401ing tenant-scoped Hub views.
...devUserVars,
// Per-worktree Temporal task-queue isolation: agent
// workers/apps append this to their task-queue names so concurrent
// worktrees don't steal each other's workflow tasks. Empty on the primary
// checkout → omitted → unchanged historical behavior.
...taskQueueSuffix ? { MESH_TASK_QUEUE_SUFFIX: taskQueueSuffix } : {},
// AWS, for the same reason as the Temporal and dev-user overlays above: the
// sourced env file WINS over the tmux session env, so a service that reads this
// file gets whatever the file says and nothing else.
//
// It matters beyond the service's own process. A module dev service can hand
// credentials to something it starts — the workspaces manager gives each
// workspace's sidecar an AWS profile so Claude Code can reach Bedrock — and with
// no AWS_* here it silently has none to give. `buildChildAwsEnv` has already
// chosen the SELF-REFRESHING shape (a profile with `credential_process`, not a
// frozen STS triple), which is exactly what a long-lived child needs.
...awsEnv
};
const envFilePath = getServiceEnvFilePath(sessionName, name);
writeEnvFile(envFilePath, serviceVars);
const launchCmd = buildLaunchCommand(
envFilePath,
serviceDir,
cmd,
service.env?.OTEL_RESOURCE_ATTRIBUTES ? logShipperPath() : void 0
);
execFileSync15("tmux", ["new-window", "-t", sessionName, "-n", name, "-c", serviceDir]);
execFileSync15("tmux", ["set-option", "-t", `${sessionName}:${name}`, "remain-on-exit", "on"], {
stdio: "ignore"
});
execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:${name}`, launchCmd, "Enter"]);
logSuccess(`Started: ${name} (${serviceDir}, port ${service.port})`);
}
if (process.env.DEVCONTAINER === "1") {
try {
await updateDevboxProxy(devOutput, appRoot);
} catch (e) {
logWarn(`Could not update devbox proxy: ${e.message}`);
}
}
const statusCmd = `watch -n2 -t npx mesh dev --status --session '${sessionName}'`;
execFileSync15("tmux", ["send-keys", "-t", `${sessionName}:status`, statusCmd, "Enter"]);
console.log("");
logSuccess(`Dev session started: ${sessionName}`);
console.log("");
if (tunnelNames.length > 0) {
console.log("Connections (VPN direct):");
for (const tName of tunnelNames) {
const t = devOutput.tunnels[tName];
if (subdomainCtx) {
console.log(` ${tName.padEnd(20)} http://${tName}.${subdomainCtx.baseDomain}`);
} else {
console.log(` ${tName.padEnd(20)} ${t.host}:${t.port}`);
}
}
console.log("");
}
console.log("Services:");
for (const name of serviceNames) {
const s = devOutput.services[name];
const hasCmd = s.command && s.command.length > 0;
if (hasCmd) {
if (subdomainCtx && s.port) {
console.log(` ${name.padEnd(20)} http://${name}.${subdomainCtx.baseDomain}`);
} else {
const addr = s.port ? `http://localhost:${s.port}` : "(no port)";
console.log(` ${name.padEnd(20)} ${addr}`);
}
} else {
console.log(` ${name.padEnd(20)} (deployed)`);
}
}
console.log("");
if (!headless) {
logInfo("Attaching to tmux session...");
if (process.env.TMUX) {
spawnSync2("tmux", ["switch-client", "-t", sessionName], { stdio: "inherit" });
} else {
spawnSync2("tmux", ["attach", "-t", sessionName], { stdio: "inherit" });
}
} else {
console.log(`Attach with: tmux attach -t ${sessionName}`);
console.log(`Stop with: mesh dev --kill`);
console.log("");
}
return devOutput;
}
async function showStatus2(sessionName, devOutput, asJson) {
if (!sessionExists(sessionName)) {
if (asJson) {
console.log(JSON.stringify({ running: false, session: sessionName }));
} else {
logInfo(`No active session: ${sessionName}`);
}
return;
}
let windows = [];
try {
const raw = execFileSync15(
"tmux",
["list-windows", "-t", sessionName, "-F", "#{window_name} #{pane_dead}"],
{ encoding: "utf-8" }
);
windows = raw.trim().split("\n");
} catch {
}
const windowStatus = {};
for (const line of windows) {
const [name, dead] = line.split(" ");
if (name) windowStatus[name] = dead === "1" ? "exited" : "running";
}
const tunnelNames = Object.keys(devOutput.tunnels);
const tunnelHealth = await probeTunnelHealth(devOutput.tunnels);
const healthByName = new Map(tunnelHealth.map((h) => [h.name, h]));
if (asJson) {
const services = {};
for (const [name, svc] of Object.entries(devOutput.services)) {
services[name] = {
status: windowStatus[name] ?? "unknown",
port: svc.port,
src: svc.src
};
}
const tunnels = {};
for (const [name, tun] of Object.entries(devOutput.tunnels)) {
tunnels[name] = {
host: tun.host,
port: tun.port,
reachable: healthByName.get(name)?.reachable ?? false
};
}
console.log(
JSON.stringify({ running: true, session: sessionName, services, tunnels }, null, 2)
);
} else {
console.log("\u2500\u2500 mesh dev \u2500\u2500");
console.log("");
console.log(`Session: ${sessionName}`);
if (tunnelNames.length > 0) {
console.log("");
for (const line of formatTunnelHealth(tunnelHealth)) console.log(line);
}
console.log("");
console.log("Services:");
for (const [name, svc] of Object.entries(devOutput.services)) {
const hasCmd = svc.command && svc.command.length > 0;
if (!hasCmd) {
console.log(` \u2601 ${name.padEnd(16)} deployed ${svc.src}`);
continue;
}
const status = windowStatus[name] ?? "unknown";
const icon = status === "running" ? "\u25CF" : status === "exited" ? "\u2717" : "?";
const addr = svc.port ? `:${svc.port}` : "";
console.log(` ${icon} ${name.padEnd(16)} ${status.padEnd(10)} ${addr.padEnd(8)} ${svc.src}`);
}
console.log("");
console.log("Commands:");
console.log(" mesh dev restart <s> Restart a service");
console.log(" mesh dev logs <s> Tail service logs");
console.log(" mesh dev --kill Stop everything");
console.log("");
console.log("tmux: Ctrl+b n/p switch windows, d detach");
}
}
async function restartService(sessionName, serviceName, appRoot, devOutput, awsEnv, opts) {
let service = devOutput.services[serviceName];
if (!service) {
logError(`Unknown service: ${serviceName}`);
logInfo(`Available: ${Object.keys(devOutput.services).join(", ")}`);
process.exit(1);
}
if (!sessionExists(sessionName)) {
logError(`No active session: ${sessionName}. Run 'mesh dev' first.`);
process.exit(1);
}
if (opts?.refreshEnv) {
if (!opts.stack) {
logError("--refresh-env: no stack resolved; keeping existing env and NOT restarting.");
return;
}
const pinned = opts.sessionState?.devOutput ?? devOutput;
let fresh;
try {
fresh = getDevOutput(appRoot, opts.stack, awsEnv);
} catch (err) {
logError(
`--refresh-env: failed to read stack output for '${opts.stack}'. Keeping the existing env file for '${serviceName}' and NOT restarting.`
);
logInfo(String(err?.message ?? err));
return;
}
const merged = mergePinnedPorts(fresh, pinned);
const mergedService = merged.services[serviceName];
if (!mergedService) {
logError(
`--refresh-env: service '${serviceName}' is absent from the fresh stack output. Keeping the existing env file and NOT restarting.`
);
return;
}
const temporalAuthVars = await resolveTemporalAuthVars(sessionName, merged);
const { taskQueueSuffix } = resolveWorktreeIdentity(appRoot);
const serviceVars = {
...getServiceEnvVars(mergedService, merged.tunnels),
...temporalAuthVars,
// Same overlay as launch: without it, --refresh-env rewrites the env
// file from dev output and its DEV_USER_* placeholders clobber the real
// token from `mesh login` (the "restart silently 401s the Hub" trap).
...resolveDevUserVars(merged),
...taskQueueSuffix ? { MESH_TASK_QUEUE_SUFFIX: taskQueueSuffix } : {},
// Same overlay as launch — a refresh that dropped these would take a working
// service's credentials away on restart, which is the hardest kind of change to
// attribute afterwards.
...awsEnv
};
writeEnvFile(getServiceEnvFilePath(sessionName, serviceName), serviceVars);
service = mergedService;
logSuccess(`Regenerated env for '${serviceName}' from current stack/SSM outputs.`);
}
const target = `${sessionName}:${serviceName}`;
try {
execFileSync15("tmux", ["respawn-pane", "-k", "-t", target], { stdio: "ignore" });
} catch {
logError(`Window '${serviceName}' not found in session.`);
process.exit(1);
}
setTmuxEnv(sessionName, awsEnv);
const cmd = service.command.join(" ");
const envFilePath = getServiceEnvFilePath(sessionName, serviceName);
let restartCmd;
if (fs19.existsSync(envFilePath)) {
const serviceDir = path20.resolve(appRoot, rebaseServiceSrc(service.src, findMonorepoRoot()));
restartCmd = buildLaunchCommand(
envFilePath,
serviceDir,
cmd,
service.env?.OTEL_RESOURCE_ATTRIBUTES ? logShipperPath() : void 0
);
} else {
logWarn(
`No launch env file for '${serviceName}' (${envFilePath}) \u2014 session predates env-file launches.`
);
logWarn("Falling back to reconstructed env; restart `mesh dev` for a faithful environment.");
const serviceVars = getServiceEnvVars(service, devOutput.tunnels);
restartCmd = `${envPrefix(serviceVars)}${cmd}`;
}
execFileSync15("tmux", ["send-keys", "-t", target, restartCmd, "Enter"]);
logSuccess(`Restarted: ${serviceName}`);
}
function showLogs(sessionName, serviceName, tail) {
if (!sessionExists(sessionName)) {
logError(`No active session: ${sessionName}. Run 'mesh dev' first.`);
process.exit(1);
}
const target = `${sessionName}:${serviceName}`;
try {
const result = execFileSync15("tmux", ["capture-pane", "-t", target, "-p", "-S", `-${tail}`], {
encoding: "utf-8"
});
process.stdout.write(result);
} catch {
logError(`Could not capture logs for '${serviceName}'.`);
logInfo("Is the service name correct? Check with: mesh dev --status");
process.exit(1);
}
}
function buildDoctorContext(appRoot, stack, sessionName) {
const state = loadSessionState(sessionName);
const defaultDeployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
const adminDeployerRole = readStackConfig(appRoot, stack, "mesh:adminDeployerRole");
const platformContext = derivePlatformContext(appRoot, stack);
let deployerRole = defaultDeployerRole;
if (defaultDeployerRole) {
const meshCreds = platformContext ? readCredentials(platformContext) : null;
const idToken = meshCreds && new Date(meshCreds.expiresAt) > /* @__PURE__ */ new Date() ? meshCreds.idToken : null;
deployerRole = selectRoleForCaller(idToken, {
defaultRole: defaultDeployerRole,
adminRole: adminDeployerRole ?? void 0
});
}
return {
appRoot,
stack,
sessionName,
deployerRole,
platformContext,
credMethod: null,
sessionState: state
};
}
function registerDevCommand(program2) {
const dev = program2.command("dev").description("Start local dev environment (reads Pulumi stack outputs)").option("--app <path>", "Path to a Pulumi app (relative to monorepo root or cwd)").option("--stack <stack>", "Pulumi stack name (default: auto-detect)").addOption(new Option("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--headless", "Start without attaching to tmux").option("--kill", "Kill existing dev session").option("--status", "Show service status").option("--json", "Output status as JSON (with --status)").option(
"--session <name>",
"Override the tmux session name (auto-derived per git worktree by default \u2014 omit to keep concurrent worktrees isolated)"
).option("--dry-run", "Print the resolved worktree/session/port plan and exit without launching").option("--force", "Relaunch even if workflow code changed since launch (may strand in-flight conversations)").option("--profile <name>", "AWS SSO profile to use (e.g., mesh-dev)").addOption(
new Option(
"--transport <mode>",
"Tunnel backing (default: auto \u2014 Tailscale when available, else SSM/VPN)"
).choices(["auto", "ssm", "tailscale"])
).option(
"--local",
"Run against the local Mesh platform from `mesh start` (no AWS, no VPN, no Pulumi state). Auto-selected when the app has no Pulumi.yaml."
).option(
"--externals [names]",
"Also realize the app's declared external services (package.json \u2192 mesh.externals): all of them, or a comma-separated subset of name[=mode] entries. Modes: mock (emulate \u2014 OpenAPI spec via Prism, or a mock process), local (a local version via docker compose, e.g. a vendor DB replica), remote (connect to the actual service \u2014 vendor sandbox credentials, or the external configured in the app's tenant environment). name=mode overrides the declaration's default for this run (e.g. plaid-db=remote). Each realization seeds the ExternalService credential secret so resolveCredentials() runs unchanged. Local mode only."
).option("--mock [names]", "Alias for --externals.").option(
"--runner <runner>",
"Process runner for local mode: 'tmux' (default \u2014 dev machines) or 'docker' (CI/headless: services run as a docker compose project with host networking; Linux semantics).",
"tmux"
);
dev.command("__token-server <port> <context>", { hidden: true }).action(async (port, context) => {
const { startTokenServer: startTokenServer2 } = await Promise.resolve().then(() => (init_dev_token_server(), dev_token_server_exports));
await startTokenServer2(Number(port), context);
});
dev.action(async (options) => {
if (options.profile) {
applyAwsProfileOverride(options.profile);
}
if (options.session && (options.status || options.kill)) {
const sessionName2 = options.session;
if (loadSessionState(sessionName2)?.runner === "docker") {
if (options.kill) {
dockerDevDown(sessionName2);
composeExternalsDown(loadSessionState(sessionName2)?.composeExternals);
localProbesRemove(loadSessionState(sessionName2)?.externalProbeFiles);
removeSessionState(sessionName2);
logSuccess(`Killed docker dev session: ${sessionName2}`);
} else {
console.log(dockerDevPs(sessionName2));
}
return;
}
if (options.kill) {
composeExternalsDown(loadSessionState(sessionName2)?.composeExternals);
localProbesRemove(loadSessionState(sessionName2)?.externalProbeFiles);
try {
const devOutput2 = loadSessionState(sessionName2)?.devOutput;
if (devOutput2) {
const tsInfo = await getTailscaleInfo();
if (tsInfo) {
const appName = devOutput2.app ?? sessionName2.replace(/-dev$/, "");
const stack2 = devOutput2.stack ?? "dev";
const baseDomain = `${appName}.${stack2}.${tsInfo.hostname}.vpn.internal`;
const platform = devOutput2.platform ?? { tenant: "mesh", env: "dev" };
unregisterDnsRecords(headscaleDnsConfig(platform.name ?? platform.tenant, platform.env), baseDomain);
}
}
} catch {
}
if (sessionExists(sessionName2)) {
killSession(sessionName2);
logSuccess(`Killed session: ${sessionName2}`);
} else {
logInfo(`No active session: ${sessionName2}`);
}
removeSessionState(sessionName2);
return;
}
const state = loadSessionState(sessionName2);
if (state) {
await showStatus2(sessionName2, state.devOutput, !!options.json);
return;
}
}
const appRoot = findAppRoot2(options.app);
const projectName = getProjectName(appRoot);
const worktree = withAppScopedPortBlock(resolveWorktreeIdentity(appRoot), appRoot);
const sessionName = options.session ?? deriveSessionName(projectName, worktree);
if (options.kill) {
composeExternalsDown(loadSessionState(sessionName)?.composeExternals);
localProbesRemove(loadSessionState(sessionName)?.externalProbeFiles);
if (loadSessionState(sessionName)?.runner === "docker") {
dockerDevDown(sessionName);
removeSessionState(sessionName);
logSuccess(`Killed docker dev session: ${sessionName}`);
return;
}
const killTsTenant = loadSessionState(sessionName)?.devOutput.platform?.name ?? "mesh";
try {
const devOutput2 = loadSessionState(sessionName)?.devOutput;
if (devOutput2) {
const tsInfo = await getTailscaleInfo();
if (tsInfo) {
const appName = devOutput2.app ?? projectName;
const stack2 = devOutput2.stack ?? "dev";
const baseDomain = `${appName}.${stack2}.${tsInfo.hostname}.vpn.internal`;
const platform = devOutput2.platform ?? { tenant: "mesh", env: "dev" };
unregisterDnsRecords(headscaleDnsConfig(platform.name ?? platform.tenant, platform.env), baseDomain);
}
}
} catch {
}
if (sessionExists(sessionName)) {
killSession(sessionName);
logSuccess(`Killed session: ${sessionName}`);
} else {
logInfo(`No active session: ${sessionName}`);
}
removeSessionState(sessionName);
if (readTunnelState(killTsTenant)) {
logInfo(
`VPN tunnels persist across sessions \u2014 stop them with: mesh vpn tunnel down --tenant ${killTsTenant}`
);
}
return;
}
const localMode = !!options.local || !hasStackBacking(appRoot);
if (localMode) {
if (options.status) {
const state = loadSessionState(sessionName);
if (state?.runner === "docker") {
console.log(dockerDevPs(sessionName));
return;
}
const statusOutput = state?.devOutput ?? buildLocalDevOutput(appRoot, detectLocalTenant(appRoot), { mocks: {} });
showStatus2(sessionName, statusOutput, !!options.json);
return;
}
const dockerRunner = options.runner === "docker";
if (!dockerRunner && !hasTmux()) {
logError("tmux is not installed. Fix: brew install tmux (or use --runner docker)");
process.exit(1);
}
await ensureLocalPlatformRunning();
const localTenant = detectLocalTenant(appRoot);
logInfo(`Project: ${projectName}, Stack: local (mesh start platform)`);
const externalsRequest = options.externals ?? options.mock;
let selectedMocks = {};
const requestedExplicitly = /* @__PURE__ */ new Set();
if (externalsRequest) {
const declared = readLocalMocks(appRoot);
let requested;
let overrides = /* @__PURE__ */ new Map();
if (externalsRequest === true) {
requested = Object.keys(declared);
} else {
({ names: requested, overrides } = parseExternalsSelection(String(externalsRequest)));
for (const name of requested) requestedExplicitly.add(name);
}
const unknown = requested.filter((name) => !declared[name]);
if (unknown.length > 0) {
throw new MeshCliError(
`Unknown external(s): ${unknown.join(", ")} \u2014 declared in package.json mesh.externals: ${Object.keys(declared).join(", ") || "(none)"}`,
{ remediation: { docs: 'package.json \u2192 "mesh": { "externals": { \u2026 } }' } }
);
}
if (requested.length === 0) {
logWarn("No externals declared (package.json \u2192 mesh.externals) \u2014 continuing without.");
}
selectedMocks = Object.fromEntries(
requested.map((name) => [
name,
overrides.has(name) ? { ...declared[name], mode: overrides.get(name) } : declared[name]
])
);
for (const [name, decl] of Object.entries(selectedMocks)) externalMode(name, decl);
}
const rawDevOutput2 = buildLocalDevOutput(appRoot, localTenant, {
mocks: selectedMocks
});
if (options.dryRun) {
printDevPlan(sessionName, appRoot, worktree, await allocatePorts(rawDevOutput2, worktree));
for (const [name, decl] of Object.entries(selectedMocks)) {
const mode = externalMode(name, decl);
if (mode === "local") {
console.log(` external ${name.padEnd(18)} local \u2014 docker compose (${decl.compose}) \u2192 localhost:${decl.port}`);
} else if (mode === "remote") {
console.log(` external ${name.padEnd(18)} remote \u2014 actual service credentials (no local process)`);
}
}
return;
}
let signInServices = [];
let appVersion;
try {
const tenant = localTenant;
const app = rawDevOutput2.app ?? projectName;
logInfo(`Provisioning auth config for tenant '${tenant}', app '${app}'\u2026`);
const services = Object.keys(rawDevOutput2.services);
const authServices = services.filter((name) => !name.startsWith("mock-"));
let authRoles = [];
signInServices = [];
try {
const appPkg = JSON.parse(
fs19.readFileSync(path20.join(appRoot, "package.json"), "utf-8")
);
if (Array.isArray(appPkg?.mesh?.auth?.roles)) {
authRoles = appPkg.mesh.auth.roles.filter((r) => typeof r === "string");
}
if (Array.isArray(appPkg?.mesh?.auth?.signIn)) {
signInServices = appPkg.mesh.auth.signIn.filter((r) => typeof r === "string");
}
if (typeof appPkg?.version === "string") appVersion = appPkg.version;
} catch {
}
await ensureAppTenantAuth({ tenant, app, services: authServices, roles: authRoles });
await ensureTemporalNamespace(localAppNamespace(tenant, app));
} catch (err) {
logWarn(
`Auth auto-provisioning skipped: ${err instanceof Error ? err.message : err} \u2014 if the local Zitadel predates seeding, run: mesh stop --destroy && mesh start`
);
}
const devOutput2 = await allocatePorts(rawDevOutput2, worktree);
for (const service of signInServices) {
const port = devOutput2.services[service]?.port;
if (!port) {
logWarn(`mesh.auth.signIn names '${service}', which this app does not run \u2014 no sign-in app registered.`);
continue;
}
try {
await ensureSignInApp({
tenant: localTenant,
app: devOutput2.app ?? projectName,
service,
baseUrl: `http://localhost:${port}`
});
} catch (err) {
logWarn(
`Could not register the browser sign-in for '${service}' (${err instanceof Error ? err.message : err}) \u2014 the Hub's Access \u2192 Sign-in tab will report this app has no login.`
);
}
}
try {
await registerLocalApp({
tenant: localTenant,
app: devOutput2.app ?? projectName,
version: appVersion,
services: Object.keys(devOutput2.services),
ports: Object.fromEntries(
Object.entries(devOutput2.services).map(([name, svc]) => [name, svc.port])
),
kinds: Object.fromEntries(
Object.entries(devOutput2.services).flatMap(
([name, svc]) => svc.kind ? [[name, svc.kind]] : []
)
),
// ExternalService.link() parity: consuming services carry the
// external names, so the Hub shows consumers + per-app uptime.
links: Object.values(selectedMocks).map((decl) => decl.external).filter((n) => !!n)
});
} catch (err) {
logWarn(`Local registry registration skipped: ${err instanceof Error ? err.message : err}`);
}
const composeExternals = [];
const externalProbeFiles = [];
for (const [name, decl] of Object.entries(selectedMocks)) {
const mode = externalMode(name, decl);
try {
let probeFile;
if (mode === "local") {
logInfo(`Starting docker external '${name}' (${decl.compose})\u2026`);
const composeRef = await composeExternalUp(appRoot, sessionName, name, decl);
if (composeRef) composeExternals.push(composeRef);
probeFile = await seedLocalMock({
tenant: localTenant,
app: devOutput2.app ?? projectName,
name,
decl,
endpoint: { url: `http://localhost:${decl.port}`, host: "localhost", port: decl.port }
});
} else if (mode === "remote") {
probeFile = await seedLocalMock({
tenant: localTenant,
app: devOutput2.app ?? projectName,
name,
decl
});
} else {
const mockService = devOutput2.services[`mock-${name}`];
if (!mockService) continue;
probeFile = await seedLocalMock({
tenant: localTenant,
app: devOutput2.app ?? projectName,
name,
decl,
endpoint: {
url: `http://localhost:${mockService.port}`,
host: "localhost",
port: mockService.port
}
});
}
if (probeFile) externalProbeFiles.push(probeFile);
} catch (err) {
if (mode === "remote" && !requestedExplicitly.has(name) && err instanceof MeshCliError) {
logWarn(
`External '${name}': skipped \u2014 ${err.message} (re-run with \`mesh dev --externals ${name}=remote\` to make this fatal).`
);
continue;
}
if (err instanceof MeshCliError) throw err;
if (mode === "local") {
throw new MeshCliError(
`Docker external '${name}' failed to start: ${err instanceof Error ? err.message : err}`,
{ remediation: { command: `docker compose -f ${decl.compose} up # debug it directly` } }
);
}
logWarn(`External '${name}' credential seeding failed: ${err instanceof Error ? err.message : err}`);
}
}
if (dockerRunner) {
const composePath = writeDevCompose(
sessionName,
appRoot,
devOutput2.services
);
logInfo(`Docker runner: ${composePath}`);
dockerDevUp(sessionName);
registerServiceProbes(devOutput2, localTenant, externalProbeFiles);
saveSessionState(sessionName, {
appRoot,
stack: "local",
devOutput: devOutput2,
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
runner: "docker",
composeExternals,
externalProbeFiles
});
console.log("");
logSuccess(`Dev session started (docker): ${sessionName}`);
console.log("");
console.log("Services:");
for (const [name, service] of Object.entries(devOutput2.services)) {
console.log(` ${name.padEnd(20)} http://localhost:${service.port}`);
}
console.log("");
console.log(`Status with: mesh dev --status --session '${sessionName}'`);
console.log("Stop with: mesh dev --kill");
return;
}
const finalDevOutput2 = await startServices(
sessionName,
appRoot,
devOutput2,
!!options.headless,
localAwsEnv(),
// ministack wiring — the only "AWS" children need
{ transport: "vpn-direct" }
// local mode: no tunnels
);
registerServiceProbes(finalDevOutput2, localTenant, externalProbeFiles);
saveSessionState(sessionName, {
appRoot,
stack: "local",
devOutput: finalDevOutput2,
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
runner: "tmux",
composeExternals,
externalProbeFiles
});
return;
}
if (options.mock || options.externals) {
logWarn(
"--externals/--mock is local-mode only for now (tethered dev outputs do not carry external declarations yet). Continuing without them."
);
}
const stack = detectStack(appRoot, resolveStackOption(options));
logInfo(`Project: ${projectName}, Stack: ${stack}`);
const monorepoRoot = findMonorepoRoot();
const looksLikeStandaloneApp = ["api", "worker", "web"].some(
(dir) => fs19.existsSync(path20.join(appRoot, dir, "package.json"))
);
if (monorepoRoot && path20.resolve(appRoot) === path20.resolve(monorepoRoot) && !looksLikeStandaloneApp) {
logError(`mesh dev should be run from an app directory, not the monorepo root.
`);
logInfo("Try:");
logInfo(" cd apps/hub && mesh dev");
logInfo("");
logInfo("Or specify the app path:");
logInfo(" mesh dev --app apps/hub");
logInfo("");
logInfo("List available apps:");
logInfo(" mesh dev list");
process.exit(1);
}
const issues = [];
const profileHint = options.profile ?? process.env.AWS_PROFILE ?? "mesh-dev";
const platformContext = derivePlatformContext(appRoot, stack);
const defaultDeployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
const adminDeployerRole = readStackConfig(appRoot, stack, "mesh:adminDeployerRole");
let deployerRole = defaultDeployerRole;
if (defaultDeployerRole) {
const meshCreds = platformContext ? readCredentials(platformContext) : null;
const idToken = meshCreds && new Date(meshCreds.expiresAt) > /* @__PURE__ */ new Date() ? meshCreds.idToken : null;
deployerRole = selectRoleForCaller(idToken, {
defaultRole: defaultDeployerRole,
adminRole: adminDeployerRole ?? void 0
});
if (adminDeployerRole && deployerRole === adminDeployerRole) {
logInfo(`Caller has admin Zitadel role \u2014 assuming ${deployerRole.split("/").pop()} (admin variant)`);
}
}
let awsEnv;
let credMethod = "ambient";
let credMethodForDoctor = "ambient";
if (deployerRole) {
const resolved = await resolveAwsCredentials(deployerRole, appRoot, stack);
if (!resolved) {
awsEnv = {};
credMethodForDoctor = null;
} else {
awsEnv = resolved.env;
credMethod = resolved.method;
credMethodForDoctor = resolved.method;
}
} else {
awsEnv = getAwsEnvVars();
credMethodForDoctor = "ambient";
}
const vpnConnected = await isVpnConnected();
let transport = resolveTransport(options.transport, {
vpnConnected,
hasSsmPlugin: hasSessionManagerPlugin(),
tailscaleAvailable: tailscaleAvailable()
});
if (options.transport === "tailscale" && !tailscaleAvailable()) {
issues.push(
" \u2718 --transport=tailscale but the tailscaled binary is missing.\n Fix: brew install tailscale"
);
}
if (transport === "ssm") {
if (hasSessionManagerPlugin()) {
if (!vpnConnected) {
logWarn("VPN not connected \u2014 will use SSM port-forwarding tunnels for VPC resources.");
}
logInfo(
" Tunnels use the 2XXXX port range (e.g., PostgreSQL on 25432, Temporal on 27233)."
);
logInfo(" Limitations: no kubectl exec into pods, no internal ingress/ALB access.");
logInfo(" For full VPC access, connect VPN: mesh vpn connect");
} else {
issues.push(
" \u2718 VPN is not connected and session-manager-plugin is not installed.\n Option 1 (VPN): mesh vpn connect\n Option 2 (SSM): brew install --cask session-manager-plugin\n Option 3 (Tailscale): mesh dev --transport=tailscale"
);
}
} else if (transport === "tailscale") {
logInfo("Bringing up userspace-Tailscale tunnels for VPC resources (shared per tenant)\u2026");
}
const preflightCtx = {
appRoot,
stack,
sessionName,
deployerRole,
platformContext,
credMethod: credMethodForDoctor,
sessionState: null
};
const preflight = await runChecks(preflightCtx, "preflight", ALL_CHECKS);
const preflightReport = renderHuman(preflight);
if (preflightReport) console.log(preflightReport);
const preflightStatus = aggregateStatus(preflight.map((r) => r.result));
if (preflightStatus === "error" || issues.length > 0) {
logError("\nPrerequisites not met. Fix the above before running mesh dev, then retry: mesh dev");
for (const issue of issues) console.log(issue);
process.exit(1);
}
const awsRegion = readStackConfig(appRoot, stack, "aws:region");
if (awsRegion && !awsEnv.AWS_REGION) {
awsEnv.AWS_REGION = awsRegion;
}
Object.assign(process.env, awsEnv);
const zitadelOpts = credMethod === "zitadel" && deployerRole ? (() => {
const context = derivePlatformContext(appRoot, stack);
return context ? {
context,
roleArn: deployerRole,
sessionScratchDir: getSessionEnvDir(sessionName),
meshBin: resolveStableMeshBin(process.argv[1])
} : void 0;
})() : void 0;
const childAwsEnv = buildChildAwsEnv(awsEnv, credMethod, profileHint, zitadelOpts);
let rawDevOutput;
try {
rawDevOutput = getDevOutput(appRoot, stack, awsEnv);
} catch (err) {
if (err instanceof MissingStackOutputError) {
logError(`No Pulumi stack output for '${stack}' \u2014 mesh dev has nothing to run.`);
logInfo("If this is a new app, initialize and materialize it first:");
logInfo(" mesh stack init # create your personal dev stack");
logInfo(` mesh deploy up --stack ${stack} --yes # produce the app output mesh dev reads`);
logInfo(`Then: mesh dev --stage ${stack} (the stack must export an \`app\` or \`dev\` output.)`);
logInfo("No deployed stack yet? Run against the local platform: mesh dev --local (needs `mesh start`).");
const cause = err.cause?.message;
if (cause) logInfo(`(underlying stack-read error: ${cause})`);
process.exit(1);
}
throw err;
}
if (options.status) {
const state = loadSessionState(sessionName);
await showStatus2(sessionName, state?.devOutput ?? rawDevOutput, !!options.json);
return;
}
const devOutput = await allocatePorts(rawDevOutput, worktree);
transport = await gateVpnDirectReachability(transport, devOutput);
if (options.dryRun) {
printDevPlan(sessionName, appRoot, worktree, devOutput);
return;
}
let tunnelPlan = { transport };
if (transport === "tailscale") {
const tsTenant = devOutput.platform?.name ?? "mesh";
const tsEnv = devOutput.platform?.env ?? "dev";
const tsContext = derivePlatformContext(appRoot, stack) ?? `${tsTenant}.${tsEnv}`;
const loginServer = deriveLoginServer2(tsContext);
if (!loginServer) {
const msg = `No login config for context '${tsContext}' \u2014 run: mesh login ${tsContext}`;
if (options.transport === "tailscale") {
logError(msg);
process.exit(1);
}
const legacy = vpnConnected ? "vpn-direct" : hasSessionManagerPlugin() ? "ssm" : "vpn-direct";
const gatedLegacy = await gateVpnDirectReachability(legacy, devOutput);
logWarn(`${msg} \u2014 falling back to ${gatedLegacy} transport.`);
tunnelPlan = { transport: gatedLegacy };
} else {
const prior = readTunnelState(tsTenant);
const daemonUp = daemonState(tsTenant).backendState !== "Down";
const socksPort = daemonUp ? readDaemonMeta(tsTenant)?.socksPort ?? prior?.socksPort ?? await findFreePort3() : await findFreePort3();
let preAuthKey;
const brokerUrl = resolveVpnJoinBroker(tsContext);
if (brokerUrl) {
const minted = await mintPreAuthKey(tsContext, brokerUrl, { getValidToken });
if (minted?.authKey) {
preAuthKey = minted.authKey;
} else {
logInfo("zero-touch VPN join unavailable, falling back to browser registration");
}
}
tunnelPlan = {
transport,
explicit: options.transport === "tailscale",
tailscale: {
tenant: tsTenant,
env: tsEnv,
region: awsRegion ?? "us-east-2",
loginServer,
socksPort,
preAuthKey
}
};
}
}
if (workflowChangeWouldStrand(loadSessionState(sessionName), appRoot, !!options.force)) {
process.exit(1);
}
const finalDevOutput = await startServices(
sessionName,
appRoot,
devOutput,
!!options.headless,
childAwsEnv,
tunnelPlan,
worktree.worktreeRoot,
worktree.taskQueueSuffix
);
saveSessionState(sessionName, {
appRoot,
stack,
devOutput: finalDevOutput,
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
workflowFingerprint: fingerprintWorkflowSource(appRoot, finalDevOutput.services)
});
});
dev.command("logs <service>").description("Show logs for a service").option("--tail <lines>", "Number of lines", "100").action((service, opts) => {
const sessionName = dev.opts().session;
const resolvedSession = sessionName ?? `${getProjectName(findAppRoot2())}-dev`;
if (loadSessionState(resolvedSession)?.runner === "docker") {
console.log(dockerDevLogs(resolvedSession, service, parseInt(opts.tail, 10)));
return;
}
if (sessionName) {
showLogs(sessionName, service, parseInt(opts.tail, 10));
} else {
const appRoot = findAppRoot2();
const projectName = getProjectName(appRoot);
showLogs(`${projectName}-dev`, service, parseInt(opts.tail, 10));
}
});
dev.command("restart <service>").description("Restart a service").option("--stack <stack>", "Pulumi stack name").addOption(new Option("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option(
"--refresh-env",
"Regenerate this service's env from current stack/SSM outputs before restarting (for config/deploy changes)"
).option("--force", "Restart the worker even if workflow code changed since launch (may strand in-flight conversations)").action(async (service, opts) => {
let ctx = null;
try {
const parentSession = dev.opts().session;
const parentProfile = dev.opts().profile;
if (parentProfile) {
applyAwsProfileOverride(parentProfile);
}
const appRoot = findAppRoot2();
const projectName = getProjectName(appRoot);
const sessionName = parentSession ?? `${projectName}-dev`;
const state = loadSessionState(sessionName);
if (state?.runner === "docker") {
dockerDevRestart(sessionName, service);
logSuccess(`Restarted: ${service}`);
return;
}
if (/worker/i.test(service) && workflowChangeWouldStrand(state, appRoot, !!opts.force)) {
process.exit(1);
}
const stack = state?.stack ?? detectStack(appRoot, resolveStackOption(opts));
ctx = derivePlatformContext(appRoot, stack);
const defaultDeployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
const adminDeployerRole = readStackConfig(appRoot, stack, "mesh:adminDeployerRole");
let deployerRole = defaultDeployerRole;
if (defaultDeployerRole) {
const meshCreds = ctx ? readCredentials(ctx) : null;
const idToken = meshCreds && new Date(meshCreds.expiresAt) > /* @__PURE__ */ new Date() ? meshCreds.idToken : null;
deployerRole = selectRoleForCaller(idToken, {
defaultRole: defaultDeployerRole,
adminRole: adminDeployerRole ?? void 0
});
}
const resolved = deployerRole ? await resolveAwsCredentials(deployerRole, appRoot, stack) : null;
if (deployerRole && !resolved && !process.env.AWS_ACCESS_KEY_ID) {
const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx) : null;
logError(
pf?.message ?? `Couldn't resolve AWS credentials to restart '${service}'. Run: mesh login ${ctx ?? "mesh.dev"} --device`
);
process.exit(1);
}
const baseAwsEnv = resolved?.env ?? getAwsEnvVars();
const awsRegion = readStackConfig(appRoot, stack, "aws:region");
if (awsRegion && !baseAwsEnv.AWS_REGION) {
baseAwsEnv.AWS_REGION = awsRegion;
}
Object.assign(process.env, baseAwsEnv);
const profileHint = parentProfile ?? process.env.AWS_PROFILE ?? "mesh-dev";
const zitadelOpts = resolved?.method === "zitadel" && deployerRole && ctx ? {
context: ctx,
roleArn: deployerRole,
sessionScratchDir: getSessionEnvDir(sessionName),
meshBin: resolveStableMeshBin(process.argv[1])
} : void 0;
const awsEnv = buildChildAwsEnv(baseAwsEnv, resolved?.method ?? "ambient", profileHint, zitadelOpts);
const devOutput = state?.devOutput ?? getDevOutput(appRoot, stack, baseAwsEnv);
await restartService(sessionName, service, appRoot, devOutput, awsEnv, {
refreshEnv: !!opts.refreshEnv,
stack,
sessionState: state
});
if (/worker/i.test(service) && state) {
saveSessionState(sessionName, {
...state,
workflowFingerprint: fingerprintWorkflowSource(appRoot, state.devOutput?.services)
});
}
} catch (err) {
logError(`Failed to restart '${service}': ${err?.message ?? String(err)}`);
logInfo(
`If this is a credential/kubeconfig issue, run \`mesh login ${ctx ?? "<your platform context>"} --device\`, or \`mesh dev\` to relaunch the session cleanly.`
);
process.exit(1);
}
});
dev.command("doctor").description(
"Diagnose the dev session (creds, tunnels, config, ports, worktree, Temporal) \u2014 names the fix"
).option("--stack <stack>", "Pulumi stack name").addOption(new Option("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--json", "Machine-readable output").action(async (opts) => {
const parentProfile = dev.opts().profile;
if (parentProfile) {
applyAwsProfileOverride(parentProfile);
}
const appRoot = findAppRoot2();
const projectName = getProjectName(appRoot);
const sessionName = dev.opts().session ?? `${projectName}-dev`;
const state = loadSessionState(sessionName);
const stack = state?.stack ?? detectStack(appRoot, resolveStackOption(opts));
const asJson = !!opts.json || !!dev.opts().json;
const ctx = buildDoctorContext(appRoot, stack, sessionName);
const status = await runDoctor(ctx, { json: asJson });
process.exit(status === "error" ? 1 : 0);
});
dev.command("list").description("List Pulumi apps in the monorepo").action(() => {
const asJson = dev.opts().json;
const mono = findMonorepoRoot();
if (!mono) {
logError("Could not find monorepo root (pnpm-workspace.yaml or .git)");
process.exit(1);
}
const apps = discoverApps(mono);
if (asJson) {
const result = apps.map((app) => {
const sessionName = `${app.name}-dev`;
return {
...app,
running: sessionExists(sessionName),
session: sessionName
};
});
console.log(JSON.stringify(result, null, 2));
} else {
if (apps.length === 0) {
logInfo("No Pulumi apps found.");
return;
}
console.log("Apps in monorepo:");
console.log("");
for (const app of apps) {
const sessionName = `${app.name}-dev`;
const running = sessionExists(sessionName);
const icon = running ? "\u25CF" : "\u25CB";
const stacks = app.stacks.length > 0 ? ` (${app.stacks.join(", ")})` : "";
console.log(
` ${icon} ${app.name.padEnd(24)} ${app.tenant.padEnd(12)} ${app.relativePath}${stacks}`
);
}
console.log("");
}
});
dev.command("test-user [name]").description("Get Temporal test user credentials (from Pulumi-managed test users)").option("--tenant <tenant>", "Tenant name", "mesh").option("--env <env>", "Environment", "dev").option("--region <region>", "AWS region", "us-east-2").action(
async (name, opts) => {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3 } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5({ region: opts.region });
const basePath = `/mesh-platform/${opts.tenant}/${opts.env}/temporal/test-users`;
if (!name) {
try {
const resp = await ssm.send(
new GetParametersByPathCommand3({
Path: basePath,
Recursive: true,
WithDecryption: true
})
);
if (!resp.Parameters || resp.Parameters.length === 0) {
logWarn(`No test users found at ${basePath}`);
logInfo(
"Test users are defined in Pulumi config under mesh:temporal.authorization.testUsers"
);
logInfo("They're only available on dev stacks.");
return;
}
console.log("Temporal test users:");
console.log("");
for (const param of resp.Parameters) {
const userName = param.Name.split("/").pop();
const creds = JSON.parse(param.Value);
console.log(` ${userName}`);
console.log(` Email: ${creds.email}`);
console.log(` Password: ${creds.password}`);
console.log(` Roles: ${creds.roles.join(", ")}`);
if (creds.description) console.log(` Note: ${creds.description}`);
console.log("");
}
logInfo("Login at: https://temporal.dev.mesh-platform.trabian.com");
} catch (err) {
logError(
`Failed to list test users: ${err instanceof Error ? err.message : String(err)}`
);
process.exit(1);
}
} else {
try {
const resp = await ssm.send(
new GetParameterCommand2({
Name: `${basePath}/${name}`,
WithDecryption: true
})
);
if (!resp.Parameter?.Value) {
logError(`Test user '${name}' not found at ${basePath}/${name}`);
return;
}
const creds = JSON.parse(resp.Parameter.Value);
const asJson = dev.opts().json;
if (asJson) {
console.log(JSON.stringify(creds, null, 2));
} else {
console.log(`Email: ${creds.email}`);
console.log(`Password: ${creds.password}`);
console.log(`Roles: ${creds.roles.join(", ")}`);
if (creds.description) console.log(`Note: ${creds.description}`);
console.log("");
logInfo("Login at: https://temporal.dev.mesh-platform.trabian.com");
}
} catch (err) {
const msg = err.message ?? String(err);
if (msg.includes("ParameterNotFound") || err.name === "ParameterNotFound") {
logError(`Test user '${name}' not found.`);
logInfo(`List available users with: mesh dev test-user`);
} else {
logError(`Failed to get test user: ${msg}`);
}
process.exit(1);
}
}
}
);
}
var MissingStackOutputError, AWS_STATIC_CREDENTIAL_ENV_KEYS, SSM_TUNNEL_BASE_PORT, TUNNEL_READY_TIMEOUT_MS;
var init_dev = __esm({
"libs/mesh-cli/src/commands/dev.ts"() {
"use strict";
init_utils();
init_aws_auth();
init_login();
init_vpn_join();
init_pulumi_run();
init_tailscale();
init_pulumi();
init_kubeconfig();
init_temporal_auth();
init_reachability();
init_workflow_fingerprint();
init_dev_launch();
init_dev_local();
init_auth_provision();
init_seed();
init_mocks();
init_dev_local();
init_stack();
init_docker_runner();
init_errors();
init_stack_flag();
init_peer_addressing();
init_dev_doctor();
init_worktree_identity();
MissingStackOutputError = class extends Error {
constructor(stackName, cause) {
super(`No Pulumi stack output for '${stackName}'`, { cause });
this.stackName = stackName;
this.name = "MissingStackOutputError";
}
};
AWS_STATIC_CREDENTIAL_ENV_KEYS = [
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
"AWS_WEB_IDENTITY_TOKEN_FILE",
"AWS_ROLE_ARN"
];
SSM_TUNNEL_BASE_PORT = 2e4;
TUNNEL_READY_TIMEOUT_MS = 6e4;
}
});
// libs/mesh-cli/src/commands/dev-doctor.ts
import { execFileSync as execFileSync16 } from "node:child_process";
import * as fs20 from "node:fs";
import * as net9 from "node:net";
import * as path21 from "node:path";
function aggregateStatus(results) {
if (results.some((r) => r.status === "error")) return "error";
if (results.some((r) => r.status === "warn")) return "warn";
return "ok";
}
async function runChecks(ctx, phase, checks) {
const applicable = checks.filter((c) => c.phases.includes(phase));
return Promise.all(
applicable.map(async (check) => {
try {
return { check, result: await check.run(ctx, phase) };
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
return {
check,
result: {
status: "error",
summary: `check '${check.id}' threw: ${msg}`
}
};
}
})
);
}
function renderHuman(results) {
const lines = [];
for (const { check, result } of results) {
lines.push(`${ICONS[result.status]} ${check.title} \u2014 ${result.summary}`);
if (result.detail) lines.push(` ${result.detail}`);
if (result.remediation) lines.push(` Fix: ${result.remediation}`);
}
return lines.join("\n");
}
function jsonReport(results) {
return {
status: aggregateStatus(results.map((r) => r.result)),
checks: results.map(({ check, result }) => ({
id: check.id,
status: result.status,
summary: result.summary,
// Stable keys for tooling: always present, `null` when absent
// (JSON.stringify would otherwise drop `undefined`).
remediation: result.remediation ?? null,
detail: result.detail ?? null
}))
};
}
function renderJson(results) {
return JSON.stringify(jsonReport(results), null, 2);
}
function fmtTtl(seconds) {
if (seconds <= 0) return "expired";
const totalMin = Math.round(seconds / 60);
const h = Math.floor(totalMin / 60);
const m = totalMin % 60;
if (h > 0) return m > 0 ? `${h}h ${m}m` : `${h}h`;
return `${m}m`;
}
function credProbeToResult(probe, context, headless) {
const loginCmd = `mesh login ${context}${headless ? " --device" : ""}`;
switch (probe.state) {
case "ok":
return {
status: probe.ttlSeconds < 300 ? "warn" : "ok",
summary: `deployer creds valid ${fmtTtl(probe.ttlSeconds)}${probe.email ? ` (${probe.email})` : ""}`,
detail: `expires ${probe.expiresAt}`
};
case "no-session":
return {
status: "error",
summary: `no Zitadel session for ${context}`,
remediation: loginCmd
};
case "expired-session":
return {
status: "error",
summary: `Zitadel session for ${context} expired and could not refresh`,
remediation: loginCmd
};
case "assume-denied":
return {
status: "error",
summary: "session valid but AssumeRole denied \u2014 wrong IAM role/policy",
detail: probe.detail,
remediation: `check mesh:deployerRole in the stack config and the role's trust/permissions; re-login if role changed: ${loginCmd}`
};
case "stale-env-override":
return {
status: "error",
summary: "stale AWS_* env vars would override the self-refreshing login profile",
remediation: "unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN"
};
}
}
function parsePortSquatter(lsofOut, psOut) {
const firstLine = lsofOut.trim().split("\n")[0]?.trim();
const pid = Number(firstLine);
if (!firstLine || !Number.isInteger(pid)) return null;
const command = psOut.trim().split("\n")[0]?.trim() || "unknown";
return { pid, command };
}
function sharesWorktreeRoot(serviceSrc, repoRoot2) {
const src = path21.resolve(serviceSrc);
const root = path21.resolve(repoRoot2);
return src === root || src.startsWith(root + path21.sep);
}
function classifyPortListener(listenerPid, sessionPids) {
if (listenerPid === null) return "dead";
return sessionPids.has(listenerPid) ? "owned" : "foreign";
}
function collectSessionPids(panePids, psOut) {
const children = /* @__PURE__ */ new Map();
for (const line of psOut.trim().split("\n")) {
const [pidStr, ppidStr] = line.trim().split(/\s+/);
const pid = Number(pidStr);
const ppid = Number(ppidStr);
if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;
const kids = children.get(ppid) ?? [];
kids.push(pid);
children.set(ppid, kids);
}
const result = /* @__PURE__ */ new Set();
const queue = panePids.filter((p) => Number.isInteger(p));
while (queue.length > 0) {
const pid = queue.shift();
if (result.has(pid)) continue;
result.add(pid);
for (const child of children.get(pid) ?? []) queue.push(child);
}
return result;
}
function whoHasPort(port) {
try {
const lsof = execFileSync16("lsof", ["-ti", `:${port}`], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
const pid = lsof.trim().split("\n")[0]?.trim();
if (!pid) return null;
let ps = "";
try {
ps = execFileSync16("ps", ["-o", "comm=", "-p", pid], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
} catch {
}
return parsePortSquatter(lsof, ps);
} catch {
return null;
}
}
function sessionProcessTree(sessionName) {
let panePids;
try {
const out = execFileSync16(
"tmux",
["list-panes", "-s", "-t", sessionName, "-F", "#{pane_pid}"],
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
);
panePids = out.trim().split("\n").map((l) => Number(l.trim())).filter((n) => Number.isInteger(n));
} catch {
return /* @__PURE__ */ new Set();
}
if (panePids.length === 0) return /* @__PURE__ */ new Set();
let psOut = "";
try {
psOut = execFileSync16("ps", ["-eo", "pid=,ppid="], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
});
} catch {
return new Set(panePids);
}
return collectSessionPids(panePids, psOut);
}
function isConfigStale(configMtimeMs, startedAtIso) {
return configMtimeMs > Date.parse(startedAtIso);
}
function canConnect2(host, port, timeoutMs = 800) {
return new Promise((resolve19) => {
const socket = new net9.Socket();
let settled = false;
const done = (ok) => {
if (settled) return;
settled = true;
socket.destroy();
resolve19(ok);
};
socket.setTimeout(timeoutMs);
socket.once("connect", () => done(true));
socket.once("timeout", () => done(false));
socket.once("error", () => done(false));
socket.connect(port, host);
});
}
function isLocalHost(host) {
return host === "localhost" || host === "127.0.0.1" || host === "::1";
}
async function runDoctor(ctx, opts) {
const results = await runChecks(ctx, "ondemand", ALL_CHECKS);
const status = aggregateStatus(results.map((r) => r.result));
if (opts.json) {
console.log(renderJson(results));
} else {
console.log(renderHuman(results));
console.log(`
${ICONS[status]} overall: ${status}`);
}
return status;
}
var ICONS, credsCheck, registryCheck, tmuxCheck, portsCheck, configStalenessCheck, worktreeCheck, tunnelsCheck, temporalCheck, ALL_CHECKS;
var init_dev_doctor = __esm({
"libs/mesh-cli/src/commands/dev-doctor.ts"() {
"use strict";
init_dev();
init_auth_preflight();
init_login();
ICONS = { ok: "\u2713", warn: "\u26A0", error: "\u2717" };
credsCheck = {
id: "creds",
title: "Credentials",
phases: ["preflight", "ondemand"],
async run(ctx) {
switch (ctx.credMethod) {
case "sso":
return {
status: "ok",
summary: "using AWS SSO credentials (auto-refresh from SSO cache)"
};
case "environment":
return { status: "ok", summary: "using AWS credentials from environment" };
case "zitadel":
return {
status: "ok",
summary: "using Zitadel deployer credentials (credential_process auto-refresh)"
};
}
if (!ctx.platformContext || !ctx.deployerRole) {
return {
status: "warn",
summary: "no deployer role/context resolved \u2014 using ambient AWS credentials",
detail: "add mesh:deployerRole to the Pulumi stack config to use mesh login credentials"
};
}
const probe = await probeCredentials(ctx.platformContext, ctx.deployerRole);
return credProbeToResult(probe, ctx.platformContext, isRemoteEnvironment());
}
};
registryCheck = {
id: "registry",
title: "Registry auth",
phases: ["preflight", "ondemand"],
async run(ctx, phase) {
const probe = await probeRegistryToken(phase === "preflight" ? { timeoutMs: 3e3 } : void 0);
switch (probe.state) {
case "fresh":
return { status: "ok", summary: "CodeArtifact token accepted by the @mesh-tech registry" };
case "expired":
return {
status: "warn",
summary: "CodeArtifact token expired/rejected \u2014 installing @mesh-tech packages will fail with E401",
detail: probe.detail,
remediation: registryLoginFix()
};
case "unreachable":
return {
status: "ok",
summary: "registry unreachable \u2014 CodeArtifact token not verified (offline?)",
detail: probe.detail
};
case "missing":
return appUsesMeshPackages(ctx.appRoot) ? {
status: "warn",
summary: "no CodeArtifact auth in ~/.npmrc, but this app depends on @mesh-tech packages \u2014 pnpm install will fail with E401",
remediation: registryLoginFix()
} : {
status: "ok",
summary: "no CodeArtifact auth configured (needed only to install @mesh-tech packages)"
};
}
}
};
tmuxCheck = {
id: "tmux",
title: "tmux",
phases: ["preflight", "ondemand"],
async run() {
try {
execFileSync16("which", ["tmux"], { stdio: "ignore" });
return { status: "ok", summary: "installed" };
} catch {
return {
status: "error",
summary: "tmux is not installed",
remediation: "brew install tmux"
};
}
}
};
portsCheck = {
id: "ports",
title: "Ports",
phases: ["preflight", "ondemand"],
async run(ctx) {
const services = ctx.sessionState?.devOutput.services;
if (!services || Object.keys(services).length === 0) {
return {
status: "ok",
summary: "no session \u2014 ports are checked at allocation time"
};
}
const sessionPids = sessionProcessTree(ctx.sessionName);
const foreign = [];
const dead = [];
const unknown = [];
for (const [name, svc] of Object.entries(services)) {
const free = await isPortFree(svc.port);
const who = free ? null : whoHasPort(svc.port);
const ownership = classifyPortListener(who?.pid ?? null, sessionPids);
if (ownership === "owned") continue;
if (ownership === "foreign") {
foreign.push(`${name} :${svc.port} held by ${who.command} (pid ${who.pid})`);
continue;
}
if (free) {
dead.push(`${name} :${svc.port} not listening`);
} else {
unknown.push(`${name} :${svc.port} in use (owner unknown)`);
}
}
const total = Object.keys(services).length;
if (foreign.length === 0 && dead.length === 0 && unknown.length === 0) {
return {
status: "ok",
summary: `all ${total} service port(s) owned by this session`
};
}
const parts = [];
if (foreign.length) parts.push(`${foreign.length} squatted by another process`);
if (dead.length) parts.push(`${dead.length} not listening (service down)`);
if (unknown.length) parts.push(`${unknown.length} in use by an unidentified process`);
const remediation = foreign.length > 0 ? "stop the squatting process (or its SSH tunnel), then `mesh dev restart <svc>`" : dead.length > 0 ? "restart the stopped service: `mesh dev restart <svc>`" : "identify the port owner (`lsof -i :<port>`), then `mesh dev restart <svc>`";
return {
// A real squatter or a down service is actionable → error; an
// unidentifiable occupant alone is only a warning.
status: foreign.length > 0 || dead.length > 0 ? "error" : "warn",
summary: `service ports: ${parts.join(", ")}`,
detail: [...foreign, ...dead, ...unknown].join("; "),
remediation
};
}
};
configStalenessCheck = {
// Preflight runs *at* launch, so nothing can be stale-relative-to-launch yet
// — this only makes sense mid-session.
id: "config-staleness",
title: "Config freshness",
phases: ["ondemand"],
async run(ctx) {
const state = ctx.sessionState;
if (!state) {
return { status: "ok", summary: "no session \u2014 config read fresh at launch" };
}
const configPath = path21.join(ctx.appRoot, `Pulumi.${ctx.stack}.yaml`);
if (!fs20.existsSync(configPath)) {
return {
status: "ok",
summary: `no Pulumi.${ctx.stack}.yaml to compare`
};
}
const mtimeMs = fs20.statSync(configPath).mtimeMs;
if (isConfigStale(mtimeMs, state.startedAt)) {
return {
status: "warn",
summary: `Pulumi.${ctx.stack}.yaml changed since launch \u2014 a restart won't pick this up`,
detail: `config mtime ${new Date(mtimeMs).toISOString()} > session start ${state.startedAt}`,
remediation: `mesh deploy up --stack ${ctx.stack} && mesh dev`
};
}
return { status: "ok", summary: "stack config unchanged since launch" };
}
};
worktreeCheck = {
id: "worktree",
title: "Worktree",
phases: ["preflight", "ondemand"],
async run(ctx) {
const services = ctx.sessionState?.devOutput.services;
if (!services) {
return { status: "ok", summary: "no session \u2014 worktree checked at launch" };
}
let repoRoot2;
try {
repoRoot2 = execFileSync16("git", ["rev-parse", "--show-toplevel"], {
cwd: ctx.appRoot,
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"]
}).trim();
} catch {
return {
status: "warn",
summary: "could not resolve git worktree root for the app"
};
}
const foreign = Object.entries(services).filter(
([, svc]) => svc.src && !sharesWorktreeRoot(svc.src, repoRoot2) && !isLinkedDependencyDir(svc.src, ctx.appRoot)
).map(([name, svc]) => `${name} \u2192 ${svc.src}`);
if (foreign.length === 0) {
return {
status: "ok",
summary: `all services resolve under ${repoRoot2}`
};
}
return {
status: "warn",
summary: `${foreign.length} service(s) resolve outside this worktree \u2014 you may be running another worktree's code`,
detail: foreign.join("; "),
remediation: "relaunch from this worktree: `mesh dev` (re-reads stack output paths)"
};
}
};
tunnelsCheck = {
// On-demand only: pre-launch the tunnels don't exist yet, and dialing the
// VPC endpoints they'll use would false-positive on an SSM-only machine.
id: "tunnels",
title: "Tunnels",
phases: ["ondemand"],
async run(ctx) {
const tunnels = ctx.sessionState?.devOutput.tunnels;
if (!tunnels || Object.keys(tunnels).length === 0) {
return { status: "ok", summary: "no tunnels in this session" };
}
const local = Object.entries(tunnels).filter(([, t]) => isLocalHost(t.host));
if (local.length === 0) {
return {
status: "ok",
summary: `${Object.keys(tunnels).length} tunnel(s), all VPC-direct (not dialed)`
};
}
const dead = [];
for (const [name, t] of local) {
if (!await canConnect2(t.host, t.port)) dead.push(`${name} (${t.host}:${t.port})`);
}
if (dead.length === 0) {
return { status: "ok", summary: `${local.length} SSM tunnel(s) live` };
}
return {
status: "error",
summary: `${dead.length} SSM tunnel(s) down`,
detail: dead.join("; "),
remediation: "relaunch to re-establish tunnels: `mesh dev`"
};
}
};
temporalCheck = {
// On-demand only: reachability depends on the session's tunnel being up.
id: "temporal",
title: "Temporal",
phases: ["ondemand"],
async run(ctx) {
const state = ctx.sessionState;
const t = state?.devOutput.tunnels["temporal"];
if (!state || !t) {
return { status: "ok", summary: "no temporal tunnel in this session" };
}
const addr = `${t.host}:${t.port}`;
if (!await canConnect2(t.host, t.port)) {
return {
status: "error",
summary: `Temporal frontend unreachable at ${addr}`,
remediation: "tunnel likely down \u2014 relaunch: `mesh dev`"
};
}
const p = state.devOutput.platform;
const app = state.devOutput.app;
if (!p || !app) {
return { status: "ok", summary: `frontend reachable at ${addr} (namespace unknown)` };
}
const namespace = `${p.tenant}-${p.env}-${app}`;
try {
const { Connection } = await import("@temporalio/client");
const connection = await Connection.connect({
address: addr,
connectTimeout: "3s"
});
try {
await connection.workflowService.describeNamespace({ namespace });
return { status: "ok", summary: `frontend reachable; namespace ${namespace} present` };
} finally {
await connection.close().catch(() => {
});
}
} catch (err) {
const code = err.code;
if (code === 5) {
return {
status: "error",
summary: `namespace ${namespace} not found on the server`,
remediation: `verify the app is deployed to this env (mesh deploy up --stack ${ctx.stack})`
};
}
if (code === 7 || code === 16) {
return {
status: "ok",
summary: `frontend reachable; namespace ${namespace} auth-gated (not verified)`
};
}
return {
status: "warn",
summary: "frontend reachable but namespace check errored",
detail: err instanceof Error ? err.message : String(err)
};
}
}
};
ALL_CHECKS = [
tmuxCheck,
credsCheck,
registryCheck,
configStalenessCheck,
portsCheck,
worktreeCheck,
tunnelsCheck,
temporalCheck
];
}
});
// libs/mesh-cli/src/commands/app-check.ts
import * as fs21 from "node:fs";
import * as path22 from "node:path";
function rel(root, abs) {
return path22.relative(root, abs).split(path22.sep).join("/");
}
function isDir(p) {
try {
return fs21.statSync(p).isDirectory();
} catch {
return false;
}
}
function readText(p) {
try {
return fs21.readFileSync(p, "utf8");
} catch {
return "";
}
}
function srcFiles(dir) {
const out = [];
const walk2 = (d) => {
let entries;
try {
entries = fs21.readdirSync(d, { withFileTypes: true });
} catch {
return;
}
for (const e of entries) {
const p = path22.join(d, e.name);
if (e.isDirectory()) {
if (SKIP_DIRS.has(e.name) || e.name.startsWith("scenario")) continue;
walk2(p);
continue;
}
if (!e.isFile()) continue;
if (!SRC_EXT.has(path22.extname(e.name))) continue;
if (/\.(test|spec)\./.test(e.name) || /\.config\./.test(e.name) || e.name === "build.mjs") continue;
out.push(p);
}
};
walk2(dir);
return out.sort();
}
function isTestHarness(name) {
return name.startsWith("test-") || name.endsWith("-test");
}
function isUiDir(name) {
return ["ui", "web", "frontend", "client", "portal-ui", "app"].includes(name) || name.endsWith("-ui");
}
function serviceDirs(app) {
let entries;
try {
entries = fs21.readdirSync(app, { withFileTypes: true });
} catch {
return [];
}
return entries.filter((e) => e.isDirectory() && !NON_SERVICE_DIRS.has(e.name) && !e.name.startsWith(".") && !isTestHarness(e.name)).map((e) => path22.join(app, e.name)).sort();
}
function authzSurface(app) {
const hits = [];
const walk2 = (d) => {
let entries;
try {
entries = fs21.readdirSync(d, { withFileTypes: true });
} catch {
return;
}
for (const e of entries) {
const p = path22.join(d, e.name);
if (e.isDirectory()) {
if (["node_modules", "dist", "build"].includes(e.name)) continue;
walk2(p);
continue;
}
if (!e.isFile()) continue;
if (!/\.(ts|tsx|yaml|sql)$/.test(e.name) || /\.(test|spec)\.tsx?$/.test(e.name) || e.name.endsWith(".d.ts")) continue;
const lines = readText(p).split("\n");
lines.forEach((text, i) => {
if (/^\s*(\/\/|\*|\/\*|#|--)/.test(text)) return;
hits.push({ file: p, line: i + 1, text });
});
}
};
walk2(app);
return hits;
}
function balancedSpan(text, open) {
const closers = { "(": ")", "{": "}", "[": "]" };
const stack = [];
let quote;
for (let i = open; i < text.length; i++) {
const ch = text[i];
if (quote) {
if (ch === "\\") i++;
else if (ch === quote) quote = void 0;
continue;
}
if (ch === '"' || ch === "'" || ch === "`") {
quote = ch;
continue;
}
if (ch === "/" && text[i + 1] === "/") {
const eol = text.indexOf("\n", i);
if (eol === -1) return "";
i = eol;
continue;
}
if (ch === "/" && text[i + 1] === "*") {
const end = text.indexOf("*/", i + 2);
if (end === -1) return "";
i = end + 1;
continue;
}
if (closers[ch]) stack.push(closers[ch]);
else if (ch === ")" || ch === "}" || ch === "]") {
if (stack.pop() !== ch) return "";
if (stack.length === 0) return text.slice(open, i + 1);
}
}
return "";
}
function splitTopLevel(inner) {
const parts = [];
let depth = 0;
let quote;
let start = 0;
for (let i = 0; i < inner.length; i++) {
const ch = inner[i];
if (quote) {
if (ch === "\\") i++;
else if (ch === quote) quote = void 0;
continue;
}
if (ch === '"' || ch === "'" || ch === "`") {
quote = ch;
continue;
}
if (ch === "/" && inner[i + 1] === "/") {
const eol = inner.indexOf("\n", i);
if (eol === -1) break;
i = eol;
continue;
}
if (ch === "/" && inner[i + 1] === "*") {
const end = inner.indexOf("*/", i + 2);
if (end === -1) break;
i = end + 1;
continue;
}
if (ch === "(" || ch === "{" || ch === "[") depth++;
else if (ch === ")" || ch === "}" || ch === "]") depth--;
else if (ch === "," && depth === 0) {
parts.push(inner.slice(start, i));
start = i + 1;
}
}
const last = inner.slice(start);
if (last.trim()) parts.push(last);
return parts;
}
function stripLeadingComments(entry) {
let text = entry;
for (; ; ) {
const next = text.replace(/^\s*(?:\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)/, "");
if (next === text) return text;
text = next;
}
}
function stripTrailingComments(value) {
let out = "";
let depth = 0;
let quote;
for (let i = 0; i < value.length; i++) {
const ch = value[i];
if (quote) {
out += ch;
if (ch === "\\") {
out += value[i + 1] ?? "";
i++;
} else if (ch === quote) quote = void 0;
continue;
}
if (ch === '"' || ch === "'" || ch === "`") {
quote = ch;
out += ch;
continue;
}
if (depth === 0 && ch === "/" && value[i + 1] === "/") {
const eol = value.indexOf("\n", i);
if (eol === -1) break;
i = eol;
out += "\n";
continue;
}
if (depth === 0 && ch === "/" && value[i + 1] === "*") {
const end = value.indexOf("*/", i + 2);
if (end === -1) break;
i = end + 1;
continue;
}
if (ch === "(" || ch === "{" || ch === "[") depth++;
else if (ch === ")" || ch === "}" || ch === "]") depth--;
out += ch;
}
return out.trim();
}
function topLevelProperties(objectText) {
const props = /* @__PURE__ */ new Map();
if (!objectText.startsWith("{") || !objectText.endsWith("}")) return props;
for (const entry of splitTopLevel(objectText.slice(1, -1))) {
const text = stripLeadingComments(entry).trim();
if (!text || text.startsWith("...")) continue;
const m = RE_PROPERTY.exec(text);
if (!m) continue;
const key = m[1] ?? m[2] ?? m[3];
props.set(key, stripTrailingComments(m[4] ?? key));
}
return props;
}
function objectLiteralOf(value, program2) {
if (!value) return void 0;
if (value.startsWith("{")) return balancedSpan(value, 0) || void 0;
const id = /^[A-Za-z_$][\w$]*$/.exec(value)?.[0];
if (!id) return void 0;
const decl = new RegExp(`\\bconst\\s+${id}\\s*(?::[^=]+)?=\\s*\\{`).exec(program2);
return decl ? balancedSpan(program2, decl.index + decl[0].length - 1) || void 0 : void 0;
}
function optionsObject(body) {
for (const arg of splitTopLevel(body.slice(1, -1))) {
const trimmed = arg.trim();
if (trimmed.startsWith("{")) return balancedSpan(trimmed, 0) || void 0;
}
return void 0;
}
function surfaceDeclarations(text, pattern = RE_SURFACE_NEW) {
const out = [];
for (const m of text.matchAll(pattern)) {
const body = balancedSpan(text, m.index + m[0].length - 1);
if (!body) continue;
out.push({ ctor: m[1] ?? "ApiDocs", body, line: text.slice(0, m.index).split("\n").length });
}
return out;
}
function isClientAuth(value) {
return value !== void 0 && value !== "undefined" && !RE_DEV_BYPASS.test(value);
}
function docsSiteFindings(text) {
const findings = [];
for (const decl of surfaceDeclarations(text)) {
const options = optionsObject(decl.body);
if (!options) continue;
const args = topLevelProperties(options);
const docsText = objectLiteralOf(args.get("docs"), text);
if (!docsText) continue;
const docs = topLevelProperties(docsText);
if (docs.get("site") === "false") continue;
if (docs.get("site") !== "true" && !DOCS_SITE_FIELDS.some((f) => docs.has(f))) continue;
const httpText = objectLiteralOf(topLevelProperties(objectLiteralOf(args.get("surfaces"), text) ?? "{}").get("http"), text);
const hasClient = isClientAuth(docs.get("auth")) || isClientAuth(topLevelProperties(httpText ?? "{}").get("auth"));
if (!hasClient) {
findings.push({
code: "DOCS_SITE_UNAUTHED",
line: decl.line,
why: `mesh.apps.${decl.ctor} declares a docs site with no OIDC client \u2014 the site is served behind the platform sign-in proxy, which needs one (docs.auth, or a client on surfaces.http.auth)`
});
}
const kindStated = decl.ctor === "AppApiSurface" || decl.ctor === "VendorApiSurface" || args.has("kind");
if (!kindStated && !docs.has("audience")) {
findings.push({
code: "DOCS_AUDIENCE_IMPLICIT",
line: decl.line,
why: `mesh.apps.${decl.ctor} declares a docs site but neither its kind nor its audience \u2014 who may read it would fall out of the name heuristic, and a vendor surface must never default to partner-readable that way`
});
}
}
for (const decl of surfaceDeclarations(text, RE_APIDOCS_NEW)) {
const options = optionsObject(decl.body);
if (!options) continue;
const args = topLevelProperties(options);
if (!args.has("ingress") || args.get("ingress") === "undefined") continue;
if (isClientAuth(args.get("auth"))) continue;
findings.push({
code: "DOCS_SITE_UNAUTHED",
line: decl.line,
why: `mesh.apps.ApiDocs is published on an ingress with no auth \u2014 the site (reference and playground) is reachable by anyone; put the platform sign-in proxy in front of it, or drop the ingress`
});
}
return findings;
}
function listApps(root) {
const apps = [];
const appsDir = path22.join(root, "apps");
if (isDir(appsDir)) {
for (const e of fs21.readdirSync(appsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
if (!e.isDirectory()) continue;
const d = path22.join(appsDir, e.name);
if (fs21.existsSync(path22.join(d, "index.ts")) || fs21.existsSync(path22.join(d, "Pulumi.yaml"))) apps.push(rel(root, d));
}
}
if (fs21.existsSync(path22.join(root, "index.ts")) && fs21.existsSync(path22.join(root, "Pulumi.yaml"))) apps.push(".");
return apps;
}
function scanApp(root, appRel) {
const app = path22.resolve(root, appRel);
const index = path22.join(app, "index.ts");
const findings = [];
const hit = (code, p, why) => {
findings.push({ code, path: p, why });
};
const hasProgram = fs21.existsSync(index) || fs21.existsSync(path22.join(app, "Pulumi.yaml"));
if (!hasProgram) return findings;
if (fs21.existsSync(index)) {
const indexRel = rel(root, index);
let servesHtml = false;
let hasUiService = false;
for (const svc of serviceDirs(app)) {
const svcName = path22.basename(svc);
if (isUiDir(svcName)) {
hasUiService = true;
continue;
}
for (const f of srcFiles(svc)) {
let why = "";
if (/\.(tsx|jsx)$/.test(f)) why = `JSX view file inside the ${svcName} service`;
else if (RE_HTML.test(readText(f))) why = `renders HTML inside the ${svcName} service`;
if (why) {
servesHtml = true;
hit("UI_IN_API", rel(root, f), `${why} \u2014 a browser UI is its own mesh.apps.Service (apps/<app>/ui)`);
}
}
}
if (servesHtml && !hasUiService) {
hit("NO_UI_SERVICE", indexRel, 'the app serves HTML but declares no ui/ service \u2014 add apps/<app>/ui + new mesh.apps.Service("ui", \u2026)');
}
if (!/env\.register\(|\.annotate\(/.test(readText(index))) {
hit("NO_REGISTER", indexRel, "never calls env.register({\u2026}) \u2014 the Hub cannot list this app");
}
for (const f of docsSiteFindings(readText(index))) hit(f.code, `${indexRel}:${f.line}`, f.why);
for (const svc of serviceDirs(app)) {
for (const f of srcFiles(svc)) {
const text = readText(f);
if (RE_OIDC.test(text) && RE_COOKIE.test(text)) {
hit("CUSTOM_SESSION_AUTH", rel(root, f), 'app-written OIDC + cookie session \u2014 protect the UI service with auth: { provider: "mesh" } instead');
}
}
}
for (const svc of serviceDirs(app)) {
for (const f of srcFiles(svc)) {
const lines = readText(f).split("\n");
const i = lines.findIndex((l) => RE_LOGGER.test(l) && !l.includes("mesh-tech/logger"));
if (i === -1) continue;
hit("NON_PLATFORM_LOGGER", `${rel(root, f)}:${i + 1}`, `logs outside @mesh-tech/logger (${lines[i]?.trim() ?? ""})`);
}
}
}
const surface = authzSurface(app);
const first = (re) => surface.find((h) => re.test(h.text));
const exposure = first(RE_EXPOSURE);
const pointer = first(RE_POINTER);
const metadata = first(RE_METADATA);
const appLabel = appRel === "." ? "(root)" : path22.basename(appRel);
if (exposure && !pointer) {
const at = surface.find((h) => RE_EXPOSURE.test(h.text) && /(^|\/)index\.ts$/.test(h.file)) ?? exposure;
hit(
"NO_AUTHZ_POINTER",
rel(root, at.file),
`${appLabel} authenticates callers or declares roles but publishes no mesh.auth.AppAuthzPointer \u2014 the Hub's Access tab is blank for this app`
);
} else if (exposure && pointer && !metadata) {
hit(
"METADATA_UNPUBLISHED",
rel(root, pointer.file),
"AppAuthzPointer present but no compiled ops-hub metadata (opsHubMetadata: compileOpsHubMetadata(schemaDef) on the pointer, or a SpiceDBSchema) \u2014 the Hub's create-key and role catalog 409 METADATA_UNPUBLISHED"
);
}
const anti = (code, re, why) => {
const files = [...new Set(surface.filter((h) => re.test(h.text)).map((h) => h.file))].sort().slice(0, 3);
for (const f of files) hit(code, rel(root, f), why);
};
anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC \u2014 grants belong in the Hub (Access \u2192 People), not in a config redeploy");
anti(
"POINTER_PROJECT_OVERRIDE",
RE_POINTER_OVERRIDE,
"AppAuthzPointer is given a `zitadel:` override \u2014 an app binds its project with AppEnvironment's zitadelAppProjectId so every consumer of env.zitadel agrees; the override is reserved for the Hub"
);
anti(
"PASSWORD_STORE",
RE_PASSWORD,
"password storage or hashing \u2014 Zitadel owns credentials; the Hub creates sign-in users with a one-time temporary password"
);
anti("EMAIL_ALLOWLIST", RE_ALLOWLIST, "email allowlist standing in for roles \u2014 declare a role and let the Hub grant it");
anti("USERS_TABLE", RE_USERS_TABLE, "hand-rolled users/roles table \u2014 people and roles live in Zitadel and are administered from the Hub");
return findings;
}
function isPlatformPackage(dir) {
try {
const pkg = JSON.parse(readText(path22.join(dir, "package.json")));
return typeof pkg.name === "string" && pkg.name.startsWith("@mesh-tech/");
} catch {
return false;
}
}
function scanRepo(root) {
const findings = [];
for (const parent of ["libs", "packages"]) {
const dir = path22.join(root, parent);
if (!isDir(dir)) continue;
for (const e of fs21.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
if (!e.isDirectory() || !e.name.includes("auth")) continue;
const lib = path22.join(dir, e.name);
if (isPlatformPackage(lib)) continue;
const verifies = srcFiles(lib).filter((f) => f.endsWith(".ts")).some((f) => /introspect|jwtVerify|createRemoteJWKSet|from ["']jose["']/.test(readText(f)));
if (verifies) {
findings.push({
code: "LOCAL_AUTH_LIB",
path: rel(root, lib),
why: "tenant-local token verification \u2014 check whether @mesh-tech/authn already owns this"
});
}
}
}
return findings;
}
function resultFor(code, findings) {
const rule = APP_CHECK_RULES[code];
const mine = findings.filter((f) => f.code === code);
if (mine.length === 0) return { status: "ok", summary: `gate ${rule.gate}: no ${code} finding` };
return {
status: rule.severity === "block" ? "error" : "warn",
summary: `gate ${rule.gate}: ${code} \xD7 ${mine.length}`,
detail: mine.map((f) => `${f.path} \u2014 ${f.why}`).join("\n "),
remediation: rule.remediation
};
}
function appContractChecks(scope) {
return APP_CHECK_CODES.filter((code) => APP_CHECK_RULES[code].scope === scope).map((code) => ({
id: code,
title: `${APP_CHECK_RULES[code].title} (${APP_CHECK_RULES[code].gate})`,
phases: ["ondemand", "preflight"],
run: async (ctx) => resultFor(code, ctx.findings)
}));
}
async function runAppCheck(root, apps) {
if (!isDir(root)) throw new MeshCliError(`no such checkout: ${root}`);
const targets = apps && apps.length > 0 ? apps.map((a) => a.replace(/\/+$/, "")) : listApps(root);
for (const a of targets) {
if (!isDir(path22.resolve(root, a))) throw new MeshCliError(`no such app dir: ${a}`);
}
const scopes = [];
for (const app of targets) {
const results = await runChecks({ root, app, findings: scanApp(root, app) }, "ondemand", appContractChecks("app"));
scopes.push({ scope: app, ...jsonReport(results) });
}
const repoResults = await runChecks({ root, app: null, findings: scanRepo(root) }, "ondemand", appContractChecks("repo"));
scopes.push({ scope: "(repo)", ...jsonReport(repoResults) });
return { root, status: aggregateStatus(scopes.map((s) => ({ status: s.status, summary: "" }))), scopes };
}
function renderReport(report, opts) {
const out = [];
for (const scope of report.scopes) {
const shown = opts.verbose ? scope.checks : scope.checks.filter((c) => c.status !== "ok");
if (shown.length === 0 && !opts.verbose) continue;
out.push(`${ICONS[scope.status]} ${scope.scope}`);
out.push(
renderHuman(
shown.map((c) => ({
check: { id: c.id, title: `${c.id} \u2014 ${APP_CHECK_RULES[c.id]?.title ?? c.id}` },
result: { status: c.status, summary: c.summary, remediation: c.remediation ?? void 0, detail: c.detail ?? void 0 }
}))
).split("\n").map((l) => ` ${l}`).join("\n")
);
}
out.push(`${ICONS[report.status]} app contract: ${report.status}`);
return out.filter(Boolean).join("\n");
}
function registerAppCommands(program2) {
const app = program2.command("app").description("Inspect a tenant app against the Mesh app contract");
app.command("check").description(
"Check tenant apps against the Mesh app contract (@mesh-tech/app-kit skills/apps/references/app-contract.md): a UI is its own service, the app registers with the Hub, sign-in goes through the platform proxy, logs go through @mesh-tech/logger, and people/roles/keys are surfaced through the Hub. Exit 1 on any blocking finding \u2014 the same gate mesh create-app, tenant CI and the platform reviewer run."
).argument("[apps...]", "repo-relative app dirs to check (default: every apps/* with a Pulumi program)").option("--root <dir>", "checkout to scan (default: the enclosing git root)").option("--json", "machine-readable output (one report per app plus the repo-scoped checks)", false).option("--verbose", "also print the gates that passed", false).action(async (apps, opts) => {
const root = path22.resolve(opts.root ?? resolveTargetRoot());
const report = await runAppCheck(root, apps);
if (opts.json) {
emitJsonPayload({ ok: report.status !== "error", ...report });
} else {
if (report.scopes.length === 1) logInfo(`no apps/* with a Pulumi program under ${root}`);
console.log(renderReport(report, { verbose: opts.verbose }));
}
if (report.status === "error") {
if (!opts.json) logError("the app contract is not met \u2014 every \u2717 above names the gate, the file and the replacement");
process.exitCode = 1;
}
});
}
var APP_CHECK_RULES, APP_CHECK_CODES, SRC_EXT, SKIP_DIRS, NON_SERVICE_DIRS, RE_HTML, RE_OIDC, RE_COOKIE, RE_LOGGER, RE_EXPOSURE, RE_POINTER, RE_METADATA, RE_IAC_GRANT, RE_POINTER_OVERRIDE, RE_PASSWORD, RE_ALLOWLIST, RE_USERS_TABLE, RE_SURFACE_NEW, RE_APIDOCS_NEW, DOCS_SITE_FIELDS, RE_DEV_BYPASS, RE_PROPERTY;
var init_app_check = __esm({
"libs/mesh-cli/src/commands/app-check.ts"() {
"use strict";
init_utils();
init_errors();
init_skills();
init_dev_doctor();
APP_CHECK_RULES = {
UI_IN_API: {
gate: "0.1",
title: "UI is its own service",
severity: "block",
remediation: 'move the pages to apps/<app>/ui and declare new mesh.apps.Service("ui", { env, src: "./ui", \u2026 })',
scope: "app"
},
NO_UI_SERVICE: {
gate: "0.1",
title: "UI service declared",
severity: "block",
remediation: 'add apps/<app>/ui + new mesh.apps.Service("ui", \u2026) in index.ts',
scope: "app"
},
NO_REGISTER: {
gate: "0.2",
title: "App registers with the Hub",
severity: "block",
remediation: "end index.ts with `export const app = env.register({ description })`",
scope: "app"
},
CUSTOM_SESSION_AUTH: {
gate: "0.3",
title: "Sign-in through the platform",
severity: "block",
remediation: 'delete the OIDC + cookie code and protect the UI service with auth: { provider: "mesh" }',
scope: "app"
},
NON_PLATFORM_LOGGER: {
gate: "0.4",
title: "Logs through @mesh-tech/logger",
severity: "block",
remediation: 'import { createLogger } from "@mesh-tech/logger" and log through it (requestLogger for HTTP)',
scope: "app"
},
LOCAL_AUTH_LIB: {
gate: "0.5",
title: "No tenant-local auth library",
severity: "advisory",
remediation: "check whether @mesh-tech/authn already owns this; if the platform lacks it, file a platform item",
scope: "repo"
},
NO_AUTHZ_POINTER: {
gate: "H",
title: "Access surfaced through the Hub",
severity: "block",
remediation: 'end index.ts with new mesh.auth.AppAuthzPointer("authz", { env, mode, opsHubMetadata: compileOpsHubMetadata(schemaDef) }) (role-gating) or { env, mode, opsHubMetadataRef: schema.opsHubMetadataRef } (policy-engine)',
scope: "app"
},
METADATA_UNPUBLISHED: {
gate: "H",
title: "Ops-hub metadata published",
severity: "block",
remediation: "export compileOpsHubMetadata(schemaDef) with mesh.exports.Export (role-gating) or let SpiceDBSchema publish it (policy-engine)",
scope: "app"
},
IAC_GRANTS: {
gate: "H",
title: "No role grants in IaC",
severity: "block",
remediation: "remove the per-user grant map; grant roles from the Hub (App \u2192 Access \u2192 People)",
scope: "app"
},
POINTER_PROJECT_OVERRIDE: {
gate: "H",
title: "Pointer publishes the env's project",
severity: "advisory",
remediation: "drop `zitadel:` from AppAuthzPointer and pass `zitadelAppProjectId: identity.projectId` to AppEnvironment \u2014 the override is the Hub's",
scope: "app"
},
PASSWORD_STORE: {
gate: "H",
title: "No app-side credentials",
severity: "block",
remediation: "delete the password code; the Hub creates sign-in users with a one-time temporary password",
scope: "app"
},
EMAIL_ALLOWLIST: {
gate: "H",
title: "No email allowlist for roles",
severity: "block",
remediation: "declare a role on the ZitadelAppIdentity and let the Hub grant it",
scope: "app"
},
USERS_TABLE: {
gate: "H",
title: "No hand-rolled users/roles table",
severity: "block",
remediation: "drop the table; people and roles live in Zitadel and are administered from the Hub",
scope: "app"
},
DOCS_SITE_UNAUTHED: {
gate: "0.3",
title: "Docs site behind the platform sign-in",
severity: "block",
remediation: 'on an API surface: docs: { auth: { clientId, clientSecret } } (or a client on surfaces.http.auth), or opt out with docs: { site: false }; on a mesh.apps.ApiDocs: auth: { provider: "mesh", clientId, clientSecret }, or drop the ingress (an in-cluster site is reachable through mesh dev)',
scope: "app"
},
DOCS_AUDIENCE_IMPLICIT: {
gate: "H",
title: "Docs audience stated, not derived",
severity: "block",
remediation: 'declare the surface as mesh.apps.AppApiSurface or VendorApiSurface (or pass kind: "app" | "vendor"), or state docs: { audience: { roles: [\u2026] } }',
scope: "app"
}
};
APP_CHECK_CODES = Object.keys(APP_CHECK_RULES);
SRC_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".mjs", ".jsx"]);
SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", ".generated", "test", "tests", "__tests__", "fixtures", "scripts"]);
NON_SERVICE_DIRS = /* @__PURE__ */ new Set([
"docs",
"scripts",
"lib",
"libs",
"migrations",
"test",
"tests",
"node_modules",
"infra",
"mocks",
"skills",
"prisma"
]);
RE_HTML = /from ["']hono\/(html|jsx)["']|react-dom\/server|\bc\.html\(|<!doctype html|<!DOCTYPE html/;
RE_OIDC = /openid-client|code_verifier|authorization_code|buildAuthorizeUrl|end_session/;
RE_COOKIE = /setCookie\(|hono\/cookie|Set-Cookie/;
RE_LOGGER = /from ["'](pino|hono\/logger|winston|bunyan)["']|require\(["'](pino|winston|bunyan)["']\)|^[^/*]*\bconsole\.(log|info|warn|error)\(/;
RE_EXPOSURE = /ZitadelAppIdentity|createOAuthMiddleware|createAuthorizationMiddleware|zitadelJwtScheme|createAuthzMiddleware|bearerAuth|type: *"oidc"|^\s+auth: *\{|_ROLES *= *\{|coarseRoles|ZITADEL_(AUDIENCE|CLIENT_ID)|OIDC_CLIENT_ID|zitadel\.(ApplicationOidc|ApplicationApi|Project|UserGrant)\(/;
RE_POINTER = /AppAuthzPointer/;
RE_METADATA = /compileOpsHubMetadata|ops-hub-metadata|SpiceDBSchema/;
RE_IAC_GRANT = /userId: *"[0-9]{6,}"/;
RE_POINTER_OVERRIDE = /\bzitadel: *\{\s*projectId\b/;
RE_PASSWORD = /(^|[^a-z])(bcrypt|argon2)|password_hash|passwordHash|hashPassword|temporaryPassword.*(send|mail)|(send|mail).*temporaryPassword/;
RE_ALLOWLIST = /ADMIN_EMAILS|ALLOWED_EMAILS|allowedEmails|adminEmails/;
RE_USERS_TABLE = /create table (if not exists )?"?(users|roles|user_roles|permissions)"?/i;
RE_SURFACE_NEW = /new\s+mesh\.apps\.(ApiSurface|AppApiSurface|VendorApiSurface|Integration)\s*\(/g;
RE_APIDOCS_NEW = /new\s+mesh\.apps\.ApiDocs\s*\(/g;
DOCS_SITE_FIELDS = ["pages", "playground", "audience", "basePath", "ingress", "auth"];
RE_DEV_BYPASS = /^["']dev-bypass["']$/;
RE_PROPERTY = /^(?:"([^"]+)"|'([^']+)'|([A-Za-z_$][\w$]*))\s*(?::\s*([\s\S]*))?$/;
}
});
// packages/agent-targets/src/schema.ts
import { z as z2 } from "zod";
var TargetEntrySchema, RegistrySchema;
var init_schema = __esm({
"packages/agent-targets/src/schema.ts"() {
"use strict";
TargetEntrySchema = z2.object({
apiBaseUrl: z2.string().url(),
hubBaseUrl: z2.string().url().optional(),
conversationPathPrefix: z2.string().optional(),
approvalPathPrefix: z2.string().optional(),
tenant: z2.string().min(1),
env: z2.string().min(1),
loginContext: z2.string().optional(),
token: z2.string().optional()
});
RegistrySchema = z2.object({ defaultTarget: z2.string().min(1), targets: z2.record(z2.string(), TargetEntrySchema) }).refine((r) => r.defaultTarget in r.targets, { message: "defaultTarget must be one of the configured targets" });
}
});
// packages/agent-targets/src/resolve.ts
function stripSlash(url) {
let end = url.length;
while (end > 0 && url.charCodeAt(end - 1) === 47) end--;
return url.slice(0, end);
}
function normalizePathPrefix(prefix2) {
return stripSlash(prefix2.startsWith("/") ? prefix2 : `/${prefix2}`);
}
function resolveEntry(entry) {
const apiBaseUrl = stripSlash(entry.apiBaseUrl);
return {
apiBaseUrl,
hubBaseUrl: stripSlash(entry.hubBaseUrl || apiBaseUrl),
conversationPathPrefix: normalizePathPrefix(entry.conversationPathPrefix || DEFAULT_CONVERSATION_PATH_PREFIX),
approvalPathPrefix: normalizePathPrefix(entry.approvalPathPrefix || DEFAULT_APPROVAL_PATH_PREFIX),
token: entry.token || void 0,
loginContext: entry.loginContext || void 0,
tenant: entry.tenant,
environment: entry.env
};
}
var DEFAULT_CONVERSATION_PATH_PREFIX, DEFAULT_APPROVAL_PATH_PREFIX;
var init_resolve = __esm({
"packages/agent-targets/src/resolve.ts"() {
"use strict";
DEFAULT_CONVERSATION_PATH_PREFIX = "/assistant/c";
DEFAULT_APPROVAL_PATH_PREFIX = "/approvals";
}
});
// packages/agent-targets/src/load.ts
import { readFileSync as readFileSync18 } from "node:fs";
import { homedir as homedir3 } from "node:os";
import path23 from "node:path";
function loadTargetsFile(env = process.env) {
const file = env.MESH_AGENT_TARGETS || DEFAULT_REGISTRY_PATH;
let raw;
try {
raw = readFileSync18(file, "utf8");
} catch {
return { status: "absent", path: file };
}
try {
const parsed = RegistrySchema.parse(JSON.parse(raw));
const targets = {};
for (const [name, entry] of Object.entries(parsed.targets)) targets[name] = resolveEntry(entry);
return { status: "loaded", targets, defaultTarget: parsed.defaultTarget, path: file };
} catch (err) {
return { status: "invalid", path: file, message: err instanceof Error ? err.message : String(err) };
}
}
var DEFAULT_REGISTRY_PATH;
var init_load = __esm({
"packages/agent-targets/src/load.ts"() {
"use strict";
init_schema();
init_resolve();
DEFAULT_REGISTRY_PATH = path23.join(homedir3(), ".config", "mesh", "agent-targets.json");
}
});
// packages/agent-targets/src/links.ts
function conversationUrl(t, conversationId) {
return `${t.hubBaseUrl}${t.conversationPathPrefix}/${conversationId}`;
}
function approvalUrl(t, dispatchId) {
return `${t.hubBaseUrl}${t.approvalPathPrefix}/${dispatchId}`;
}
var init_links = __esm({
"packages/agent-targets/src/links.ts"() {
"use strict";
}
});
// packages/agent-targets/src/pull/types.ts
var init_types = __esm({
"packages/agent-targets/src/pull/types.ts"() {
"use strict";
}
});
// packages/agent-targets/src/pull/delegates.ts
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function extractDelegateIds(messages, parentId) {
const hay = JSON.stringify(messages ?? []);
const re = new RegExp(escapeRegExp(parentId) + "\\/delegate-[A-Za-z0-9_-]+", "g");
return [...new Set(hay.match(re) ?? [])];
}
var init_delegates = __esm({
"packages/agent-targets/src/pull/delegates.ts"() {
"use strict";
}
});
// packages/agent-targets/src/pull/pull.ts
function sortKeys(v) {
if (Array.isArray(v)) return v.map(sortKeys);
if (v && typeof v === "object") {
const src = v;
return Object.keys(src).sort().reduce((acc, k) => {
acc[k] = sortKeys(src[k]);
return acc;
}, {});
}
return v;
}
function canonicalJson(v) {
return JSON.stringify(sortKeys(v));
}
function pullHintFor(ref, hintTarget) {
const t = hintTarget ? ` --target ${hintTarget}` : " --target <name>";
if (ref.resource === "vcs" && ref.repo) {
return ref.path ? `mesh vcs get ${ref.repo} ${ref.path}${t} # one path, not the whole repo` : `mesh vcs get ${ref.repo} <path>${t} # browse conversation.md for the doc paths that matter`;
}
if (ref.id) return `mesh artifacts get <conversationId>:${ref.id}${t}`;
return "see conversation.md for context";
}
function describeErr(e) {
return e instanceof Error ? e.message : String(e);
}
function slugify(s) {
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
}
function safeSlug(s) {
return s.replace(/[/\\]+/g, "-").replace(/\.\.+/g, "").replace(/^[.\-]+/, "").slice(0, 120) || "design";
}
function slugForDesign(saveFolder, title, wf) {
if (saveFolder) return safeSlug(saveFolder);
if (title && slugify(title)) return slugify(title);
return slugify(wf.split(":").pop() ?? wf) || "design";
}
function refOf(a) {
return a.content?.attachment?.ref;
}
async function pullConversation(deps, conversationId, opts = {}) {
const recursive = opts.recursive ?? true;
const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
const warnings = [];
const getJson = async (apiPath) => {
const res = await deps.fetchApi(apiPath);
if (!res.ok) throw new Error(`${res.status} ${apiPath}`);
return await res.json();
};
let transcript = null;
let title;
let delegateChildIds = {};
try {
const body = await getJson(
`/v1/conversations/${encodeURIComponent(conversationId)}`
);
transcript = { messages: body.messages ?? [] };
title = body.title;
delegateChildIds = body.delegateChildIds ?? {};
} catch (e) {
warnings.push({ stage: "transcript", ref: conversationId, detail: describeErr(e) });
transcript = { messages: [] };
}
let artifacts = [];
try {
const body = await getJson(
`/v1/conversations/${encodeURIComponent(conversationId)}/artifacts`
);
artifacts = body.artifacts ?? [];
} catch (e) {
warnings.push({ stage: "artifacts", ref: conversationId, detail: describeErr(e) });
}
const designs = [];
const uiSpecs = [];
const references = [];
for (const a of artifacts) {
const ref = refOf(a);
if (a.resource === "workflow" && typeof ref?.wf === "string") {
const wf = ref.wf;
try {
const d = await getJson(`/v1/conversations/${encodeURIComponent(conversationId)}/workflow-design?wf=${encodeURIComponent(wf)}`);
const design = {
wf,
slug: slugForDesign(d.saveLocation?.folder, a.title, wf),
title: a.title,
ir: d.ir,
degraded: d.degraded,
saveLocation: d.saveLocation,
saveStatus: d.saveStatus,
savedAt: d.savedAt
};
if (deps.enrichVcs && design.saveLocation) {
const loc = `${design.saveLocation.repo}/${design.saveLocation.folder}`;
try {
const folder = await deps.enrichVcs(design.saveLocation.repo, design.saveLocation.folder);
design.folderRef = folder.ref;
const liveIr = canonicalJson(design.ir);
const kept = [];
for (const f of folder.files) {
if (f.path === "design-ir.json") {
let savedMatches = false;
try {
savedMatches = canonicalJson(JSON.parse(f.contents)) === liveIr;
} catch {
savedMatches = false;
}
if (!savedMatches) {
kept.push({ path: "design-ir.saved.json", contents: f.contents });
warnings.push({
stage: "vcs",
ref: loc,
detail: "saved design-ir.json differs from the live IR \u2014 kept as design-ir.saved.json"
});
}
} else {
kept.push(f);
}
}
design.folderFiles = kept;
} catch (e) {
warnings.push({ stage: "vcs", ref: loc, detail: describeErr(e) });
}
}
designs.push(design);
} catch (e) {
warnings.push({ stage: "design", ref: wf, detail: describeErr(e) });
}
} else if (a.resource === "ui" && typeof ref?.wf === "string") {
const wf = ref.wf;
try {
const u = await getJson(
`/v1/conversations/${encodeURIComponent(conversationId)}/ui-design?wf=${encodeURIComponent(wf)}`
);
uiSpecs.push({ wf, slug: slugForDesign(void 0, a.title, wf), title: a.title, spec: u.spec });
} catch (e) {
warnings.push({ stage: "ui", ref: wf, detail: describeErr(e) });
}
} else if (a.resource === "vcs" && ref && typeof ref.repo === "string") {
const entry = {
resource: "vcs",
id: a.id,
title: a.title,
repo: ref.repo,
ref: ref.ref,
path: ref.path
};
references.push({ ...entry, pullHint: pullHintFor(entry, opts.hintTarget) });
} else if (a.resource && a.resource !== "workflow" && a.resource !== "ui") {
const entry = { resource: a.resource, id: a.id, title: a.title };
references.push({ ...entry, pullHint: pullHintFor(entry, opts.hintTarget) });
}
}
const parentOf = (childId) => childId.replace(/\/delegate-[^/]*$/, "");
const subagents = [];
const seen = /* @__PURE__ */ new Set();
const queue = [];
const enqueue = (childId, depth) => {
if (!seen.has(childId) && !queue.some((q) => q.childId === childId)) queue.push({ childId, depth });
};
for (const childId of Object.values(delegateChildIds)) if (typeof childId === "string") enqueue(childId, 0);
for (const childId of extractDelegateIds(transcript.messages, conversationId)) enqueue(childId, 0);
while (queue.length > 0) {
const { childId, depth } = queue.shift();
if (seen.has(childId)) continue;
seen.add(childId);
let childMessages = [];
try {
const body = await getJson(
`/v1/conversations/${encodeURIComponent(conversationId)}/children/${encodeURIComponent(childId)}/messages`
);
childMessages = body.messages ?? [];
subagents.push({ childId, parentId: parentOf(childId), depth, messages: childMessages });
} catch (e) {
warnings.push({ stage: "subagent", ref: childId, detail: describeErr(e) });
continue;
}
if (recursive && depth + 1 <= maxDepth) {
for (const grandchild of extractDelegateIds(childMessages, childId)) enqueue(grandchild, depth + 1);
}
}
return { conversationId, title, transcript, artifacts, designs, uiSpecs, subagents, references, warnings };
}
var DEFAULT_MAX_DEPTH;
var init_pull = __esm({
"packages/agent-targets/src/pull/pull.ts"() {
"use strict";
init_delegates();
DEFAULT_MAX_DEPTH = 8;
}
});
// packages/agent-targets/src/pull/write.ts
import { mkdir, writeFile } from "node:fs/promises";
import { join as join20, dirname as dirname19 } from "node:path";
async function put(dir, rel2, contents, written) {
const abs = join20(dir, rel2);
await mkdir(dirname19(abs), { recursive: true });
await writeFile(abs, contents);
written.push(rel2);
}
function renderToolOutput(output) {
const o = output;
if (o?.type === "text" || o?.type === "error-text") return clip(String(o.value ?? ""));
if (o?.type === "json" || o?.type === "error-json") return fence("json", JSON.stringify(o.value, null, 2));
if (o?.type === "content" && Array.isArray(o.value)) {
return o.value.map((part) => {
const p = part;
if (p?.type === "text") return p.text ?? "";
if (p?.type === "media") return `_[media: ${p.mediaType ?? "unknown"}]_`;
return fence("json", JSON.stringify(part, null, 2));
}).join("\n\n");
}
return fence("json", JSON.stringify(output, null, 2));
}
function renderPart(part) {
const p = part;
switch (p?.type) {
case "text":
return p.text ?? "";
case "reasoning":
return p.text ? `> _(reasoning)_ ${p.text.replace(/\n/g, "\n> ")}` : "";
case "tool-call":
return `**\u2192 \`${p.toolName ?? "tool"}\`**
${fence("json", JSON.stringify(p.input ?? {}, null, 2))}`;
case "tool-result":
return `**\u2190 \`${p.toolName ?? "tool"}\`**
${renderToolOutput(p.output)}`;
case "file":
case "image":
return `_[${p.type}: ${p.mediaType ?? "attachment"}]_`;
default:
return fence("json", JSON.stringify(part, null, 2));
}
}
function renderMessageContent(content) {
if (typeof content === "string") return content;
if (Array.isArray(content)) return content.map(renderPart).filter(Boolean).join("\n\n");
if (content == null) return "";
return fence("json", JSON.stringify(content, null, 2));
}
function transcriptMd(messages) {
return messages.map((m) => {
const r = m;
return `### ${r.role ?? "?"}
${renderMessageContent(r.content)}`;
}).join("\n\n");
}
async function writeBundle(bundle, dir) {
const written = [];
await put(
dir,
"index.json",
enc({
conversationId: bundle.conversationId,
title: bundle.title,
artifacts: bundle.artifacts,
designs: bundle.designs.map((d) => ({
wf: d.wf,
slug: d.slug,
title: d.title,
degraded: d.degraded,
saveLocation: d.saveLocation,
saveStatus: d.saveStatus,
savedAt: d.savedAt,
folderRef: d.folderRef,
folderFiles: (d.folderFiles ?? []).map((f) => f.path),
hasManifest: (d.folderFiles ?? []).some((f) => f.path === "manifest.json")
})),
uiSpecs: bundle.uiSpecs.map((u) => ({ wf: u.wf, slug: u.slug, title: u.title })),
subagents: bundle.subagents.map((s) => ({ childId: s.childId, parentId: s.parentId, depth: s.depth })),
references: bundle.references,
warnings: bundle.warnings
}),
written
);
await put(
dir,
"conversation.md",
`# ${bundle.title ?? bundle.conversationId}
${transcriptMd(bundle.transcript?.messages ?? [])}
`,
written
);
for (const d of bundle.designs) {
await put(dir, `designs/${d.slug}/design-ir.json`, enc(d.ir), written);
for (const f of d.folderFiles ?? []) {
await put(dir, `designs/${d.slug}/${f.path}`, f.contents, written);
}
}
for (const u of bundle.uiSpecs) await put(dir, `ui/${u.slug}.json`, enc(u.spec), written);
for (const s of bundle.subagents) {
await put(dir, `subagents/${slugChildId(s.childId)}.md`, `# ${s.childId}
${transcriptMd(s.messages)}
`, written);
}
return written;
}
var enc, CLIP, clip, fence, slugChildId;
var init_write = __esm({
"packages/agent-targets/src/pull/write.ts"() {
"use strict";
enc = (v) => JSON.stringify(v, null, 2) + "\n";
CLIP = 4e3;
clip = (s) => s.length > CLIP ? `${s.slice(0, CLIP)}
\u2026 (${s.length} chars total, clipped)` : s;
fence = (lang, body) => `\`\`\`${lang}
${clip(body)}
\`\`\``;
slugChildId = (id) => id.replace(/\//g, "__");
}
});
// packages/agent-targets/src/pull/vcs-reader.ts
import { execFile as execFile2 } from "node:child_process";
import { promisify } from "node:util";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir as tmpdir4 } from "node:os";
import { join as join21 } from "node:path";
function deriveVcsBaseUrl(apiBaseUrl) {
try {
const u = new URL(apiBaseUrl);
const labels = u.hostname.split(".");
if (labels.length < 2) return null;
return `https://vcs.${labels.slice(1).join(".")}`;
} catch {
return null;
}
}
async function refHasFolder(cwd, ref, folder) {
try {
const { stdout } = await execFileAsync("git", ["-C", cwd, "ls-tree", "--name-only", ref, "--", folder]);
return stdout.trim().length > 0;
} catch {
return false;
}
}
async function resolveFolderRef(cwd, folder) {
if (await refHasFolder(cwd, "origin/main", folder)) return "origin/main";
const { stdout } = await execFileAsync("git", [
"-C",
cwd,
"for-each-ref",
"--sort=-committerdate",
"--format=%(refname)",
"refs/remotes/origin/drafts"
]);
for (const ref of stdout.split("\n").map((s) => s.trim()).filter(Boolean)) {
if (await refHasFolder(cwd, ref, folder)) return ref;
}
return null;
}
async function readFolderAtRef(cwd, ref, folder) {
const { stdout } = await execFileAsync("git", ["-C", cwd, "ls-tree", "-r", "--name-only", ref, "--", folder]);
const paths = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
const files = [];
for (const p of paths) {
const { stdout: contents } = await execFileAsync("git", ["-C", cwd, "show", `${ref}:${p}`], {
maxBuffer: 32 * 1024 * 1024
});
const rel2 = p.startsWith(`${folder}/`) ? p.slice(folder.length + 1) : p;
files.push({ path: rel2, contents });
}
return files;
}
function createVcsFolderReader(opts) {
const { token } = opts;
const base = new URL(opts.vcsBaseUrl);
if (base.protocol !== "https:" && base.protocol !== "http:") {
throw new Error(`unsafe vcs base URL protocol: ${opts.vcsBaseUrl}`);
}
const clones = /* @__PURE__ */ new Map();
const cloneRepo = (repo) => {
if (!SAFE_REPO_RE.test(repo)) throw new Error(`unsafe repo name: ${JSON.stringify(repo)}`);
let p = clones.get(repo);
if (!p) {
p = (async () => {
const dir = await mkdtemp(join21(tmpdir4(), "mesh-vcs-"));
await execFileAsync("git", [
"clone",
"--filter=blob:none",
"--no-checkout",
"-c",
`http.extraHeader=Authorization: Bearer ${token}`,
"--",
`${base.origin}/git/${repo}`,
dir
]);
return dir;
})();
clones.set(repo, p);
}
return p;
};
const readPath = async (repo, path46) => {
const dir = await cloneRepo(repo);
const ref = await resolveFolderRef(dir, path46);
if (!ref) throw new Error(`path "${path46}" not found on any ref of ${repo}`);
const files = await readFolderAtRef(dir, ref, path46);
return { ref, files };
};
const cleanup = async () => {
await Promise.all(
[...clones.values()].map(async (p) => {
try {
await rm(await p, { recursive: true, force: true });
} catch {
}
})
);
};
return { enrichVcs: readPath, readPath, cleanup };
}
var execFileAsync, SAFE_REPO_RE;
var init_vcs_reader = __esm({
"packages/agent-targets/src/pull/vcs-reader.ts"() {
"use strict";
execFileAsync = promisify(execFile2);
SAFE_REPO_RE = /^[A-Za-z0-9._-]+$/;
}
});
// packages/agent-targets/src/pull/index.ts
var init_pull2 = __esm({
"packages/agent-targets/src/pull/index.ts"() {
"use strict";
init_types();
init_delegates();
init_pull();
init_write();
init_vcs_reader();
}
});
// packages/agent-targets/src/index.ts
var src_exports = {};
__export(src_exports, {
DEFAULT_APPROVAL_PATH_PREFIX: () => DEFAULT_APPROVAL_PATH_PREFIX,
DEFAULT_CONVERSATION_PATH_PREFIX: () => DEFAULT_CONVERSATION_PATH_PREFIX,
DEFAULT_REGISTRY_PATH: () => DEFAULT_REGISTRY_PATH,
RegistrySchema: () => RegistrySchema,
TargetEntrySchema: () => TargetEntrySchema,
approvalUrl: () => approvalUrl,
conversationUrl: () => conversationUrl,
createVcsFolderReader: () => createVcsFolderReader,
deriveVcsBaseUrl: () => deriveVcsBaseUrl,
extractDelegateIds: () => extractDelegateIds,
loadTargetsFile: () => loadTargetsFile,
normalizePathPrefix: () => normalizePathPrefix,
pullConversation: () => pullConversation,
renderMessageContent: () => renderMessageContent,
resolveEntry: () => resolveEntry,
slugForDesign: () => slugForDesign,
stripSlash: () => stripSlash,
writeBundle: () => writeBundle
});
var init_src2 = __esm({
"packages/agent-targets/src/index.ts"() {
"use strict";
init_schema();
init_resolve();
init_load();
init_links();
init_pull2();
}
});
// libs/mesh-cli/src/commands/agent-api-client.ts
function resolveTarget(opts) {
if (opts.apiUrl) {
return { apiBaseUrl: stripSlash(opts.apiUrl), loginContext: opts.context || DEFAULT_AUTH_CONTEXT };
}
const res = loadTargetsFile();
if (opts.target) {
if (res.status === "invalid") {
throw new TargetResolutionError(
`Registry at ${res.path} is invalid: ${res.message}. Fix it, or pass --api-url <url>.`
);
}
if (res.status === "absent") {
throw new TargetResolutionError(`No agent target given and ${NO_REGISTRY_GUIDANCE}.`);
}
const entry = res.targets[opts.target];
if (!entry) {
throw new TargetResolutionError(
`Unknown target "${opts.target}". Available: ${Object.keys(res.targets).join(", ") || "(none)"}. Add it with 'mesh agent-targets add ${opts.target}' or pass --api-url.`
);
}
return {
apiBaseUrl: entry.apiBaseUrl,
loginContext: entry.loginContext,
conversationPathPrefix: entry.conversationPathPrefix,
hubBaseUrl: entry.hubBaseUrl,
token: entry.token
};
}
if (res.status === "loaded") {
const entry = res.targets[res.defaultTarget];
return {
apiBaseUrl: entry.apiBaseUrl,
loginContext: entry.loginContext,
conversationPathPrefix: entry.conversationPathPrefix,
hubBaseUrl: entry.hubBaseUrl,
token: entry.token
};
}
if (res.status === "invalid") {
logWarn(`Agent-targets registry at ${res.path} is invalid: ${res.message}. Falling back to env/localhost.`);
}
const fallbackUrl = process.env.AGENT_API_URL || process.env.API_URL || "http://localhost:8787";
return { apiBaseUrl: stripSlash(fallbackUrl), loginContext: opts.context || DEFAULT_AUTH_CONTEXT };
}
async function authHeaders(target) {
if (target.token) return { Authorization: `Bearer ${target.token}` };
const ctx = target.loginContext || DEFAULT_AUTH_CONTEXT;
const token = await getValidToken(ctx);
if (token) return { Authorization: `Bearer ${token}` };
if (LOCAL_URL_RE.test(target.apiBaseUrl)) return { "X-Forwarded-User": "dev-user", "X-Forwarded-Email": "dev@localhost" };
throw new TargetResolutionError(`No valid credentials for context "${ctx}". Run: mesh login ${ctx}`);
}
async function agentApiFetch(target, apiPath) {
const headers = await authHeaders(target);
return fetch(`${target.apiBaseUrl}${apiPath}`, { headers, redirect: "manual" });
}
async function agentApiSend(target, apiPath, init) {
const headers = await authHeaders(target);
const body = init.json !== void 0 ? JSON.stringify(init.json) : init.body;
const contentType = init.json !== void 0 ? "application/json" : init.contentType ?? "application/octet-stream";
return fetch(`${target.apiBaseUrl}${apiPath}`, {
method: init.method,
headers: { ...headers, ...body === void 0 ? {} : { "content-type": contentType } },
// `as never` rather than `as BodyInit`: this package's lib config does not
// pull in DOM types, so the global name is unavailable even though Node's
// fetch accepts exactly these shapes at runtime.
...body === void 0 ? {} : { body },
redirect: "manual"
});
}
function describeNetworkError(err, target) {
const message = err instanceof Error ? err.message : String(err);
if (!NETWORK_ERROR_PATTERN.test(message)) return message;
if (LOCAL_URL_RE.test(target.apiBaseUrl)) {
return "Is the agent API running? Start it with: mesh dev";
}
const loginContext = target.loginContext ?? DEFAULT_AUTH_CONTEXT;
return `Couldn't reach ${target.apiBaseUrl} \u2014 check the URL/VPN/tailscale, or \`mesh login ${loginContext}\` if it's an auth redirect.`;
}
async function describeHttpError(res, target, ctx) {
const isRedirect = res.redirected || res.type === "opaqueredirect" || res.status === 302;
if (res.status === 401 || res.status === 403 || isRedirect) {
const loginContext = target.loginContext ?? DEFAULT_AUTH_CONTEXT;
return `auth did not reach the agent \u2014 run \`mesh login ${loginContext}\``;
}
if (res.status === 404) {
const idPart = ctx?.id ? ` (${ctx.id})` : "";
return `conversation/artifact not found${idPart}, or not owned by this identity \u2014 run \`mesh conversations list\` to see valid ids.`;
}
const bodyText = await res.text();
if (res.status === 503) {
try {
const parsed = JSON.parse(bodyText);
if (parsed.error === "conversation_unavailable") {
const message = parsed.detail ?? bodyText;
const recoverHint = ctx?.id ? `
try: mesh temporal recover-conversation ${ctx.id}` : "";
return `${message}${recoverHint}`;
}
} catch {
}
}
return `agent-api error ${res.status}: ${bodyText || res.statusText} (${target.apiBaseUrl})`;
}
var DEFAULT_AUTH_CONTEXT, LOCAL_URL_RE, TargetResolutionError, NO_REGISTRY_GUIDANCE, NETWORK_ERROR_PATTERN;
var init_agent_api_client = __esm({
"libs/mesh-cli/src/commands/agent-api-client.ts"() {
"use strict";
init_src2();
init_login();
init_log();
DEFAULT_AUTH_CONTEXT = "mesh.dev";
LOCAL_URL_RE = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/i;
TargetResolutionError = class extends Error {
};
NO_REGISTRY_GUIDANCE = "no registry \u2014 add `mesh agent-targets add <name>` or pass --api-url";
NETWORK_ERROR_PATTERN = /ECONNREFUSED|fetch failed|ENOTFOUND|EAI_AGAIN/;
}
});
// libs/mesh-cli/src/commands/artifacts.ts
import * as fs22 from "fs";
import * as path24 from "path";
function parseRef(ref) {
const colonIdx = ref.indexOf(":");
if (colonIdx === -1) {
return { conversationId: ref, artifactId: DEFAULT_ARTIFACT_ID };
}
return {
conversationId: ref.slice(0, colonIdx),
artifactId: ref.slice(colonIdx + 1)
};
}
async function fetchExportBundle(target, conversationId, artifactId) {
const apiPath = `/v1/conversations/${encodeURIComponent(conversationId)}/artifacts/${encodeURIComponent(artifactId)}/export`;
const response = await agentApiFetch(target, apiPath);
if (!response.ok) {
throw new Error(await describeHttpError(response, target, { id: conversationId }));
}
return await response.json();
}
async function getArtifact(ref, options, target) {
const { conversationId, artifactId } = parseRef(ref);
logInfo(`Fetching ${conversationId}:${artifactId} from ${target.apiBaseUrl}\u2026`);
const bundle = await fetchExportBundle(target, conversationId, artifactId);
const outputDir = options.output ?? path24.join("workflows", bundle.primaryWorkflow);
fs22.mkdirSync(outputDir, { recursive: true });
let filesWritten = 0;
for (const file of bundle.files) {
const filePath = path24.join(outputDir, file.filename);
fs22.writeFileSync(filePath, file.code, "utf-8");
logSuccess(` ${file.filename}`);
filesWritten++;
}
if (bundle.overview) {
const readmePath = path24.join(outputDir, "README.md");
fs22.writeFileSync(readmePath, bundle.overview, "utf-8");
logSuccess(` README.md`);
filesWritten++;
}
if (bundle.ir) {
const irPath = path24.join(outputDir, "workflow-ir.json");
fs22.writeFileSync(irPath, JSON.stringify(bundle.ir, null, 2), "utf-8");
logSuccess(` workflow-ir.json`);
filesWritten++;
}
const metaPath = path24.join(outputDir, ".workflow-meta.json");
const meta = {
conversationId: bundle.conversationId,
artifactId,
primaryWorkflow: bundle.primaryWorkflow,
exportedAt: bundle.exportedAt,
files: bundle.files.map((f) => f.filename)
};
fs22.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
console.log();
logSuccess(
`Exported ${filesWritten} files to ${outputDir}/`
);
if (bundle.files.length > 1) {
const primary = bundle.files.find((f) => f.name === bundle.primaryWorkflow);
if (primary) {
logInfo(`Entry point: ${primary.filename}`);
}
}
logInfo(`Ref: ${conversationId}:${artifactId}`);
}
function registerArtifactsCommands(program2) {
const artifacts = program2.command("artifacts").description("Manage workflow artifacts from AI agent conversations");
artifacts.command("get <ref>").description(
`Download artifact files from an agent conversation
Ref format: <conversationId>:<artifactId>
Short form uses default artifactId "${DEFAULT_ARTIFACT_ID}"`
).option("-o, --output <dir>", "Output directory (default: workflows/<workflowName>)").option("--target <name>", "Named agent target from the agent-targets registry").option("--api-url <url>", "Agent API URL (overrides --target; ad-hoc, no registry lookup)").option("--context <ctx>", `Zitadel auth context, used with --api-url (default: ${DEFAULT_AUTH_CONTEXT2})`).action(async (ref, opts) => {
let target;
try {
target = resolveTarget(opts);
} catch (error) {
logError(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
return;
}
try {
await getArtifact(ref, opts, target);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(message);
const hint = describeNetworkError(error, target);
if (hint !== message) {
logWarn(hint);
logWarn("Or specify the URL: mesh artifacts get <ref> --api-url https://your-agent-api.example.com");
}
process.exitCode = 1;
}
});
}
var DEFAULT_ARTIFACT_ID, DEFAULT_AUTH_CONTEXT2;
var init_artifacts = __esm({
"libs/mesh-cli/src/commands/artifacts.ts"() {
"use strict";
init_log();
init_agent_api_client();
DEFAULT_ARTIFACT_ID = "workflow-artifact";
DEFAULT_AUTH_CONTEXT2 = "mesh.dev";
}
});
// libs/mesh-cli/src/commands/cluster.ts
import { spawnSync as spawnSync3 } from "node:child_process";
async function resolveClusterAccess(opts) {
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
throw new Error(
"Not in a Mesh app directory (no Pulumi.yaml found). Run from an app directory."
);
}
const stack = opts.stack ?? getCurrentStack(appRoot);
if (!stack) {
const stacks = findStackConfigs(appRoot);
throw new Error(
"No Pulumi stack selected.\n" + (stacks.length > 0 ? `Available: ${stacks.join(", ")}
Use --stack <name> (the deployed stack, e.g. dev).` : "No stack configs found in this directory.")
);
}
const sa = ["--stack", stack];
const deployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
const ctx = derivePlatformContext(appRoot, stack);
const resolved = deployerRole ? await resolveAwsCredentials(deployerRole, appRoot, stack) : null;
if (deployerRole && !resolved && !process.env.AWS_ACCESS_KEY_ID) {
const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx) : null;
throw new Error(
pf?.message ?? `Couldn't resolve AWS credentials. Run: mesh login ${ctx ?? "mesh.dev"} --device`
);
}
const awsEnv = resolved?.env;
const region = readStackConfig(appRoot, stack, "aws:region");
if (awsEnv && region && !awsEnv.AWS_REGION) awsEnv.AWS_REGION = region;
if (awsEnv) Object.assign(process.env, awsEnv);
let appOutput;
let devOutput;
try {
appOutput = JSON.parse(pulumiStackOutput(appRoot, "app", sa, awsEnv));
devOutput = appOutput.dev ?? appOutput;
} catch {
try {
devOutput = JSON.parse(pulumiStackOutput(appRoot, "dev", sa, awsEnv));
appOutput = devOutput;
} catch {
throw new Error(
`Could not read stack output for '${stack}'. Ensure the app is deployed: mesh deploy up`
);
}
}
const platformEnv = devOutput?.platform?.env ?? "dev";
const platformName = resolveHubPlatformName(devOutput?.platform);
const namespace = appOutput?.namespace ?? devOutput?.namespace;
if (!namespace) {
throw new Error(`No namespace found in stack output for '${stack}'.`);
}
let failure;
const kubeconfigPath = await ensureKubeconfig(
platformName,
platformEnv,
`mesh-cluster-${platformName}-${platformEnv}-${stack}`,
{ onError: (f) => failure = f }
);
if (!kubeconfigPath) {
throw new Error(clusterResolveErrorMessage(platformName, platformEnv, failure));
}
return { kubeconfigPath, namespace, stack };
}
function clusterResolveErrorMessage(platformName, platformEnv, failure) {
const parameter = failure?.parameter ?? `/mesh-platform/${platformName}/${platformEnv}/core/eks`;
const err = failure?.error;
const detail = err instanceof Error ? `${err.name}: ${err.message}` : err !== void 0 ? String(err) : "no cluster data returned";
return `Could not resolve the EKS cluster for platform '${platformName}/${platformEnv}' from SSM.
Parameter tried: ${parameter}
Failure: ${detail}
If this platform name is wrong, check the app's \`mesh:platform\` stack config (the hub platform name, e.g. \`trabian\`) \u2014 an AccessDeniedException here usually means the WRONG platform path, not missing SSM permissions.`;
}
function hasNamespaceFlag(args) {
return args.some((a) => NS_FLAGS.has(a) || a.startsWith("--namespace="));
}
function runKubectl(access, args, opts = {}) {
const finalArgs = opts.defaultNamespace !== false && !hasNamespaceFlag(args) ? ["-n", access.namespace, ...args] : args;
const res = spawnSync3("kubectl", finalArgs, {
stdio: "inherit",
env: { ...process.env, KUBECONFIG: access.kubeconfigPath }
});
if (res.error) {
const e = res.error;
if (e.code === "ENOENT") {
logError("kubectl not found on PATH. Install kubectl to use mesh cluster commands.");
} else {
logError(`Failed to run kubectl: ${e.message}`);
}
return 1;
}
return res.status ?? 0;
}
function targetToSelector(target) {
if (target.includes("/")) return [target];
if (target.includes("=")) return ["-l", target];
return ["-l", `app=${target}`];
}
function registerClusterCommands(program2) {
program2.command("kubectl").description("Run kubectl against the app's cluster (deployer role + SSM kubeconfig)").option("--stack <stack>", "Deployed Pulumi stack (default: current selection)").allowUnknownOption(true).allowExcessArguments(true).helpOption(false).action(async (opts, cmd) => {
try {
const access = await resolveClusterAccess({ stack: opts.stack });
process.stderr.write(
`\u2192 kubectl \xB7 ns ${access.namespace} \xB7 stack ${access.stack}
`
);
process.exit(runKubectl(access, cmd.args));
} catch (e) {
logError(e.message);
process.exit(1);
}
});
program2.command("logs [target]").description("Tail logs from the app's pods (kubectl logs; target = service, deployment/x, or k=v)").option("--stack <stack>", "Deployed Pulumi stack (default: current selection)").option("-f, --follow", "Stream new logs").option("--tail <n>", "Lines from the end of the logs", "200").option("-c, --container <name>", "Container name").option("--previous", "Logs from the previous container instance (crash debugging)").action(
async (target, opts) => {
try {
const access = await resolveClusterAccess({ stack: opts.stack });
if (!target) {
logInfo(
`Which service? Pods in ${access.namespace}:
Then: mesh logs <service> (e.g. mesh logs demo-agent-worker)
`
);
process.exit(runKubectl(access, ["get", "pods"]));
}
const kargs = ["logs", ...targetToSelector(target), "--prefix", "--tail", opts.tail ?? "200"];
if (opts.follow) kargs.push("-f");
if (opts.container) kargs.push("-c", opts.container);
if (opts.previous) kargs.push("--previous");
process.exit(runKubectl(access, kargs));
} catch (e) {
logError(e.message);
process.exit(1);
}
}
);
program2.command("exec <target> [cmd...]").description("Exec a command in the app's pod (kubectl exec; target = service or deployment/x)").option("--stack <stack>", "Deployed Pulumi stack (default: current selection)").option("-c, --container <name>", "Container name").action(
async (target, cmdParts, opts) => {
try {
const access = await resolveClusterAccess({ stack: opts.stack });
const podRef = target.includes("/") ? target : `deployment/${target}`;
const command = cmdParts.length > 0 ? cmdParts : ["sh"];
const kargs = ["exec"];
kargs.push(process.stdout.isTTY ? "-it" : "-i");
kargs.push(podRef);
if (opts.container) kargs.push("-c", opts.container);
kargs.push("--", ...command);
process.exit(runKubectl(access, kargs));
} catch (e) {
logError(e.message);
process.exit(1);
}
}
);
}
var NS_FLAGS;
var init_cluster = __esm({
"libs/mesh-cli/src/commands/cluster.ts"() {
"use strict";
init_log();
init_pulumi();
init_aws_auth();
init_kubeconfig();
init_login();
init_pulumi_run();
NS_FLAGS = /* @__PURE__ */ new Set(["-n", "--namespace", "-A", "--all-namespaces"]);
}
});
// libs/mesh-cli/src/commands/vcs-enrich.ts
async function buildVcsEnricher(target) {
const vcsBaseUrl = deriveVcsBaseUrl(target.apiBaseUrl);
if (!vcsBaseUrl) {
logWarn(`could not derive a vcs URL from ${target.apiBaseUrl}; skipping design-folder enrichment (--no-vcs to silence)`);
return void 0;
}
const token = target.token ?? await getValidToken(target.loginContext ?? "mesh.dev");
if (!token) {
logWarn(`no vcs token for context "${target.loginContext ?? "mesh.dev"}"; skipping design-folder enrichment`);
return void 0;
}
return createVcsFolderReader({ vcsBaseUrl, token });
}
var init_vcs_enrich = __esm({
"libs/mesh-cli/src/commands/vcs-enrich.ts"() {
"use strict";
init_src2();
init_login();
init_log();
}
});
// libs/mesh-cli/src/commands/conversations.ts
function truncate(s, max) {
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
}
function formatConversations(list, asJson) {
if (asJson) {
return JSON.stringify(list);
}
if (list.length === 0) {
return "(no conversations)";
}
const rows = list.map((c) => ({
id: c.id ?? "",
title: truncate(c.title ?? "", MAX_TITLE_LEN),
updated: c.updatedAt ?? c.lastUpdated ?? ""
}));
const idWidth = Math.max("ID".length, ...rows.map((r) => r.id.length));
const titleWidth = Math.max("TITLE".length, ...rows.map((r) => r.title.length));
const pad = (s, width) => s.padEnd(width);
const header = `${pad("ID", idWidth)} ${pad("TITLE", titleWidth)} UPDATED`;
const lines = rows.map((r) => `${pad(r.id, idWidth)} ${pad(r.title, titleWidth)} ${r.updated}`);
return [header, ...lines].join("\n");
}
function summarizeRefs(items) {
return items.map((item) => {
if (typeof item === "string") return item;
if (item && typeof item === "object") {
const rec = item;
const label = rec.id ?? rec.name ?? rec.toolName;
if (typeof label === "string") return label;
}
return "?";
}).join(", ");
}
function formatTranscript(body, asJson) {
if (asJson) {
return JSON.stringify(body);
}
const messages = body.messages ?? [];
if (messages.length === 0) {
return "(no messages)";
}
return messages.map((m) => {
const lines = [`${m.role}: ${renderMessageContent(m.content)}`];
if (Array.isArray(m.toolCalls) && m.toolCalls.length > 0) {
lines.push(` tool-calls: ${summarizeRefs(m.toolCalls)}`);
}
if (Array.isArray(m.artifactRefs) && m.artifactRefs.length > 0) {
lines.push(` artifacts: ${summarizeRefs(m.artifactRefs)}`);
}
return lines.join("\n");
}).join("\n\n");
}
function formatArtifacts(list, asJson, refHint) {
if (asJson) {
return JSON.stringify(list);
}
if (list.length === 0) {
return "(no artifacts)";
}
const rows = list.map((a) => ({
id: a.id ?? "",
kind: a.kind ?? "",
title: a.title ?? ""
}));
const idWidth = Math.max("ARTIFACT-ID".length, ...rows.map((r) => r.id.length));
const kindWidth = Math.max("KIND".length, ...rows.map((r) => r.kind.length));
const pad = (s, width) => s.padEnd(width);
const header = `${pad("ARTIFACT-ID", idWidth)} \xB7 ${pad("KIND", kindWidth)} \xB7 TITLE`;
const lines = rows.map((r) => `${pad(r.id, idWidth)} \xB7 ${pad(r.kind, kindWidth)} \xB7 ${r.title}`);
const exampleId = rows[0]?.id ?? "";
return [header, ...lines, "", `Download: mesh artifacts get ${refHint}:${exampleId}`].join("\n");
}
function withTargetOptions(cmd) {
return cmd.option("--target <name>", "Named agent target from the agent-targets registry").option("--api-url <url>", "Agent API URL (overrides --target; ad-hoc, no registry lookup)").option("--context <ctx>", "Zitadel auth context, used with --api-url (default: mesh.dev)").option("--json", "Emit JSON (default when stdout is not a TTY)");
}
async function runConversationsVerb(opts, apiPath, format, ctx) {
let target;
try {
target = resolveTarget(opts);
const res = await agentApiFetch(target, apiPath);
if (!res.ok) {
throw new Error(await describeHttpError(res, target, ctx));
}
const body = await res.json();
const asJson = opts.json ?? !process.stdout.isTTY;
process.stdout.write(format(body, asJson) + "\n");
} catch (error) {
reportCliError(error, target);
}
}
function reportCliError(error, target) {
if (error instanceof TargetResolutionError) {
logError(error.message);
} else {
const message = error instanceof Error ? error.message : String(error);
logError(message);
if (target) {
const hint = describeNetworkError(error, target);
if (hint !== message) logWarn(hint);
}
}
process.exitCode = 1;
}
function registerConversationsCommands(program2) {
const group = program2.command("conversations").alias("conv").description("Read a deployed Mesh agent's conversations (list, show, artifacts)");
withTargetOptions(
group.command("list").description("List the caller's conversations on an agent target")
).action(async (opts) => {
await runConversationsVerb(
opts,
"/v1/conversations",
(body, asJson) => formatConversations(body.conversations ?? [], asJson)
);
});
withTargetOptions(
group.command("show <id>").description("Render a conversation's transcript")
).action(async (id, opts) => {
await runConversationsVerb(
opts,
`/v1/conversations/${encodeURIComponent(id)}`,
(body, asJson) => formatTranscript(body, asJson),
{ id }
);
});
withTargetOptions(
group.command("artifacts <id>").description("List a conversation's artifacts")
).action(async (id, opts) => {
await runConversationsVerb(
opts,
`/v1/conversations/${encodeURIComponent(id)}/artifacts`,
(body, asJson) => formatArtifacts(body.artifacts ?? [], asJson, id),
{ id }
);
});
group.command("pull <id>").description("Pull a conversation + its designs/UI + recursive subagent transcripts into a bundle").option("--target <name>", "Named agent target from the agent-targets registry").option("--api-url <url>", "Agent API URL (overrides --target)").option("--context <ctx>", "Zitadel auth context (default: mesh.dev)").option("-o, --output <dir>", "Write the bundle directory here (prints JSON when omitted)").option("--no-recursive", "Only pull direct subagents (default: recurse the delegate tree)").option("--no-vcs", "Skip pulling each design's own vcs folder (docs/manifest); IR-only").action(async (id, opts) => {
const { pullConversation: pullConversation2, writeBundle: writeBundle2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
let target;
let enricher;
try {
target = resolveTarget(opts);
const resolved = target;
enricher = opts.vcs === false ? void 0 : await buildVcsEnricher(resolved);
const bundle = await pullConversation2(
{ fetchApi: (apiPath) => agentApiFetch(resolved, apiPath), enrichVcs: enricher?.enrichVcs },
id,
{ recursive: opts.recursive !== false, hintTarget: opts.target }
);
if (opts.output) {
const written = await writeBundle2(bundle, opts.output);
logInfo(`wrote ${written.length} files \u2192 ${opts.output}`);
if (bundle.warnings.length) {
logWarn(
`${bundle.warnings.length} warning(s): ${bundle.warnings.map((w) => `${w.stage}:${w.ref ?? ""}`).join(", ")}`
);
}
} else {
process.stdout.write(JSON.stringify(bundle) + "\n");
}
} catch (error) {
reportCliError(error, target);
} finally {
await enricher?.cleanup();
}
});
}
var MAX_TITLE_LEN;
var init_conversations = __esm({
"libs/mesh-cli/src/commands/conversations.ts"() {
"use strict";
init_src2();
init_vcs_enrich();
init_log();
init_agent_api_client();
MAX_TITLE_LEN = 40;
}
});
// libs/mesh-cli/src/utils/scaffold-versions.ts
import { execFileSync as execFileSync17 } from "child_process";
function isPrerelease(range) {
return /[-+]/.test(range.replace(/^\^/, ""));
}
function toCaretRange(version) {
const trimmed = version.trim();
return /^\d+\.\d+\.\d+(?:[-+].*)?$/.test(trimmed) ? `^${trimmed}` : null;
}
function resolvePublishedRange(pkg, cwd) {
try {
const out = execFileSync17("npm", ["view", pkg, "version"], {
cwd,
encoding: "utf-8",
timeout: 15e3,
stdio: ["ignore", "pipe", "ignore"]
});
const range = toCaretRange(out);
if (range && !isPrerelease(range)) return { range, resolved: true };
} catch {
}
return { range: SCAFFOLD_PACKAGE_LINES[pkg], resolved: false };
}
var SCAFFOLD_PACKAGE_LINES, SCAFFOLD_TOOLCHAIN;
var init_scaffold_versions = __esm({
"libs/mesh-cli/src/utils/scaffold-versions.ts"() {
"use strict";
SCAFFOLD_PACKAGE_LINES = {
"@mesh-tech/app-kit": "^1.45.0",
"@mesh-tech/esm-bundle": "^0.1.1"
};
SCAFFOLD_TOOLCHAIN = {
"@types/node": "^24.2.1",
tsx: "^4.20.4",
typescript: "^5.9.2"
};
}
});
// libs/mesh-cli/src/commands/create-app.ts
var create_app_exports = {};
__export(create_app_exports, {
NO_REGISTRY_ACCESS_MESSAGE: () => NO_REGISTRY_ACCESS_MESSAGE,
PLATFORM_MONOREPO_NAME: () => PLATFORM_MONOREPO_NAME,
bootstrapAppsRepo: () => bootstrapAppsRepo,
composableNextSteps: () => composableNextSteps,
copyTemplate: () => copyTemplate,
describeNoAppsHome: () => describeNoAppsHome,
ensureRegistryAccess: () => ensureRegistryAccess,
ensureWorkspaceGlobs: () => ensureWorkspaceGlobs,
generateComposableApp: () => generateComposableApp,
isInsidePlatformMonorepo: () => isInsidePlatformMonorepo,
registerCreateAppCommand: () => registerCreateAppCommand,
resolveAppDir: () => resolveAppDir,
shouldBootstrapAppsRepo: () => shouldBootstrapAppsRepo,
workspaceGlobForApp: () => workspaceGlobForApp
});
import * as fs23 from "fs";
import * as os7 from "os";
import * as path25 from "path";
import { fileURLToPath as fileURLToPath2 } from "url";
import Handlebars from "handlebars";
import { parse as parseYaml2 } from "yaml";
function isInsidePlatformMonorepo(dir) {
let cur = path25.resolve(dir);
while (cur !== path25.dirname(cur)) {
if (fs23.existsSync(path25.join(cur, "pnpm-workspace.yaml"))) {
try {
const pkg = JSON.parse(fs23.readFileSync(path25.join(cur, "package.json"), "utf-8"));
if (pkg?.name === PLATFORM_MONOREPO_NAME) return true;
} catch {
}
}
cur = path25.dirname(cur);
}
return false;
}
function resolveDeployerRoleArn(tenant, platformName, env) {
const account = HUB_ACCOUNTS[platformName] ?? "ACCOUNT_ID";
return `arn:aws:iam::${account}:role/${tenant}-${env}-apps-deployer`;
}
function shouldBootstrapAppsRepo(cwd) {
return fs23.existsSync(path25.join(cwd, ".git")) && !isInsidePlatformMonorepo(cwd) && !fs23.existsSync(path25.join(cwd, "package.json")) && !fs23.existsSync(path25.join(cwd, "tenants")) && !fs23.existsSync(path25.join(cwd, "apps"));
}
function bootstrapAppsRepo(cwd, tenant) {
const templateDir = path25.join(packageRoot, "templates", "apps-repo");
const registryAccount = HUB_ACCOUNTS.mesh;
const context = {
repoName: path25.basename(cwd),
tenant,
tenantTitle: tenant.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "),
registryUrl: `https://mesh-platform-${registryAccount}.d.codeartifact.us-east-2.amazonaws.com/npm/mesh-packages/`,
intentRange: INTENT_RANGE
};
const staging = fs23.mkdtempSync(path25.join(os7.tmpdir(), "mesh-apps-repo-"));
try {
copyTemplate(templateDir, staging, context);
for (const [from, to] of [
["gitignore", ".gitignore"],
["npmrc", ".npmrc"]
]) {
const p = path25.join(staging, from);
if (fs23.existsSync(p)) fs23.renameSync(p, path25.join(staging, to));
}
const created = [];
for (const entry of fs23.readdirSync(staging)) {
const dest = path25.join(cwd, entry);
if (fs23.existsSync(dest)) continue;
fs23.cpSync(path25.join(staging, entry), dest, { recursive: true });
created.push(entry);
}
fs23.mkdirSync(path25.join(cwd, "apps"), { recursive: true });
created.push("apps/");
return created;
} finally {
fs23.rmSync(staging, { recursive: true, force: true });
}
}
function ensureWorkspaceGlobs(root, required) {
const file = path25.join(root, "pnpm-workspace.yaml");
if (!fs23.existsSync(file)) return [];
const text = fs23.readFileSync(file, "utf-8");
let declared;
try {
declared = parseYaml2(text)?.packages ?? [];
} catch {
return [];
}
const missing = required.filter((glob) => !declared.includes(glob));
if (missing.length === 0) return [];
const lines = text.split("\n");
const start = lines.findIndex((line) => /^packages:\s*$/.test(line));
if (start === -1) return [];
let end = start;
for (let i = start + 1; i < lines.length; i++) {
const line = lines[i] ?? "";
const isEntry = /^\s+-\s/.test(line);
if (isEntry || /^\s*#/.test(line) || line.trim() === "") {
if (isEntry) end = i;
continue;
}
break;
}
const indent = (lines[end] ?? "").match(/^(\s*)-/)?.[1] ?? " ";
lines.splice(end + 1, 0, ...missing.map((glob) => `${indent}- ${glob}`));
fs23.writeFileSync(file, lines.join("\n"));
return missing;
}
function workspaceGlobForApp(root, appDir) {
const rel2 = path25.relative(root, appDir).split(path25.sep).join("/");
if (rel2 === "" || rel2.split("/")[0] === "..") return null;
return `${path25.posix.dirname(rel2)}/*/*`;
}
function maybeBootstrapAppsRepo(cwd, tenant, test) {
if (test || !shouldBootstrapAppsRepo(cwd)) return;
const created = bootstrapAppsRepo(cwd, tenant);
logInfo(`Standalone apps repo detected \u2014 bootstrapped workspace files: ${created.join(", ")}`);
}
function isInteractive() {
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
}
async function promptForOptions(options) {
const { default: input2 } = await import("@inquirer/input");
const { default: checkbox } = await import("@inquirer/checkbox");
const tenant = options.tenant ?? await input2({
message: "Tenant name",
default: findMeshJson(process.cwd())?.data.tenant,
validate: (v) => v.length > 0 ? true : "Tenant name is required"
});
const name = options.name ?? await input2({
message: "App name",
validate: (v) => v.length > 0 ? true : "App name is required"
});
let primitives;
if (options.primitives) {
primitives = parsePrimitives(options.primitives);
} else {
primitives = await checkbox({
message: "Select primitives",
choices: VALID_PRIMITIVES.map((p) => ({
name: PRIMITIVES[p],
value: p,
checked: p === "service"
// service checked by default
}))
});
if (primitives.length === 0) {
primitives = ["service"];
}
}
if (primitives.includes("database") && !primitives.includes("service")) {
primitives.push("service");
logInfo("Added 'service' \u2014 database files live in the api/ directory.");
}
if (primitives.includes("bucket") && !primitives.includes("service")) {
primitives.push("service");
logInfo("Added 'service' \u2014 bucket requires an API service for S3 helpers.");
}
return { tenant, name, primitives };
}
function copyTemplate(srcDir, destDir, context) {
const entries = fs23.readdirSync(srcDir, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path25.join(srcDir, entry.name);
let destName = entry.name;
if (destName.endsWith(".hbs")) {
destName = destName.slice(0, -4);
}
if (destName.includes("{{")) {
destName = Handlebars.compile(destName)(context);
}
const destPath = path25.join(destDir, destName);
if (entry.isDirectory()) {
fs23.mkdirSync(destPath, { recursive: true });
copyTemplate(srcPath, destPath, context);
} else if (entry.name.endsWith(".hbs")) {
const templateContent = fs23.readFileSync(srcPath, "utf-8");
const template = Handlebars.compile(templateContent);
const output = template(context);
fs23.writeFileSync(destPath, output);
} else {
fs23.copyFileSync(srcPath, destPath);
}
}
}
function parsePrimitives(input2) {
const selected = input2.split(",").map((s) => s.trim().toLowerCase());
const invalid = selected.filter(
(s) => !VALID_PRIMITIVES.includes(s)
);
if (invalid.length > 0) {
logError(`Unknown primitives: ${invalid.join(", ")}`);
logInfo(`Available primitives: ${VALID_PRIMITIVES.join(", ")}`);
process.exit(1);
}
return selected;
}
function describeNoAppsHome(cwd, tenant, baseDir, test) {
if (test || isInsidePlatformMonorepo(cwd)) {
return {
message: `Tenant directory not found: ${baseDir}/${tenant}/`,
hint: "Create the tenant directory first, or run from within it."
};
}
return {
message: `This directory is not a Mesh apps repo \u2014 no apps/ here${fs23.existsSync(path25.join(cwd, ".git")) ? "" : " and it is not a git repo"}.`,
hint: `Run: mesh init (turns this folder into your ${tenant}-mesh-apps repo), or cd into that repo and re-run mesh create-app.`
};
}
function resolveAppDir(cwd, tenant, name, test) {
const baseDir = test ? "tests/tenants" : "tenants";
const possiblePaths = test ? [path25.join(cwd, baseDir, tenant, "apps")] : [
path25.join(cwd, baseDir, tenant, "apps"),
path25.join(cwd, "..", tenant, "apps"),
path25.join(cwd, "apps")
];
let appsDir = possiblePaths.find((p) => fs23.existsSync(p));
if (!appsDir) {
appsDir = path25.join(cwd, baseDir, tenant, "apps");
if (!fs23.existsSync(path25.join(cwd, baseDir, tenant))) {
const { message, hint } = describeNoAppsHome(cwd, tenant, baseDir, test);
logError(message);
logInfo(hint);
process.exit(1);
}
if (!fs23.existsSync(appsDir)) {
logInfo(`Creating apps directory: ${appsDir}`);
fs23.mkdirSync(appsDir, { recursive: true });
}
}
return path25.join(appsDir, name);
}
function generateComposableApp(appDir, context) {
const fragmentsDir = path25.join(packageRoot, "fragments");
const baseDir = path25.join(fragmentsDir, "base");
if (!fs23.existsSync(baseDir)) {
logError(`Fragments directory not found: ${baseDir}`);
logInfo("Make sure the mesh-cli package is installed correctly.");
process.exit(1);
}
copyTemplate(baseDir, appDir, context);
if (context.service) {
const serviceDir = path25.join(fragmentsDir, "service");
if (!fs23.existsSync(serviceDir)) {
throw new Error(`Fragment directory not found: ${serviceDir}`);
}
copyTemplate(serviceDir, appDir, context);
}
if (context.database) {
const dbDir = path25.join(fragmentsDir, "database");
if (!fs23.existsSync(dbDir)) {
throw new Error(`Fragment directory not found: ${dbDir}`);
}
copyTemplate(dbDir, appDir, context);
}
if (context.temporal) {
const temporalDir = path25.join(fragmentsDir, "temporal");
if (!fs23.existsSync(temporalDir)) {
throw new Error(`Fragment directory not found: ${temporalDir}`);
}
copyTemplate(temporalDir, appDir, context);
}
if (context.bucket) {
const bucketDir = path25.join(fragmentsDir, "bucket");
if (!fs23.existsSync(bucketDir)) {
throw new Error(`Fragment directory not found: ${bucketDir}`);
}
copyTemplate(bucketDir, appDir, context);
}
const agentsDir = path25.join(fragmentsDir, "agents");
if (fs23.existsSync(agentsDir)) {
copyTemplate(agentsDir, appDir, context);
}
}
function registerCreateAppCommand(program2) {
program2.command("create-app").description("Scaffold a new tenant application").option("--tenant <tenant>", "Tenant name (e.g., acme)").option("--name <name>", "Application name (e.g., billing)").option(
"--template <template>",
`Legacy template: ${Object.keys(TEMPLATES).join(", ")}`
).option(
"--primitives <primitives>",
`Comma-separated primitives: ${VALID_PRIMITIVES.join(", ")} (default: service)`
).option("--test", "Create in tests/tenants/ directory", false).option(
"--skip-registry-check",
"Scaffold without checking that this machine can install @mesh-tech packages (offline use; run `mesh registry login` before `pnpm install`)",
false
).action(async (options) => {
await ensureRegistryAccess(process.cwd(), options);
if (options.template) {
if (!options.tenant || !options.name) {
logError("--tenant and --name are required with --template");
process.exit(1);
}
await runLegacyTemplate(
options.tenant,
options.name,
options.template,
options.test
);
return;
}
let tenant;
let name;
let primitives;
const needsPrompt = !options.tenant || !options.name || !options.primitives;
if (needsPrompt && isInteractive()) {
const prompted = await promptForOptions(options);
tenant = prompted.tenant;
name = prompted.name;
primitives = prompted.primitives;
} else if (needsPrompt) {
options.tenant ??= findMeshJson(process.cwd())?.data.tenant;
if (!options.tenant) {
logError("--tenant is required (no TTY for interactive mode)");
process.exit(1);
}
if (!options.name) {
logError("--name is required (no TTY for interactive mode)");
process.exit(1);
}
tenant = options.tenant;
name = options.name;
primitives = options.primitives ? parsePrimitives(options.primitives) : ["service"];
} else {
tenant = options.tenant;
name = options.name;
primitives = parsePrimitives(options.primitives);
}
if (primitives.includes("database") && !primitives.includes("service")) {
primitives.push("service");
logInfo("Added 'service' \u2014 database files live in the api/ directory.");
}
if (primitives.includes("bucket") && !primitives.includes("service")) {
primitives.push("service");
logInfo("Added 'service' \u2014 bucket requires an API service for S3 helpers.");
}
await runComposable(tenant, name, primitives, options.test);
});
}
async function ensureRegistryAccess(cwd, options, preflight = registryPreflight) {
if (isInsidePlatformMonorepo(cwd)) return;
if (options.skipRegistryCheck) {
logWarn("Skipping the registry check \u2014 `pnpm install` will need `mesh registry login` first.");
return;
}
const pf = await preflight();
if (pf.state === "unreachable") {
logWarn(`Could not reach the package registry to verify access (${pf.detail ?? "unknown error"}) \u2014 continuing.`);
return;
}
if (pf.state !== "valid") {
throw new MeshCliError(NO_REGISTRY_ACCESS_MESSAGE);
}
logSuccess(`Registry access OK (${pf.email ?? "token valid"})`);
}
async function runLegacyTemplate(tenant, name, template, test) {
if (!Object.keys(TEMPLATES).includes(template)) {
const removed = REMOVED_TEMPLATES[template];
logError(`Unknown template: ${template}${removed ? ` (${removed})` : ""}`);
logInfo(`Available templates:`);
for (const [tplName, desc] of Object.entries(TEMPLATES)) {
console.log(` ${tplName.padEnd(20)} - ${desc}`);
}
process.exit(1);
}
logInfo(`Creating app '${name}' for tenant '${tenant}'...`);
logInfo(`Template: ${template} (${TEMPLATES[template]})`);
if (test) {
logInfo("Creating in tests/ directory");
}
maybeBootstrapAppsRepo(process.cwd(), tenant, test);
const appDir = resolveAppDir(process.cwd(), tenant, name, test);
if (fs23.existsSync(appDir)) {
logError(`App directory already exists: ${appDir}`);
process.exit(1);
}
const templateDir = path25.join(packageRoot, "templates", template);
if (!fs23.existsSync(templateDir)) {
logError(`Template not found: ${templateDir}`);
process.exit(1);
}
fs23.mkdirSync(appDir, { recursive: true });
try {
copyTemplate(templateDir, appDir, { name, tenant });
logSuccess(`Created app at ${appDir}`);
} catch (error) {
logError(`Failed to create app: ${error}`);
fs23.rmSync(appDir, { recursive: true, force: true });
process.exit(1);
}
autoSyncSkills(appDir, name);
await checkScaffoldContract(appDir);
printLegacyNextSteps(appDir, template);
}
function autoSyncSkills(appDir, appName) {
const root = resolveTargetRoot(appDir);
try {
syncSkills(root);
} catch (err) {
logWarn(`Agent-skill sync skipped: ${err instanceof Error ? err.message : err} \u2014 run: mesh skills sync`);
}
try {
const seeded = seedAppSkill(root, appName, path25.relative(root, appDir));
if (seeded) logSuccess(`seeded: ${seeded} (yours to grow \u2014 not managed by mesh skills sync)`);
} catch (err) {
logWarn(`App skill stub skipped: ${err instanceof Error ? err.message : err}`);
}
}
async function checkScaffoldContract(appDir) {
const root = resolveTargetRoot(appDir);
const report = await runAppCheck(root, [path25.relative(root, appDir) || "."]);
if (report.status === "ok") {
logSuccess("app contract: met (mesh app check)");
return;
}
console.log(renderReport(report, { verbose: false }));
if (report.status === "warn") {
logSuccess("app contract: met \u2014 the \u26A0 lines above are advisories on the repo, not on this app (mesh app check)");
return;
}
if (report.status === "error") {
logError("the scaffold does not meet the Mesh app contract \u2014 this is a template defect; file a platform item and cite the \u2717 lines above");
process.exitCode = 1;
}
}
async function runComposable(tenant, name, primitives, test) {
logInfo(`Creating app '${name}' for tenant '${tenant}'...`);
logInfo(`Primitives: ${primitives.join(", ")}`);
if (test) {
logInfo("Creating in tests/ directory");
}
maybeBootstrapAppsRepo(process.cwd(), tenant, test);
const appDir = resolveAppDir(process.cwd(), tenant, name, test);
if (fs23.existsSync(appDir)) {
logError(`App directory already exists: ${appDir}`);
process.exit(1);
}
const region = "us-east-2";
const platformName = "mesh";
const platformEnv = "dev";
const workspaceDeps = isInsidePlatformMonorepo(appDir);
const repoRoot2 = resolveTargetRoot(process.cwd());
const mesh = workspaceDeps ? { range: "", resolved: true } : resolvePublishedRange("@mesh-tech/app-kit", repoRoot2);
const esmBundle = workspaceDeps ? { range: "", resolved: true } : resolvePublishedRange("@mesh-tech/esm-bundle", repoRoot2);
if (!workspaceDeps) {
logInfo(
mesh.resolved ? `@mesh-tech/* pinned to ${mesh.range} (current published line)` : `Registry lookup unavailable \u2014 pinning @mesh-tech/* to ${mesh.range}; run \`pnpm up @mesh-tech/*\` once you have registry auth`
);
}
const context = {
name,
tenant,
service: primitives.includes("service"),
database: primitives.includes("database"),
temporal: primitives.includes("temporal"),
bucket: primitives.includes("bucket"),
region,
workspaceDeps,
meshRange: mesh.range,
esmBundleRange: esmBundle.range,
typesNodeRange: SCAFFOLD_TOOLCHAIN["@types/node"],
tsxRange: SCAFFOLD_TOOLCHAIN.tsx,
typescriptRange: SCAFFOLD_TOOLCHAIN.typescript,
deployerRoleArn: resolveDeployerRoleArn(tenant, platformName, platformEnv)
};
fs23.mkdirSync(appDir, { recursive: true });
try {
generateComposableApp(appDir, context);
logSuccess(`Created app at ${appDir}`);
} catch (error) {
logError(`Failed to create app: ${error}`);
fs23.rmSync(appDir, { recursive: true, force: true });
process.exit(1);
}
const nested = fs23.readdirSync(appDir, { withFileTypes: true }).some((entry) => entry.isDirectory() && fs23.existsSync(path25.join(appDir, entry.name, "package.json")));
const glob = workspaceGlobForApp(repoRoot2, appDir);
if (nested && glob) {
const added = ensureWorkspaceGlobs(repoRoot2, [glob]);
if (added.length > 0) {
logInfo(`pnpm-workspace.yaml: added ${added.join(", ")} so the app's api/worker packages install`);
}
}
autoSyncSkills(appDir, name);
await checkScaffoldContract(appDir);
printComposableNextSteps(appDir, context);
}
function printLegacyNextSteps(appDir, _template) {
console.log("");
logSuccess("App created successfully!");
console.log("");
console.log("Next steps:");
console.log(` cd ${appDir}`);
console.log(" pnpm install");
console.log(" pnpm mesh dev");
console.log("");
console.log("To deploy:");
console.log(" pnpm mesh deploy up");
console.log("");
}
function printComposableNextSteps(appDir, context) {
console.log("");
logSuccess("App created successfully!");
console.log("");
console.log("Generated:");
console.log(" index.ts Pulumi app definition");
console.log(" AGENTS.md AI agent instructions");
console.log(" CLAUDE.md Bridges AGENTS.md into Claude Code (@AGENTS.md)");
if (context.service) {
console.log(" api/ HTTP service (Hono)");
}
if (context.temporal) {
console.log(" worker/ Temporal worker");
}
if (context.database) {
console.log(" prisma/ Database schema");
}
if (context.bucket) {
console.log(" api/src/storage.ts S3 helpers");
}
console.log("");
for (const line of composableNextSteps(appDir, findMeshJson(process.cwd())?.data.platform ?? null)) {
console.log(line);
}
}
function composableNextSteps(appDir, platform) {
const common = [
"Next steps:",
` cd ${appDir}`,
" pnpm install",
" mesh skills sync # re-run once deps are installed: picks up package skills + Intent",
" pnpm install # again only if skills sync reports it added @tanstack/intent"
];
const local = [
" mesh start # the local Mesh platform (once; Docker \u2014 from anywhere)",
" mesh dev # run the app against it"
];
const deploy = [
" mesh stack init # personal dev stack (deploy: false)",
" mesh deploy up --yes # deploy via the stack's deployer role"
];
if (platform === null || platform === "local") {
return [...common, ...local, "", "When you deploy to a Mesh platform:", ...deploy];
}
return [...common, ...deploy, " mesh dev # run locally against the platform"];
}
var __filename, __dirname, packageRoot, TEMPLATES, REMOVED_TEMPLATES, PRIMITIVES, HUB_ACCOUNTS, PLATFORM_MONOREPO_NAME, VALID_PRIMITIVES, NO_REGISTRY_ACCESS_MESSAGE;
var init_create_app = __esm({
"libs/mesh-cli/src/commands/create-app.ts"() {
"use strict";
init_utils();
init_skills();
init_app_check();
init_stack();
init_scaffold_versions();
init_auth_preflight();
init_errors();
init_mesh_json();
__filename = fileURLToPath2(import.meta.url);
__dirname = path25.dirname(__filename);
packageRoot = findPackageRoot(__dirname);
TEMPLATES = {
workflow: "Hono API + Temporal worker + hello workflow (Pulumi/app-kit)",
"api-auth": "Hono API with platform auth: authn (Zitadel JWT) + authz (SpiceDB)",
"api-role-gating": "Hono API with role-gating authz: authn (Zitadel JWT) + coarse-role checks on Zitadel project roles, in-process (no SpiceDB)",
"external-service": "Hono API calling a third-party vendor via platform-managed credentials (ExternalService + mock for `mesh dev --externals`)"
};
REMOVED_TEMPLATES = {
"temporal-api-worker": "renamed to 'workflow'",
"api-web-db": "removed (SST-based) \u2014 use --primitives service,database"
};
PRIMITIVES = {
service: "HTTP Service (API with Hono)",
database: "Database (PostgreSQL via Prisma)",
temporal: "Temporal (workflow orchestration)",
bucket: "S3 Bucket (file storage)"
};
HUB_ACCOUNTS = {
mesh: "159923586610"
};
PLATFORM_MONOREPO_NAME = "mesh-platform";
VALID_PRIMITIVES = Object.keys(PRIMITIVES);
Handlebars.registerHelper("titleCase", (str) => {
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
});
NO_REGISTRY_ACCESS_MESSAGE = "No registry access on this machine \u2014 @mesh-tech packages cannot be installed.\n Run: mesh init\n (sets up your tenant and signs you in to the package registry)\n Scaffolding offline anyway? add --skip-registry-check";
}
});
// libs/mesh-cli/src/commands/db/connect.ts
import { spawn as spawn4 } from "child_process";
async function connectCommand(options) {
let tenant;
let platformEnv;
if (options.tenant && options.env) {
tenant = options.tenant;
platformEnv = options.env;
logInfo(`Explicit mode: Tenant=${tenant}, Env=${platformEnv}`);
} else if (options.tenant || options.env) {
logError("Both --tenant and --env are required");
logInfo("Usage: mesh db connect --tenant mesh --env dev-temporal");
process.exit(1);
} else {
const ctx = detectContext();
tenant = ctx.tenant;
platformEnv = ctx.platformEnv;
logInfo(`Auto-detected: Tenant=${tenant}, Env=${platformEnv}`);
}
const bastion = await getPlatformBastionInfo(tenant, platformEnv);
const rdsService = bastion.services.rds;
if (!rdsService) {
logError("RDS service not available in platform bastion");
process.exit(1);
}
console.log("");
logInfo(`Tunnel will forward localhost:5432 \u2192 ${rdsService.host}:${rdsService.port}`);
console.log("");
logInfo("In another terminal, connect with:");
console.log(" psql -h localhost -p 5432 -U <username> -d <database>");
console.log("");
logInfo("Or use: mesh db credentials --tenant ... --env ... --app ...");
console.log("");
logInfo("Press Ctrl+C to stop the tunnel");
console.log("");
const tunnel = spawn4(
"aws",
[
"ssm",
"start-session",
"--target",
bastion.instanceId,
"--document-name",
"AWS-StartPortForwardingSessionToRemoteHost",
"--parameters",
JSON.stringify({
host: [rdsService.host],
portNumber: [String(rdsService.port)],
localPortNumber: ["5432"]
})
],
{ stdio: "inherit" }
);
tunnel.on("error", (err) => {
console.error("Failed to start tunnel:", err.message);
process.exit(1);
});
tunnel.on("exit", (code) => {
process.exit(code ?? 0);
});
}
var init_connect = __esm({
"libs/mesh-cli/src/commands/db/connect.ts"() {
"use strict";
init_utils();
}
});
// libs/mesh-cli/src/commands/db/credentials.ts
async function credentialsCommand(options) {
let platformTenant;
let platformEnv;
if (options.tenant && options.env) {
platformTenant = options.tenant;
platformEnv = options.env;
logInfo(`Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
} else if (options.tenant || options.env) {
logError("Both --tenant and --env are required");
logInfo("Usage: mesh db credentials --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev");
process.exit(1);
} else {
const ctx = detectContext();
platformTenant = ctx.tenant;
platformEnv = ctx.platformEnv;
logInfo(`Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
}
const appTenant = options.appTenant ?? platformTenant;
const appStage = options.appStage ?? platformEnv;
const appName = options.app;
if (appName) {
logInfo(`App credentials: tenant=${appTenant}, stage=${appStage}, app=${appName}`);
}
const databaseUrl = await getDatabaseUrl(appTenant, appStage, { app: appName });
const parsed = new URL(databaseUrl);
console.log("");
console.log("Database Credentials:");
console.log(` Host: ${parsed.hostname}`);
console.log(` Port: ${parsed.port || 5432}`);
console.log(` Database: ${parsed.pathname.slice(1)}`);
console.log(` Username: ${parsed.username}`);
console.log(` Password: ${decodeURIComponent(parsed.password)}`);
console.log("");
console.log("DATABASE_URL:");
console.log(` ${databaseUrl}`);
console.log("");
console.log("For tunnel usage (localhost:5432):");
const tunnelUrl = new URL(databaseUrl);
tunnelUrl.hostname = "localhost";
tunnelUrl.port = "5432";
console.log(` ${tunnelUrl.toString()}`);
console.log("");
}
var init_credentials2 = __esm({
"libs/mesh-cli/src/commands/db/credentials.ts"() {
"use strict";
init_utils();
}
});
// libs/mesh-cli/src/commands/db/env.ts
async function envCommand(options) {
let platformTenant;
let platformEnv;
if (options.tenant && options.env) {
platformTenant = options.tenant;
platformEnv = options.env;
console.error(`\u2139 Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
} else if (options.tenant || options.env) {
logError("Both --tenant and --env are required");
logInfo("Usage: eval $(mesh db env --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev)");
process.exit(1);
} else {
const ctx = detectContext();
platformTenant = ctx.tenant;
platformEnv = ctx.platformEnv;
console.error(`\u2139 Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
}
const appTenant = options.appTenant ?? platformTenant;
const appStage = options.appStage ?? platformEnv;
const appName = options.app;
if (appName) {
console.error(`\u2139 App credentials: tenant=${appTenant}, stage=${appStage}, app=${appName}`);
}
const rawUrl = await getDatabaseUrl(appTenant, appStage, { app: appName });
const tunnelUrl = rewriteDatabaseUrl(rawUrl, { endpoint: "localhost:5432" });
const parsed = new URL(tunnelUrl);
console.log(`export DATABASE_URL="${tunnelUrl}"`);
console.log('export DB_HOST="localhost"');
console.log('export DB_PORT="5432"');
console.log(`export DB_NAME="${parsed.pathname.slice(1)}"`);
console.log(`export DB_USERNAME="${parsed.username}"`);
console.log(`export DB_PASSWORD="${decodeURIComponent(parsed.password)}"`);
}
var init_env = __esm({
"libs/mesh-cli/src/commands/db/env.ts"() {
"use strict";
init_utils();
}
});
// libs/mesh-cli/src/commands/db/psql.ts
import { spawn as spawn5 } from "child_process";
import * as net10 from "net";
async function startTunnelBackground(instanceId, rdsHost, rdsPort, localPort) {
logInfo("Starting tunnel in background...");
const tunnel = spawn5(
"aws",
[
"ssm",
"start-session",
"--target",
instanceId,
"--document-name",
"AWS-StartPortForwardingSessionToRemoteHost",
"--parameters",
JSON.stringify({
host: [rdsHost],
portNumber: [String(rdsPort)],
localPortNumber: [String(localPort)]
})
],
{ stdio: ["ignore", "ignore", "ignore"] }
);
const maxAttempts = 15;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
if (tunnel.exitCode !== null) {
throw new Error("Tunnel process died");
}
const isListening = await checkPort(localPort);
if (isListening) {
logSuccess(`Tunnel started (PID: ${tunnel.pid})`);
return tunnel;
}
await sleep2(1e3);
}
tunnel.kill();
throw new Error(`Tunnel failed to start after ${maxAttempts} seconds`);
}
function checkPort(port) {
return new Promise((resolve19) => {
const socket = new net10.Socket();
socket.setTimeout(500);
socket.on("connect", () => {
socket.destroy();
resolve19(true);
});
socket.on("timeout", () => {
socket.destroy();
resolve19(false);
});
socket.on("error", () => {
socket.destroy();
resolve19(false);
});
socket.connect(port, "localhost");
});
}
function sleep2(ms) {
return new Promise((resolve19) => setTimeout(resolve19, ms));
}
async function psqlCommand(options) {
let platformTenant;
let platformEnv;
if (options.tenant && options.env) {
platformTenant = options.tenant;
platformEnv = options.env;
logInfo(`Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
} else if (options.tenant || options.env) {
logError("Both --tenant and --env are required");
logInfo("Usage: mesh db psql --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev");
process.exit(1);
} else {
const ctx = detectContext();
platformTenant = ctx.tenant;
platformEnv = ctx.platformEnv;
logInfo(`Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
}
const appTenant = options.appTenant ?? platformTenant;
const appStage = options.appStage ?? platformEnv;
const appName = options.app;
if (appName) {
logInfo(`App credentials: tenant=${appTenant}, stage=${appStage}, app=${appName}`);
}
const psqlCheck = spawn5("which", ["psql"]);
await new Promise((resolve19, reject) => {
psqlCheck.on("exit", (code) => {
if (code !== 0) {
logError("psql not found. Install with: brew install postgresql");
reject(new Error("psql not found"));
} else {
resolve19();
}
});
});
const bastion = await getPlatformBastionInfo(platformTenant, platformEnv);
const rdsService = bastion.services.rds;
if (!rdsService) {
logError("RDS service not available in platform bastion");
process.exit(1);
}
const tunnel = await startTunnelBackground(
bastion.instanceId,
rdsService.host,
rdsService.port,
5432
);
const cleanup = () => {
logInfo("Stopping tunnel...");
tunnel.kill();
};
process.on("exit", cleanup);
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
const databaseUrl = await getDatabaseUrl(appTenant, appStage, { app: appName });
const parsed = new URL(databaseUrl);
const username = parsed.username;
const password2 = decodeURIComponent(parsed.password);
const database = parsed.pathname.slice(1);
console.log("");
logInfo(`Connecting to ${database} as ${username}...`);
console.log("");
const psql = spawn5(
"psql",
["-h", "localhost", "-p", "5432", "-U", username, "-d", database],
{
stdio: "inherit",
env: { ...process.env, PGPASSWORD: password2 }
}
);
psql.on("exit", (code) => {
cleanup();
process.exit(code ?? 0);
});
}
var init_psql = __esm({
"libs/mesh-cli/src/commands/db/psql.ts"() {
"use strict";
init_utils();
}
});
// libs/mesh-cli/src/commands/db/exec.ts
import { spawn as spawn6 } from "child_process";
import * as net11 from "net";
import * as fs24 from "fs";
import * as path26 from "path";
import {
SecretsManagerClient as SecretsManagerClient3,
GetSecretValueCommand as GetSecretValueCommand3
} from "@aws-sdk/client-secrets-manager";
function findSstOutputs() {
let dir = process.cwd();
const root = path26.parse(dir).root;
while (dir !== root) {
const outputsPath = path26.join(dir, ".sst", "outputs.json");
if (fs24.existsSync(outputsPath)) {
try {
const content = fs24.readFileSync(outputsPath, "utf-8");
const outputs = JSON.parse(content);
if (outputs.databaseUrl) {
const secretArnMatch = outputs.databaseUrl.match(/secretArn=([^&]+)/);
const secretArn = secretArnMatch ? decodeURIComponent(secretArnMatch[1]) : void 0;
return {
databaseUrl: outputs.databaseUrl,
databaseName: outputs.databaseName,
secretArn,
rootDir: dir
};
}
} catch {
}
}
dir = path26.dirname(dir);
}
return null;
}
function checkPort2(port) {
return new Promise((resolve19) => {
const socket = new net11.Socket();
socket.setTimeout(500);
socket.on("connect", () => {
socket.destroy();
resolve19(true);
});
socket.on("timeout", () => {
socket.destroy();
resolve19(false);
});
socket.on("error", () => {
socket.destroy();
resolve19(false);
});
socket.connect(port, "localhost");
});
}
function sleep3(ms) {
return new Promise((resolve19) => setTimeout(resolve19, ms));
}
async function startTunnelBackground2(instanceId, rdsEndpoint, rdsPort, localPort) {
logInfo("Starting database tunnel...");
const tunnel = spawn6(
"aws",
[
"ssm",
"start-session",
"--target",
instanceId,
"--document-name",
"AWS-StartPortForwardingSessionToRemoteHost",
"--parameters",
JSON.stringify({
host: [rdsEndpoint],
portNumber: [String(rdsPort)],
localPortNumber: [String(localPort)]
})
],
{ stdio: ["ignore", "ignore", "ignore"] }
);
const maxAttempts = 15;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
if (tunnel.exitCode !== null) {
throw new Error("Tunnel process died");
}
if (await checkPort2(localPort)) {
logSuccess(`Tunnel started (PID: ${tunnel.pid})`);
return tunnel;
}
await sleep3(1e3);
}
tunnel.kill();
throw new Error(`Tunnel failed to start after ${maxAttempts} seconds`);
}
async function fetchSecretWithRetry(client, secretId) {
let lastError;
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
const response = await client.send(
new GetSecretValueCommand3({ SecretId: secretId })
);
if (!response.SecretString) {
throw new Error(`Secret ${secretId} has no string value`);
}
return JSON.parse(response.SecretString);
} catch (error) {
lastError = error;
const isNotFound = error.name === "ResourceNotFoundException";
if (isNotFound && attempt < MAX_RETRIES) {
logWarn(`Secret not found (attempt ${attempt}/${MAX_RETRIES}), waiting...`);
await sleep3(RETRY_DELAY_MS);
continue;
}
throw error;
}
}
throw lastError;
}
async function execCommand(commandArgs, options) {
if (commandArgs.length === 0) {
logError("No command specified");
logInfo("Usage: mesh db exec [options] -- <command> [args...]");
logInfo("");
logInfo("Options:");
logInfo(" -t, --tenant <tenant> Platform tenant (e.g., mesh)");
logInfo(" -e, --env <env> Platform environment (e.g., dev)");
logInfo(" --secret <arn> Use secret ARN directly (skips SSM discovery)");
logInfo(" --db-host <host> Database host (skips SSM lookup)");
logInfo(" --ssl <mode> SSL mode: require, no-verify, disable");
logInfo(" --port <port> Local port for tunnel (default: 5432)");
logInfo(" --stage <stage> Stage for legacy discovery mode");
logInfo("");
logInfo("Examples:");
logInfo(" # Explicit mode (recommended)");
logInfo(" mesh db exec --tenant mesh --env dev --ssl=require -- npx prisma db push");
logInfo("");
logInfo(" # Direct mode (with existing tunnel)");
logInfo(" mesh db exec --secret=arn:aws:... --ssl=require -- pnpm dev");
process.exit(1);
}
const localPort = options.port ? parseInt(options.port, 10) : DEFAULT_LOCAL_PORT;
const secretsManager = new SecretsManagerClient3({});
let tunnel = null;
let databaseUrl;
if (options.secret) {
logInfo(`Direct mode: using secret ${options.secret}`);
const tunnelExists = await checkPort2(localPort);
if (!tunnelExists) {
logError(`No tunnel found on port ${localPort}`);
logInfo("In direct mode, start a tunnel first with: mesh db connect");
logInfo("Or use discovery mode (without --secret) to auto-start tunnel");
process.exit(1);
}
logSuccess(`Using existing tunnel on port ${localPort}`);
logInfo(`Fetching credentials from secret...`);
const secret = await fetchSecretWithRetry(secretsManager, options.secret);
if (secret.DATABASE_URL) {
databaseUrl = secret.DATABASE_URL;
logSuccess("Got DATABASE_URL from secret");
} else if (secret.username && secret.password) {
const dbName = secret.dbname ?? secret.database ?? "postgres";
databaseUrl = `postgresql://${secret.username}:${encodeURIComponent(secret.password)}@localhost:${localPort}/${dbName}`;
logSuccess(`Got credentials for user: ${secret.username}`);
} else {
logError("Secret must contain DATABASE_URL or username/password fields");
process.exit(1);
}
try {
const parsed = new URL(databaseUrl);
parsed.hostname = "localhost";
parsed.port = String(localPort);
if (options.ssl) {
parsed.searchParams.set("sslmode", options.ssl);
if (options.ssl === "require" || options.ssl === "no-verify") {
parsed.searchParams.set("sslaccept", "accept_invalid_certs");
}
}
databaseUrl = parsed.toString();
} catch {
logWarn("Could not parse DATABASE_URL for rewriting");
}
} else {
let platformTenant;
let platformEnv;
if (options.tenant && options.env) {
platformTenant = options.tenant;
platformEnv = options.env;
logInfo(`Explicit mode: Platform Tenant=${platformTenant}, Platform Env=${platformEnv}`);
} else if (options.tenant || options.env) {
logError("Both --tenant and --env are required when using explicit mode");
logInfo("Usage: mesh db exec --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev -- <command>");
logInfo("Or omit both to use auto-detection");
process.exit(1);
} else {
const ctx = detectContext();
platformTenant = ctx.tenant;
platformEnv = ctx.platformEnv;
logInfo(`Discovery mode: Platform=${platformEnv}, Tenant=${platformTenant}`);
}
const appTenant = options.appTenant ?? platformTenant;
const appStage = options.appStage ?? platformEnv;
const appName = options.app;
if (appName) {
logInfo(`App credentials: tenant=${appTenant}, stage=${appStage}, app=${appName}`);
}
const tunnelExists = await checkPort2(localPort);
const sstOutputs = findSstOutputs();
if (sstOutputs?.secretArn && tunnelExists) {
logInfo(`Found SST outputs in: ${sstOutputs.rootDir}`);
try {
const secret = await fetchSecretWithRetry(secretsManager, sstOutputs.secretArn);
if (secret.DATABASE_URL) {
databaseUrl = rewriteDatabaseUrl(secret.DATABASE_URL, {
endpoint: `localhost:${localPort}`,
sslMode: options.ssl
});
logSuccess(`Using DATABASE_URL from SST outputs`);
}
} catch {
logWarn("Could not fetch from SST outputs, falling back to credential lookup");
}
}
if (!databaseUrl) {
if (!tunnelExists) {
const bastion = await getPlatformBastionInfo(platformTenant, platformEnv);
const rdsService = bastion.services.rds;
if (!rdsService) {
throw new Error("RDS service not available in platform bastion");
}
tunnel = await startTunnelBackground2(
bastion.instanceId,
rdsService.host,
rdsService.port,
localPort
);
} else {
logSuccess(`Using existing tunnel on port ${localPort}`);
}
const rawUrl = await getDatabaseUrl(appTenant, appStage, { app: appName });
databaseUrl = rewriteDatabaseUrl(rawUrl, {
endpoint: `localhost:${localPort}`,
sslMode: options.ssl
});
}
}
const env = { ...process.env, DATABASE_URL: databaseUrl };
if (options.ssl === "require" || options.ssl === "no-verify") {
env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
logInfo("Set NODE_TLS_REJECT_UNAUTHORIZED=0 for SSL through tunnel");
}
const cleanup = () => {
if (tunnel) {
logInfo("Stopping tunnel...");
tunnel.kill();
}
};
process.on("exit", cleanup);
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
const [cmd, ...args] = commandArgs;
logInfo(`Running: ${commandArgs.join(" ")}`);
console.log("");
const child = spawn6(cmd, args, { stdio: "inherit", env });
child.on("exit", (code) => {
cleanup();
process.exit(code ?? 0);
});
child.on("error", (err) => {
logError(`Failed to run command: ${err.message}`);
cleanup();
process.exit(1);
});
}
var DEFAULT_LOCAL_PORT, MAX_RETRIES, RETRY_DELAY_MS;
var init_exec = __esm({
"libs/mesh-cli/src/commands/db/exec.ts"() {
"use strict";
init_utils();
DEFAULT_LOCAL_PORT = 5432;
MAX_RETRIES = 12;
RETRY_DELAY_MS = 5e3;
}
});
// libs/mesh-cli/src/commands/db/index.ts
function registerDbCommands(program2) {
const db = program2.command("db").description("Database utilities");
db.command("connect").description("Start DB tunnel for manual psql access").option("-t, --tenant <tenant>", "Platform tenant (e.g., mesh)").option("-e, --env <env>", "Platform environment (e.g., dev-temporal)").action(connectCommand);
db.command("credentials").alias("creds").description("Print database credentials").option("-t, --tenant <tenant>", "Platform tenant (e.g., mesh)").option("-e, --env <env>", "Platform environment (e.g., dev-temporal)").option("-a, --app <app>", "App name for credentials path (e.g., rdc)").option("--app-tenant <tenant>", "Tenant for app credentials (e.g., encore)").option("--app-stage <stage>", "Stage for app credentials (e.g., dev)").action(credentialsCommand);
db.command("env").description("Export DATABASE_URL for current shell (use with eval)").option("-t, --tenant <tenant>", "Platform tenant (e.g., mesh)").option("-e, --env <env>", "Platform environment (e.g., dev-temporal)").option("-a, --app <app>", "App name for credentials path (e.g., rdc)").option("--app-tenant <tenant>", "Tenant for app credentials (e.g., encore)").option("--app-stage <stage>", "Stage for app credentials (e.g., dev)").action(envCommand);
db.command("psql").description("Connect to database with psql").option("-t, --tenant <tenant>", "Platform tenant (e.g., mesh)").option("-e, --env <env>", "Platform environment (e.g., dev-temporal)").option("-a, --app <app>", "App name for credentials path (e.g., rdc)").option("--app-tenant <tenant>", "Tenant for app credentials (e.g., encore)").option("--app-stage <stage>", "Stage for app credentials (e.g., dev)").action(psqlCommand);
db.command("exec").description("Run any command with database connection").option("-t, --tenant <tenant>", "Platform tenant for bastion lookup (e.g., mesh)").option("-e, --env <env>", "Platform environment for bastion lookup (e.g., dev-temporal)").option("-a, --app <app>", "App name for credentials path (e.g., rdc)").option("--app-tenant <tenant>", "Tenant for app credentials if different from platform (e.g., encore)").option("--app-stage <stage>", "Stage for app credentials (e.g., dev)").option("--secret <arn>", "Use secret ARN directly (skips SSM discovery)").option("--ssl <mode>", "SSL mode: require, no-verify, disable").option("--port <port>", "Local tunnel port (default: 5432)").argument("<command...>", "Command and arguments to run").action(execCommand);
}
var init_db = __esm({
"libs/mesh-cli/src/commands/db/index.ts"() {
"use strict";
init_connect();
init_credentials2();
init_env();
init_psql();
init_exec();
}
});
// libs/mesh-cli/src/utils/deploy-preflight.ts
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
import { dirname as dirname22, join as join25, relative as relative6 } from "node:path";
import { parse as parseYaml3 } from "yaml";
function isGated(name) {
return GATED_PREFIXES.some((p) => name.startsWith(p));
}
function normalizeLockVersion(raw) {
if (typeof raw !== "string" || raw.length === 0) return null;
const bare = raw.split("(")[0].trim();
return /^\d/.test(bare) ? bare : null;
}
function extractImporterMeshVersions(lockDoc, importerRel) {
const out = {};
const importers = lockDoc?.importers;
const importer = importers?.[importerRel];
if (!importer) return out;
for (const group of ["dependencies", "devDependencies", "optionalDependencies"]) {
const deps = importer[group];
if (!deps) continue;
for (const [name, entry] of Object.entries(deps)) {
if (!isGated(name)) continue;
const raw = typeof entry === "string" ? entry : entry?.version;
const version = normalizeLockVersion(raw);
if (version) out[name] = version;
}
}
return out;
}
function diffLockVsInstalled(expected, installed) {
const out = [];
for (const [name, exp] of Object.entries(expected)) {
const got = installed[name] ?? null;
if (got !== exp) out.push({ name, expected: exp, installed: got });
}
return out;
}
function findPnpmLock(startDir) {
let dir = startDir;
for (; ; ) {
const candidate = join25(dir, "pnpm-lock.yaml");
if (existsSync20(candidate)) return candidate;
const parent = dirname22(dir);
if (parent === dir) return null;
dir = parent;
}
}
function readInstalledVersion(fromDir, name) {
try {
const pkgPath = join25(fromDir, "node_modules", name, "package.json");
const version = JSON.parse(readFileSync21(pkgPath, "utf8")).version;
return typeof version === "string" ? version : null;
} catch {
return null;
}
}
function checkDeployDepsFresh(appRoot) {
const lockPath = findPnpmLock(appRoot);
if (!lockPath) return null;
let lockDoc;
try {
lockDoc = parseYaml3(readFileSync21(lockPath, "utf8"));
} catch {
logWarn(
`deploy preflight: could not parse ${lockPath} \u2014 skipping the stale-node_modules check.`
);
return null;
}
const importerRel = relative6(dirname22(lockPath), appRoot) || ".";
const expected = extractImporterMeshVersions(lockDoc, importerRel);
const names = Object.keys(expected);
if (names.length === 0) return [];
const installed = {};
for (const name of names) installed[name] = readInstalledVersion(appRoot, name);
return diffLockVsInstalled(expected, installed);
}
function formatStaleDepsError(mismatches) {
const lines = [
`\u2717 ${mismatches.length} @mesh-tech/* dep${mismatches.length === 1 ? "" : "s"} in node_modules do not match the lockfile \u2014 run pnpm install before deploying:`,
""
];
for (const m of mismatches) {
lines.push(
` \u2022 ${m.name} \u2014 installed ${m.installed ?? "(missing)"}, lockfile wants ${m.expected}`
);
}
lines.push(
"",
" A stale node_modules makes pulumi evaluate OLD component code and can plan to",
" DELETE resources the current code would keep. Fix:",
"",
" pnpm install",
"",
` To bypass this check (rarely correct): ${SKIP_ENV}=1 mesh deploy \u2026`
);
return lines.join("\n");
}
function assertDeployDepsFresh(appRoot, op) {
if (!PROGRAM_EVAL_OPS.has(op)) return;
if (process.env[SKIP_ENV]) return;
const mismatches = checkDeployDepsFresh(appRoot);
if (!mismatches || mismatches.length === 0) return;
process.stderr.write(formatStaleDepsError(mismatches) + "\n");
process.exit(1);
}
var GATED_PREFIXES, PROGRAM_EVAL_OPS, SKIP_ENV;
var init_deploy_preflight = __esm({
"libs/mesh-cli/src/utils/deploy-preflight.ts"() {
"use strict";
init_log();
GATED_PREFIXES = ["@mesh-tech/"];
PROGRAM_EVAL_OPS = /* @__PURE__ */ new Set(["up", "preview", "destroy", "refresh", "import"]);
SKIP_ENV = "MESH_SKIP_DEPLOY_PREFLIGHT";
}
});
// libs/mesh-cli/src/commands/deploy.ts
import { execFileSync as execFileSync18 } from "child_process";
function buildPulumiArgs(pulumiArgs, stack) {
const base = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? ["up", ...pulumiArgs] : [...pulumiArgs];
const op = base[0];
const takesStackFlag = !!op && !OPS_WITHOUT_STACK_FLAG.has(op) && !(op === "stack" && STACK_SUBCOMMANDS_WITHOUT_FLAG.has(base[1] ?? ""));
return takesStackFlag ? [...base, "-s", stack] : base;
}
function parseStackFromArgs(args) {
for (let i = 0; i < args.length; i++) {
if (args[i] === "--stack" || args[i] === "-s") {
return args[i + 1] ?? null;
}
if (args[i]?.startsWith("--stack=")) {
return args[i].split("=")[1] ?? null;
}
}
return null;
}
function stripStackFromArgs(args) {
for (let i = args.length - 1; i >= 0; i--) {
if (args[i] === "--stack" || args[i] === "-s") {
args.splice(i, 2);
} else if (args[i]?.startsWith("--stack=")) {
args.splice(i, 1);
}
}
}
function registerDeployCommand(program2) {
program2.command("deploy").description("Run pulumi with the app's deployer role (passthrough)").allowUnknownOption(true).allowExcessArguments(true).helpOption(false).action(async (_opts, cmd) => {
const pulumiArgs = cmd.args;
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
logError("No Pulumi.yaml found. Run from within a Pulumi app directory.");
process.exit(1);
}
const stack = parseStackFromArgs(pulumiArgs) ?? getCurrentStack(appRoot);
stripStackFromArgs(pulumiArgs);
if (!stack) {
logError(
"Could not determine stack. Either:\n - Pass --stack <name> (e.g., mesh deploy up --stack dev-<you> --yes)\n - Create a personal dev stack first: mesh stack init (it prints the exact --stack command)"
);
process.exit(1);
}
const op = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? "up" : pulumiArgs[0];
assertDeployDepsFresh(appRoot, op ?? "up");
if (op === "up" || op === "destroy") {
const wt = resolveWorktreeIdentity(appRoot);
const isPersonal = readStackConfig(appRoot, stack, "mesh:deploy") === "false";
if (isPersonal && stackNeedsWorktreeIsolation(stack, wt)) {
logWarn(
`Stack "${stack}" carries no worktree token, but you're in linked worktree "${wt.slug}".
Deploying it from multiple worktrees at once collides on SSM export paths
(ParameterAlreadyExists) and the Temporal namespace. To isolate this worktree:
mesh stack init --worktree
Continuing with "${stack}" \u2014 pass --stack to target a different one.`
);
}
}
const credEnv = await resolvePulumiEnv({ appRoot, stack });
const env = { ...process.env, ...credEnv };
delete env.AWS_PROFILE;
const finalArgs = buildPulumiArgs(pulumiArgs, stack);
try {
execFileSync18("pulumi", finalArgs, {
cwd: appRoot,
env,
stdio: "inherit"
});
} catch (err) {
process.exit(err.status ?? 1);
}
});
}
var OPS_WITHOUT_STACK_FLAG, STACK_SUBCOMMANDS_WITHOUT_FLAG;
var init_deploy = __esm({
"libs/mesh-cli/src/commands/deploy.ts"() {
"use strict";
init_log();
init_pulumi();
init_pulumi_run();
init_deploy_preflight();
init_worktree_identity();
OPS_WITHOUT_STACK_FLAG = /* @__PURE__ */ new Set([
"whoami",
"version",
"plugin",
"login",
"logout",
"about",
"org",
"env",
"help"
]);
STACK_SUBCOMMANDS_WITHOUT_FLAG = /* @__PURE__ */ new Set([
"rm",
"select",
"init",
"rename",
"unselect",
"ls"
]);
}
});
// libs/mesh-cli/src/docs/schema.ts
import { z as z3 } from "zod";
function isReservedSegment(segment) {
const lowered = segment.toLowerCase();
return RESERVED_SEGMENTS.some((reserved) => reserved === lowered);
}
function hasReservedSegment(relPath) {
return relPath.split("/").some(isReservedSegment);
}
function slugify2(value) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "index";
}
var RESERVED_SEGMENTS, docsJsonSchema;
var init_schema2 = __esm({
"libs/mesh-cli/src/docs/schema.ts"() {
"use strict";
RESERVED_SEGMENTS = [
"plans",
"designs",
"proposals",
"incidents",
"handoffs",
"reviews",
"runbooks",
"hran",
"superpowers",
"templates",
"archive",
"internal",
"drafts",
"node_modules",
"__tests__",
".claude",
".pi"
];
docsJsonSchema = z3.object({
/** Section label in the nav, e.g. "Building apps". */
title: z3.string().min(1),
/**
* Section position in the nav. Ties break alphabetically by title, so two
* sections can never fight over a slot nondeterministically.
*/
order: z3.number().int().default(50),
/**
* Route prefix for the section. Defaults to the slugified title. The
* empty string is legal and means "this section lives at the site root" —
* exactly one root should do that (the front door, `docs/portal`).
*/
slug: z3.string().regex(
/^$|^[a-z0-9]+(?:-[a-z0-9]+)*$/,
'slug must be lower-kebab-case or ""'
).optional(),
/**
* Escape hatch: explicit repo-relative paths to publish that live outside
* any natural doc root (a package README, a repo-root policy file). Expected
* to hold a handful of entries across the whole repo — adding one is a
* deliberate, reviewable act. Paths under a reserved segment are refused.
*/
include: z3.array(z3.string().min(1)).default([])
}).strict();
}
});
// libs/mesh-cli/src/docs/discover.ts
import { existsSync as existsSync21, readdirSync as readdirSync9, readFileSync as readFileSync22, statSync as statSync5 } from "node:fs";
import path27 from "node:path";
function routePathFor(relPath) {
const withoutExt = relPath.replace(/\.md$/i, "");
const parts = withoutExt.split("/").map(slugify2);
if (parts[parts.length - 1] === "index") parts.pop();
return parts.join("/");
}
function walkMarkdown(absDir, relDir, errors, rootDir) {
const out = [];
let entries;
try {
entries = readdirSync9(absDir, { withFileTypes: true });
} catch {
return out;
}
for (const entry of entries) {
const rel2 = relDir ? `${relDir}/${entry.name}` : entry.name;
if (hasReservedSegment(entry.name)) {
continue;
}
const abs = path27.join(absDir, entry.name);
if (entry.isDirectory()) {
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
out.push(...walkMarkdown(abs, rel2, errors, rootDir));
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
if (entry.name.toLowerCase() === "readme.md") continue;
out.push(rel2);
} else if (entry.isFile() && entry.name === "docs.json" && relDir !== "") {
errors.push(
`nested doc root: ${rootDir}/.../${rel2} \u2014 a docs.json inside another doc root is ambiguous; give it its own sibling root or remove it`
);
}
}
return out;
}
function discoverDocRoots(repoRoot2) {
const errors = [];
const roots = [];
const findRoots = (absDir, relDir, insideReserved) => {
let entries;
try {
entries = readdirSync9(absDir, { withFileTypes: true });
} catch {
return;
}
const hasDocsJson = entries.some(
(e) => e.isFile() && e.name === "docs.json"
);
if (hasDocsJson) {
const rel2 = relDir || ".";
if (insideReserved || relDir && hasReservedSegment(relDir)) {
errors.push(
`doc root ${rel2}/ is under a reserved segment \u2014 it publishes nothing. Move the content out of the reserved directory instead.`
);
} else {
const root = loadRoot(absDir, rel2, errors);
if (root) roots.push(root);
}
return;
}
for (const entry of entries) {
if (!entry.isDirectory()) continue;
if (ROOT_WALK_SKIP.has(entry.name)) continue;
if (entry.name.startsWith(".")) continue;
const rel2 = relDir ? `${relDir}/${entry.name}` : entry.name;
findRoots(
path27.join(absDir, entry.name),
rel2,
insideReserved || hasReservedSegment(entry.name)
);
}
};
findRoots(repoRoot2, "", false);
const pages = [];
for (const root of roots) {
const absRoot = path27.join(repoRoot2, root.dir);
const relFiles = walkMarkdown(absRoot, "", errors, root.dir);
for (const relPath of relFiles.sort()) {
pages.push({
source: root.dir === "." ? relPath : `${root.dir}/${relPath}`,
root,
routePath: routePathFor(relPath),
included: false
});
}
for (const includePath of root.config.include) {
const normalized = includePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
if (hasReservedSegment(normalized)) {
errors.push(
`${root.dir}/docs.json includes ${normalized}, which is under a reserved segment \u2014 include[] cannot pull internal material into the site`
);
continue;
}
if (!normalized.toLowerCase().endsWith(".md")) {
errors.push(
`${root.dir}/docs.json includes ${normalized}, which is not a markdown file`
);
continue;
}
if (!existsSync21(path27.join(repoRoot2, normalized))) {
errors.push(
`${root.dir}/docs.json includes ${normalized}, which does not exist`
);
continue;
}
if (pages.some((p) => p.source === normalized)) continue;
const stem = path27.posix.basename(normalized).replace(/\.md$/i, "");
pages.push({
source: normalized,
root,
// An included README is the section's index page — "a package README
// is its natural overview page". Anything else routes by its stem.
routePath: stem.toLowerCase() === "readme" ? "" : slugify2(stem),
included: true
});
}
}
const seenRoutes = /* @__PURE__ */ new Map();
for (const page of pages) {
const route = [page.root.slug, page.routePath].filter(Boolean).join("/");
const existing = seenRoutes.get(route);
if (existing) {
errors.push(
`route collision: ${page.source} and ${existing} both map to /${route || "(site root)"}`
);
}
seenRoutes.set(route, page.source);
}
roots.sort(
(a, b) => a.config.order - b.config.order || a.config.title.localeCompare(b.config.title)
);
return { roots, pages, errors };
}
function loadRoot(absDir, relDir, errors) {
const configPath = path27.join(absDir, "docs.json");
let raw;
try {
raw = JSON.parse(readFileSync22(configPath, "utf-8"));
} catch (error) {
errors.push(
`${relDir}/docs.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
);
return null;
}
const parsed = docsJsonSchema.safeParse(raw);
if (!parsed.success) {
for (const issue of parsed.error.issues) {
errors.push(
`${relDir}/docs.json: ${issue.path.join(".") || "(root)"}: ${issue.message}`
);
}
return null;
}
const config = parsed.data;
const slug = config.slug ?? slugify2(config.title);
return { dir: relDir, config, slug };
}
function pathKind(candidate) {
try {
const stat2 = statSync5(candidate);
return stat2.isDirectory() ? "dir" : "file";
} catch {
return null;
}
}
var ROOT_WALK_SKIP;
var init_discover = __esm({
"libs/mesh-cli/src/docs/discover.ts"() {
"use strict";
init_schema2();
ROOT_WALK_SKIP = /* @__PURE__ */ new Set([
".git",
"node_modules",
"dist",
".turbo",
".next",
".deploy",
".generated",
"coverage",
".portal",
".vercel",
".wrangler"
]);
}
});
// libs/mesh-cli/src/docs/assemble.ts
import { execFileSync as execFileSync19 } from "node:child_process";
import { mkdirSync as mkdirSync11, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync15 } from "node:fs";
import path28 from "node:path";
import { parse as parseYaml4 } from "yaml";
function splitFrontMatter(markdown) {
const normalized = markdown.replace(/^\uFEFF/, "");
if (!normalized.startsWith("---\n") && !normalized.startsWith("---\r\n")) {
return { data: {}, body: markdown, hasFrontMatter: false };
}
const end = normalized.indexOf("\n---", 4);
if (end === -1) return { data: {}, body: markdown, hasFrontMatter: false };
const rawBlock = normalized.slice(4, end);
const bodyStart = normalized.indexOf("\n", end + 1);
const body = (bodyStart === -1 ? "" : normalized.slice(bodyStart + 1)).replace(/^\r?\n/, "");
try {
const data = parseYaml4(rawBlock);
if (data && typeof data === "object" && !Array.isArray(data)) {
return {
data,
body,
hasFrontMatter: true
};
}
} catch {
}
return { data: {}, body, hasFrontMatter: true };
}
function joinFrontMatter(data, body) {
const lines = Object.entries(data).map(([key, value]) => {
if (typeof value === "number" || typeof value === "boolean")
return `${key}: ${value}`;
const text = String(value);
return /^[\w .,'’()/-]+$/.test(text) ? `${key}: ${text}` : `${key}: ${JSON.stringify(text)}`;
});
return `---
${lines.join("\n")}
---
${body.replace(/^\s*\n/, "")}`;
}
function firstHeading(body) {
let inFence = false;
for (const line of body.split("\n")) {
if (/^\s*(```|~~~)/.test(line)) {
inFence = !inFence;
continue;
}
if (inFence) continue;
const match = /^#\s+(.+?)\s*#*\s*$/.exec(line);
if (match) return match[1].trim();
}
return void 0;
}
function titleizeStem(stem) {
const words = stem.replace(/[-_]+/g, " ").trim();
return words.charAt(0).toUpperCase() + words.slice(1);
}
function deriveTitle(frontMatter, body, source) {
const fmTitle = frontMatter.title;
if (typeof fmTitle === "string" && fmTitle.trim()) return fmTitle.trim();
const heading = firstHeading(body);
if (heading) return heading;
const stem = path28.posix.basename(source).replace(/\.md$/i, "");
return titleizeStem(stem);
}
function scanLinks(markdown) {
const hits = [];
const lines = markdown.split("\n");
let offset = 0;
let inFence = null;
for (const line of lines) {
const fenceMatch = /^(\s{0,3})(`{3,}|~{3,})/.exec(line);
if (fenceMatch) {
if (!inFence) {
inFence = { marker: fenceMatch[2][0] };
} else if (fenceMatch[2].startsWith(inFence.marker)) {
inFence = null;
}
offset += line.length + 1;
continue;
}
if (inFence) {
offset += line.length + 1;
continue;
}
const refDef = /^ {0,3}\[[^\]]+\]:\s*(\S+)/.exec(line);
if (refDef) {
const href = refDef[1];
const start = offset + line.indexOf(href);
hits.push({ start, end: start + href.length, href });
offset += line.length + 1;
continue;
}
let i = 0;
while (i < line.length) {
const ch = line[i];
if (ch === "`") {
const run2 = /^`+/.exec(line.slice(i))[0];
const close = line.indexOf(run2, i + run2.length);
i = close === -1 ? line.length : close + run2.length;
continue;
}
if (ch === "<") {
const close = line.indexOf(">", i);
i = close === -1 ? line.length : close + 1;
continue;
}
if (ch === "!" && line[i + 1] === "[") {
i = scanBracketLink(line, i + 1, offset, hits);
continue;
}
if (ch === "[") {
i = scanBracketLink(line, i, offset, hits);
continue;
}
i++;
}
offset += line.length + 1;
}
return hits;
}
function scanBracketLink(line, open, lineOffset, hits) {
const closeBracket = line.indexOf("]", open);
if (closeBracket === -1) return line.length;
if (line[closeBracket + 1] !== "(") return closeBracket + 1;
let j = closeBracket + 2;
while (j < line.length && /\s/.test(line[j])) j++;
if (line[j] === "<") {
const end = line.indexOf(">", j);
if (end === -1) return line.length;
hits.push({
start: lineOffset + j + 1,
end: lineOffset + end,
href: line.slice(j + 1, end)
});
return end + 1;
}
const closeParen = line.indexOf(")", j);
if (closeParen === -1) return line.length;
const inner = line.slice(j, closeParen).trim();
const space = inner.search(/\s/);
const href = space === -1 ? inner : inner.slice(0, space);
if (href) {
const start = lineOffset + line.indexOf(href, j);
hits.push({ start, end: start + href.length, href });
}
return closeParen + 1;
}
function stripHtmlComments(body) {
const lines = body.split("\n");
const out = [];
let inFence = false;
let inComment = false;
for (const line of lines) {
if (/^\s*(```|~~~)/.test(line)) {
if (!inComment) inFence = !inFence;
if (!inComment) out.push(line);
continue;
}
if (inFence) {
out.push(line);
continue;
}
let rest = line;
let rebuilt = "";
while (rest.length > 0) {
if (inComment) {
const end = rest.indexOf("-->");
if (end === -1) {
rest = "";
break;
}
inComment = false;
rest = rest.slice(end + 3);
continue;
}
const start = rest.indexOf("<!--");
if (start === -1) {
rebuilt += rest;
break;
}
rebuilt += rest.slice(0, start);
inComment = true;
rest = rest.slice(start + 4);
}
if (rebuilt.trim() !== "" || !inComment)
out.push(rebuilt.replace(/\s+$/, ""));
}
return out.join("\n").replace(/\n{3,}/g, "\n\n");
}
function isAbsoluteHref(href) {
return /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(href);
}
function normalizeRepoPath(value) {
return path28.posix.normalize(value.replace(/\\/g, "/")).replace(/^\.\//, "").replace(/\/+$/, "");
}
function splitHash(href) {
const index = href.indexOf("#");
return index === -1 ? [href, ""] : [href.slice(0, index), href.slice(index + 1)];
}
function resolveLink(args) {
const { href, pageRoute: pageRoute2, sourceDir, sourceIndex, routes, repoUrl } = args;
if (href.startsWith("#") || isAbsoluteHref(href)) return { href };
if (args.ignoreLinkPrefixes.some((prefix2) => href.startsWith(prefix2)))
return { href };
const [rawTarget, rawHash] = splitHash(href);
const hash = rawHash ? `#${rawHash}` : "";
if (rawTarget === "") return { href };
if (rawTarget.startsWith("/")) {
const route = rawTarget.replace(/\/$/, "") || "/";
return routes.has(route) ? { href: `${route}${hash}` } : { href, issue: { kind: "broken", page: pageRoute2, href } };
}
const repoPath = normalizeRepoPath(path28.posix.join(sourceDir, rawTarget));
const page = sourceIndex.get(repoPath);
if (page) return { href: `${page.route}${hash}` };
for (const candidate of [`${repoPath}/index.md`]) {
const indexPage = sourceIndex.get(normalizeRepoPath(candidate));
if (indexPage) return { href: `${indexPage.route}${hash}` };
}
const kind = args.pathKind(repoPath);
if (kind !== null) {
const base = repoUrl.replace(/\/$/, "").replace(/\/(blob|tree)$/, "");
return {
href: `${base}/${kind === "dir" ? "tree" : "blob"}/main/${repoPath}${hash}`,
issue: { kind: "external", page: pageRoute2, href }
};
}
return { href, issue: { kind: "broken", page: pageRoute2, href } };
}
function rewriteLinks(args) {
const issues = [];
const hits = scanLinks(args.body);
if (hits.length === 0) return { body: args.body, issues };
const sourceDir = path28.posix.dirname(args.page.source);
let out = "";
let cursor = 0;
for (const hit of hits) {
const resolution = resolveLink({
href: hit.href,
pageRoute: args.page.route,
sourceDir,
sourceIndex: args.sourceIndex,
routes: args.routes,
ignoreLinkPrefixes: args.ignoreLinkPrefixes,
pathKind: args.pathKind,
repoUrl: args.repoUrl
});
out += args.body.slice(cursor, hit.start) + resolution.href;
cursor = hit.end;
if (resolution.issue) issues.push(resolution.issue);
}
out += args.body.slice(cursor);
return { body: out, issues };
}
function pageRoute(page) {
const joined = [page.root.slug, page.routePath].filter(Boolean).join("/");
return `/${joined}`.replace(/\/$/, "") || "/";
}
function pageFilePath(route) {
return route === "/" ? "index.md" : `${route.slice(1)}.md`;
}
function assemblePortal(options) {
const { repoRoot: repoRoot2, discovery, outDir } = options;
const repoUrl = options.repoUrl ?? DEFAULT_REPO_URL;
const ignoreLinkPrefixes = options.ignoreLinkPrefixes ?? DEFAULT_IGNORE_LINK_PREFIXES;
const pages = discovery.pages.map((page) => {
const route = pageRoute(page);
return {
source: page.source,
route,
filePath: pageFilePath(route),
title: "",
sectionTitle: page.root.config.title,
sectionOrder: page.root.config.order,
sectionSlug: page.root.slug
};
});
const sourceIndex = new Map(
pages.map((p) => [normalizeRepoPath(p.source), p])
);
const routes = new Set(pages.map((p) => p.route));
const brokenLinks = [];
const externalLinks = [];
rmSync5(outDir, { recursive: true, force: true });
mkdirSync11(outDir, { recursive: true });
for (const page of pages) {
const raw = readFileSync23(path28.join(repoRoot2, page.source), "utf-8");
const frontMatter = splitFrontMatter(raw);
const title = deriveTitle(frontMatter.data, frontMatter.body, page.source);
page.title = title;
const order = frontMatter.data.order;
if (typeof order === "number" && Number.isFinite(order)) page.order = order;
const rewritten = rewriteLinks({
body: stripHtmlComments(frontMatter.body),
page,
sourceIndex,
routes,
ignoreLinkPrefixes,
pathKind: (rel2) => pathKind(path28.join(repoRoot2, rel2)),
repoUrl
});
for (const issue of rewritten.issues) {
(issue.kind === "broken" ? brokenLinks : externalLinks).push(issue);
}
const outData = { ...frontMatter.data, title };
const blobBase = repoUrl.replace(/\/$/, "").replace(/\/(blob|tree)$/, "");
const sourceNote = `
---
<sub>Source: [\`${page.source}\`](${blobBase}/blob/main/${page.source}) \u2014 edit that file, not this page.</sub>
`;
const outFile = path28.join(outDir, page.filePath);
mkdirSync11(path28.dirname(outFile), { recursive: true });
writeFileSync15(
outFile,
joinFrontMatter(outData, rewritten.body.trimEnd() + sourceNote)
);
}
const navigation = buildNavigation(pages);
const reservedViolations = pages.map((p) => p.filePath).filter((filePath) => hasReservedSegment(filePath));
const publishManifest = {
version: 1,
count: pages.length,
sources: pages.map((p) => p.source).sort()
};
return {
outDir,
pages,
navigation,
brokenLinks,
externalLinks,
publishManifest,
reservedViolations
};
}
function titleizeDir(name) {
return titleizeStem(name);
}
function buildNavigation(pages) {
const sections = /* @__PURE__ */ new Map();
for (const page of pages) {
const key = page.sectionSlug;
if (!sections.has(key)) {
sections.set(key, {
title: page.sectionTitle,
order: page.sectionOrder,
slug: page.sectionSlug,
pages: []
});
}
sections.get(key).pages.push(page);
}
const sortedSections = [...sections.values()].sort(
(a, b) => a.order - b.order || a.title.localeCompare(b.title)
);
const sortPages = (list) => [...list].sort(
(a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER) || a.filePath.localeCompare(b.filePath)
);
const docRef = (page) => {
const file = page.filePath.replace(/\.md$/, "");
return page.route === `/${file}` ? file : { type: "doc", file, path: page.route };
};
const items = [];
for (const section of sortedSections) {
const indexPage = section.pages.find(
(p) => p.route === `/${section.slug}` || section.slug === "" && p.route === "/"
);
const rest = section.pages.filter((p) => p !== indexPage);
const rootPages = [];
const dirs = /* @__PURE__ */ new Map();
for (const page of rest) {
const rel2 = page.route.replace(/^\//, "");
const sectionPrefix = section.slug ? `${section.slug}/` : "";
const relParts = (sectionPrefix && rel2.startsWith(sectionPrefix) ? rel2.slice(sectionPrefix.length) : rel2).split("/").filter(Boolean);
if (relParts.length <= 1) {
rootPages.push(page);
continue;
}
const dirKey = relParts.slice(0, -1).join("/");
if (!dirs.has(dirKey)) {
dirs.set(dirKey, {
label: titleizeDir(relParts[relParts.length - 2]),
routePath: relParts.slice(0, -1).join("/"),
pages: [],
dirs: /* @__PURE__ */ new Map()
});
}
dirs.get(dirKey).pages.push(page);
}
const byRoute = new Map(section.pages.map((p) => [p.route, p]));
const childItems = sortPages(rootPages).map(docRef);
for (const dir of [...dirs.values()].sort(
(a, b) => a.routePath.localeCompare(b.routePath)
)) {
const dirRoute = `/${[section.slug, dir.routePath].filter(Boolean).join("/")}`.replace(
/\/$/,
""
) || "/";
const dirIndex = byRoute.get(dirRoute);
const grandchildren = sortPages(
dir.pages.filter((p) => p !== dirIndex)
).map(docRef);
if (grandchildren.length === 0 && dirIndex) {
childItems.push(docRef(dirIndex));
continue;
}
childItems.push({
type: "category",
label: dir.label,
...dirIndex ? { link: docRef(dirIndex) } : {},
items: grandchildren
});
}
items.push({
type: "category",
label: section.title,
collapsible: false,
...indexPage ? { link: docRef(indexPage) } : {},
items: childItems
});
}
return [
{
type: "category",
label: "Documentation",
link: { type: "doc", file: "index", path: "/" },
items
}
];
}
function renderZudokuConfig(args) {
const versionLine = args.baseline ? `Documents @mesh-tech/* ${args.baseline} \xB7 built from ${args.commit ?? "unknown"}` : `Local build \xB7 ${args.commit ?? "unknown"}`;
const config = {
metadata: {
title: args.title,
description: args.description
},
site: {
title: args.title,
logoUrl: "/",
footer: {
copyright: `${versionLine} \xB7 /version.json`
}
},
theme: "THEME_IMPORT",
navigation: args.navigation,
docs: {
files: ["/content/**/*.{md,mdx}"],
defaultOptions: {
toc: true,
showLastModified: false
}
},
redirects: []
// Search is Zudoku's built-in Pagefind index — no config needed.
};
const json = JSON.stringify(config, null, 2).replace(
`"THEME_IMPORT"`,
"portalTheme"
);
return `/**
* GENERATED by \`mesh docs portal\` \u2014 do not edit.
*
* The navigation array is derived from the repo's docs.json roots and the
* markdown tree beneath them. To change what the site publishes, change the
* tree; to change section labels or order, change the root's docs.json.
* Regenerate: pnpm exec mesh docs portal --assemble-only
*/
import type { ZudokuConfig } from "zudoku";
import { portalTheme } from "./theme.js";
const config: ZudokuConfig = ${json};
export default config;
`;
}
function renderVersionJson(args) {
return `${JSON.stringify(
{
meshBaseline: args.baseline ?? null,
commit: args.commit,
builtAt: args.builtAt
},
null,
2
)}
`;
}
function currentCommit(repoRoot2) {
try {
return execFileSync19("git", ["rev-parse", "--short", "HEAD"], {
cwd: repoRoot2,
encoding: "utf-8"
}).trim();
} catch {
return "unknown";
}
}
function currentBaseline(repoRoot2) {
try {
const pkg = JSON.parse(
readFileSync23(path28.join(repoRoot2, "libs/app-kit/package.json"), "utf-8")
);
return pkg.version;
} catch {
return void 0;
}
}
function publishSetAtRef(repoRoot2, ref) {
const git = (gitArgs) => execFileSync19("git", gitArgs, {
cwd: repoRoot2,
encoding: "utf-8",
maxBuffer: 64 * 1024 * 1024
});
const allFiles = git(["ls-tree", "-r", "--name-only", ref]).split("\n").filter(Boolean);
const docsJsonPaths = allFiles.filter(
(file) => file === "docs.json" || file.endsWith("/docs.json")
);
const published = /* @__PURE__ */ new Set();
for (const docsJsonPath of docsJsonPaths) {
const rootDir = docsJsonPath === "docs.json" ? "" : docsJsonPath.slice(0, -"/docs.json".length);
if (rootDir && hasReservedSegment(rootDir)) continue;
let config;
try {
config = JSON.parse(git(["show", `${ref}:${docsJsonPath}`]));
} catch {
continue;
}
const prefix2 = rootDir ? `${rootDir}/` : "";
for (const file of allFiles) {
if (!file.startsWith(prefix2) || !file.toLowerCase().endsWith(".md"))
continue;
const rel2 = file.slice(prefix2.length);
if (hasReservedSegment(rel2)) continue;
if (path28.posix.basename(file).toLowerCase() === "readme.md") continue;
published.add(file);
}
for (const include of config.include ?? []) {
const normalized = include.replace(/\\/g, "/").replace(/^\.\//, "");
if (hasReservedSegment(normalized)) continue;
if (allFiles.includes(normalized)) published.add(normalized);
}
}
return [...published].sort();
}
function diffPublishSets(base, head) {
const baseSet = new Set(base);
const headSet = new Set(head);
return {
added: head.filter((source) => !baseSet.has(source)),
removed: base.filter((source) => !headSet.has(source))
};
}
function renderPublishSetSummary(manifest, diff) {
const lines = [
`## Docs portal publish set`,
``,
`**${manifest.count} pages publish** from this tree.`,
``
];
if (diff && (diff.added.length > 0 || diff.removed.length > 0)) {
lines.push(`### Changes vs the merge base`, ``);
for (const source of diff.added) lines.push(`- \u2795 \`${source}\``);
for (const source of diff.removed) lines.push(`- \u2796 \`${source}\``);
lines.push(
``,
`Growing the public surface is a deliberate act \u2014 a reviewer should see this list.`
);
} else if (diff) {
lines.push(`No change vs the merge base.`);
}
lines.push(``, `<details><summary>Full publish set</summary>`, ``);
for (const source of manifest.sources) lines.push(`- \`${source}\``);
lines.push(``, `</details>`);
return `${lines.join("\n")}
`;
}
var DEFAULT_REPO_URL, DEFAULT_IGNORE_LINK_PREFIXES;
var init_assemble = __esm({
"libs/mesh-cli/src/docs/assemble.ts"() {
"use strict";
init_discover();
init_schema2();
DEFAULT_REPO_URL = "https://github.com/mesh-tech/mesh-platform";
DEFAULT_IGNORE_LINK_PREFIXES = ["node_modules/"];
}
});
// libs/mesh-cli/src/docs/cli-reference.ts
var cli_reference_exports = {};
__export(cli_reference_exports, {
extractCliReference: () => extractCliReference,
flattenCommands: () => flattenCommands,
formatDefault: () => formatDefault,
normalizeMachinePaths: () => normalizeMachinePaths,
renderCliReferenceMarkdown: () => renderCliReferenceMarkdown,
slugifyCommandPath: () => slugifyCommandPath
});
import os8 from "node:os";
function currentMachinePaths() {
return { homeDir: os8.homedir(), tmpDir: os8.tmpdir() };
}
function normalizeMachinePaths(value, paths) {
const candidates = [
{ prefix: paths.tmpDir, symbol: "$TMPDIR" },
{ prefix: paths.homeDir, symbol: "~" }
];
const substitutions = candidates.filter((candidate) => candidate.prefix && candidate.prefix !== "/").sort((a, b) => b.prefix.length - a.prefix.length);
for (const { prefix: prefix2, symbol } of substitutions) {
if (value === prefix2) return symbol;
if (value.startsWith(`${prefix2}/`)) return symbol + value.slice(prefix2.length);
}
return value;
}
function formatDefault(value, paths = currentMachinePaths()) {
if (value === void 0 || value === null) return void 0;
if (typeof value === "string") return normalizeMachinePaths(value, paths);
if (typeof value === "boolean" || typeof value === "number") return String(value);
return normalizeMachinePaths(JSON.stringify(value), paths);
}
function slugifyCommandPath(path46) {
return path46.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}
function extractOptions(command) {
const options = command.options ?? [];
return options.map((option) => option).filter((option) => !option.hidden).map((option) => {
const ref = {
flags: option.flags ?? "",
description: option.description ?? "",
required: Boolean(option.mandatory ?? option.required ?? false)
};
const rendered = formatDefault(option.defaultValue);
if (rendered !== void 0) ref.defaultValue = rendered;
if (option.argChoices?.length) ref.choices = [...option.argChoices];
return ref;
});
}
function extractArgs(command) {
const internals = command;
const declared = internals.registeredArguments ?? internals._args ?? [];
if (!Array.isArray(declared) || declared.length === 0) return "";
return declared.map((argument) => {
const arg = argument;
const name = typeof arg.name === "function" ? arg.name() : "arg";
const spread = arg.variadic ? "..." : "";
return arg.required ? `<${name}${spread}>` : `[${name}${spread}]`;
}).join(" ");
}
function isUndocumented(command) {
const internals = command;
return internals._name === "help" || internals._hidden === true;
}
function extractCommand(command, parentPath, depth) {
const internals = command;
const name = internals._name ?? "";
const path46 = parentPath ? `${parentPath} ${name}` : name;
return {
path: path46,
name,
aliases: [...internals._aliases ?? []],
description: internals._description ?? "",
args: extractArgs(command),
options: extractOptions(command),
slug: slugifyCommandPath(path46),
depth,
subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, path46, depth + 1))
};
}
function extractCliReference(program2) {
const internals = program2;
const rootName = internals._name ?? "mesh";
return {
name: rootName,
description: internals._description ?? "",
options: extractOptions(program2).filter(
(option) => !/(^|\s)(-V|--version)(\s|,|$)/.test(option.flags)
),
commands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, rootName, 1))
};
}
function flattenCommands(reference) {
const flat = [];
const visit = (command) => {
flat.push(command);
command.subcommands.forEach(visit);
};
reference.commands.forEach(visit);
return flat;
}
function cell(text) {
return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
}
function renderOptionsTable(options) {
if (options.length === 0) return [];
const lines = ["| Option | Description | Default |", "| --- | --- | --- |"];
for (const option of options) {
const notes = [];
if (option.required) notes.push("**required**");
if (option.choices?.length) notes.push(`one of: ${option.choices.join(", ")}`);
const description = [cell(option.description), ...notes].filter(Boolean).join(" \u2014 ");
const defaultValue = option.defaultValue === void 0 ? "" : `\`${cell(option.defaultValue)}\``;
lines.push(`| \`${cell(option.flags)}\` | ${description} | ${defaultValue} |`);
}
lines.push("");
return lines;
}
function renderCommand(command) {
const heading = "#".repeat(Math.min(command.depth + 1, 4));
const usage = [command.path, command.options.length > 0 ? "[options]" : "", command.args].filter(Boolean).join(" ");
const lines = [`${heading} \`${command.path}\``, ""];
if (command.description) lines.push(command.description, "");
if (command.aliases.length > 0) {
lines.push(`Alias: ${command.aliases.map((alias) => `\`${alias}\``).join(", ")}`, "");
}
lines.push("```bash", usage, "```", "");
lines.push(...renderOptionsTable(command.options));
command.subcommands.forEach((child) => lines.push(...renderCommand(child)));
return lines;
}
function renderCliReferenceMarkdown(reference) {
const lines = [
"<!--",
" GENERATED FILE \u2014 do not edit by hand.",
" Source: the commander tree in libs/mesh-cli/src/program.ts",
" Rebuild: pnpm exec mesh docs cli-reference",
"-->",
"",
"# `mesh` CLI reference",
"",
reference.description,
"",
"Every command below is generated from the CLI's own command tree, so this",
"page cannot drift from the binary you have installed. Run any command with",
"`--help` for the same information at the terminal.",
""
];
lines.push(...renderOptionsTable(reference.options));
lines.push("## Commands", "");
for (const command of reference.commands) {
const summary = cell(command.description) || "\u2014";
lines.push(`- [\`${command.path}\`](#${command.slug}) \u2014 ${summary}`);
}
lines.push("");
reference.commands.forEach((command) => lines.push(...renderCommand(command)));
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
`;
}
var init_cli_reference = __esm({
"libs/mesh-cli/src/docs/cli-reference.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/docs/portal.ts
import { spawn as spawn7 } from "node:child_process";
import {
appendFileSync,
existsSync as existsSync22,
mkdirSync as mkdirSync12,
mkdtempSync as mkdtempSync3,
readFileSync as readFileSync24,
writeFileSync as writeFileSync16
} from "node:fs";
import { tmpdir as tmpdir6 } from "node:os";
import path29 from "node:path";
function runAssemble(args) {
const discovery = discoverDocRoots(args.repoRoot);
const assembled = assemblePortal({
repoRoot: args.repoRoot,
discovery,
outDir: args.outDir,
meshBaseline: currentBaseline(args.repoRoot),
commit: currentCommit(args.repoRoot)
});
const errors = [...discovery.errors];
if (args.writeFiles !== false) {
const manifestPath = args.outDir.includes(tmpdir6()) ? path29.join(args.outDir, PUBLISH_MANIFEST) : path29.join(args.outDir, "..", PUBLISH_MANIFEST);
writeFileSync16(
manifestPath,
`${JSON.stringify(assembled.publishManifest, null, 2)}
`
);
if (args.configOut) {
writeFileSync16(
args.configOut,
renderZudokuConfig({
navigation: assembled.navigation,
title: SITE_TITLE,
description: SITE_DESCRIPTION,
baseline: currentBaseline(args.repoRoot),
commit: currentCommit(args.repoRoot)
})
);
const versionDir = path29.join(
path29.dirname(args.configOut),
VERSION_JSON_DIR
);
mkdirSync12(versionDir, { recursive: true });
writeFileSync16(
path29.join(versionDir, VERSION_JSON),
renderVersionJson({
baseline: currentBaseline(args.repoRoot),
commit: currentCommit(args.repoRoot),
builtAt: (/* @__PURE__ */ new Date()).toISOString()
})
);
}
}
return { assembled, errors };
}
function reportAssembly(result) {
const { assembled } = result;
logSuccess(
`Assembled ${assembled.pages.length} pages from ${new Set(assembled.pages.map((p) => p.sectionTitle)).size} sections \u2192 ${assembled.outDir}`
);
if (assembled.externalLinks.length > 0) {
const unique = new Set(
assembled.externalLinks.map((issue) => `${issue.page} \u2192 ${issue.href}`)
);
logInfo(
`${unique.size} link${unique.size === 1 ? "" : "s"} point at repo files outside the portal and were rewritten to blob links`
);
}
if (assembled.brokenLinks.length > 0) {
logWarn(`${assembled.brokenLinks.length} link(s) resolve to nothing:`);
for (const issue of assembled.brokenLinks) {
logWarn(` ${issue.page}: ${issue.href}`);
}
}
}
async function checkPortal(args) {
const scratch = mkdtempSync3(path29.join(tmpdir6(), "mesh-docs-check-"));
const result = runAssemble({ repoRoot: args.repoRoot, outDir: scratch });
const failures = [...result.errors];
for (const violation of result.assembled.reservedViolations) {
failures.push(
`assembled output path contains a reserved segment: ${violation}`
);
}
for (const issue of result.assembled.brokenLinks) {
failures.push(`broken link on ${issue.page}: ${issue.href}`);
}
const cliReferenceError = await args.checkCliReference();
if (cliReferenceError) failures.push(cliReferenceError);
reportAssembly(result);
if (args.printManifest) {
process.stdout.write(
`${JSON.stringify(result.assembled.publishManifest, null, 2)}
`
);
}
if (args.manifestOut) {
mkdirSync12(path29.dirname(args.manifestOut), { recursive: true });
writeFileSync16(
args.manifestOut,
`${JSON.stringify(result.assembled.publishManifest, null, 2)}
`
);
}
if (args.diffBase) {
writePublishSetSummary(
args.repoRoot,
args.diffBase,
result.assembled.publishManifest
);
}
return failures;
}
function writePublishSetSummary(repoRoot2, diffBase, manifest) {
let diff = null;
try {
const base = publishSetAtRef(repoRoot2, diffBase);
diff = diffPublishSets(base, manifest.sources);
} catch (error) {
logWarn(
`could not compute the publish-set diff against ${diffBase}: ${error instanceof Error ? error.message : String(error)}`
);
}
const summary = renderPublishSetSummary(manifest, diff);
process.stdout.write(`
${summary}`);
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
if (summaryPath) {
try {
appendFileSync(summaryPath, summary);
} catch {
logWarn(`GITHUB_STEP_SUMMARY is set but not writable (${summaryPath})`);
}
}
}
function runZudoku(args) {
const cliArgs = args.mode === "dev" ? ["dev", "--port", String(args.port ?? 3e3)] : ["build"];
logInfo(`Running \`zudoku ${cliArgs.join(" ")}\` in ${args.appDir}`);
return new Promise((resolve19, reject) => {
const child = spawn7("pnpm", ["exec", "zudoku", ...cliArgs], {
cwd: args.appDir,
stdio: "inherit",
env: process.env
});
child.on("error", reject);
child.on("close", (code) => resolve19(code ?? 1));
});
}
async function runPortalCommand(opts, deps) {
const { repoRoot: repoRoot2 } = deps;
if (opts.check) {
const failures = await checkPortal({
repoRoot: repoRoot2,
diffBase: opts.diffBase,
printManifest: opts.printManifest ?? false,
manifestOut: opts.manifestOut,
checkCliReference: deps.checkCliReference
});
if (failures.length > 0) {
for (const failure of failures) logError(failure);
throw new Error(
`docs portal check failed with ${failures.length} problem(s)`
);
}
logSuccess("Docs portal check passed");
return;
}
const appDir = path29.join(repoRoot2, DOCS_APP_DIR);
const outDir = opts.out ? path29.resolve(repoRoot2, opts.out) : path29.join(appDir, CONTENT_DIR);
const result = runAssemble({
repoRoot: repoRoot2,
outDir,
configOut: path29.join(appDir, GENERATED_CONFIG)
});
reportAssembly(result);
if (result.errors.length > 0) {
for (const error of result.errors) logError(error);
throw new Error(
`docs portal assembly failed with ${result.errors.length} problem(s)`
);
}
if (opts.assembleOnly || opts.out) {
logInfo(
`Assembled content at ${path29.relative(repoRoot2, outDir)} \u2014 build with: pnpm --filter @mesh-tech/docs build`
);
return;
}
const mode = opts.serve ? "dev" : "build";
let port;
if (opts.serve && opts.port) {
port = Number.parseInt(opts.port, 10);
if (!Number.isInteger(port) || port < 0 || port > 65535) {
throw new Error(`Invalid --port: ${opts.port}`);
}
}
const code = await runZudoku({
appDir,
mode,
...port !== void 0 ? { port } : {}
});
if (code !== 0) throw new Error(`zudoku ${mode} exited with code ${code}`);
if (mode === "build") {
logSuccess(
`Portal built \u2192 ${path29.join(path29.relative(repoRoot2, appDir), "dist")}`
);
}
}
async function cliReferenceStaleness(repoRoot2) {
const [
{ createProgram: createProgram2 },
{ extractCliReference: extractCliReference2, renderCliReferenceMarkdown: renderCliReferenceMarkdown2 }
] = await Promise.all([
Promise.resolve().then(() => (init_program(), program_exports)),
Promise.resolve().then(() => (init_cli_reference(), cli_reference_exports))
]);
const outPath = path29.join(repoRoot2, "docs/portal/generated/cli-reference.md");
const markdown = renderCliReferenceMarkdown2(
extractCliReference2(createProgram2())
);
const existing = existsSync22(outPath) ? readFileSync24(outPath, "utf-8") : null;
if (existing === markdown) return null;
return `${existing === null ? "CLI reference is missing" : "CLI reference is stale"}: docs/portal/generated/cli-reference.md \u2014 the commander tree changed; regenerate and commit with \`pnpm exec mesh docs cli-reference\``;
}
var DOCS_APP_DIR, CONTENT_DIR, GENERATED_CONFIG, PUBLISH_MANIFEST, VERSION_JSON_DIR, VERSION_JSON, SITE_TITLE, SITE_DESCRIPTION;
var init_portal = __esm({
"libs/mesh-cli/src/docs/portal.ts"() {
"use strict";
init_log();
init_assemble();
init_discover();
DOCS_APP_DIR = "apps/docs";
CONTENT_DIR = "content";
GENERATED_CONFIG = "zudoku.config.ts";
PUBLISH_MANIFEST = "publish-manifest.json";
VERSION_JSON_DIR = "public";
VERSION_JSON = "version.json";
SITE_TITLE = "Mesh Developer Portal";
SITE_DESCRIPTION = "Build and run financial-services apps on Mesh Platform with the mesh developer CLI.";
}
});
// libs/mesh-cli/src/utils/build-info.ts
import { execFileSync as execFileSync20 } from "child_process";
import * as fs25 from "fs";
import * as path30 from "path";
import { fileURLToPath as fileURLToPath3 } from "url";
function detectRuntimeMode(modulePath) {
return modulePath.split(/[\\/]/).includes("dist") ? "dist" : "source";
}
function formatVersionLine(info) {
if (info.mode === "source") {
const at = [info.commit, info.builtAt].filter(Boolean).join(" ");
return `${info.version} (source${at ? ` @ ${at}` : ""})`;
}
const built = info.builtAt ? ` ${info.builtAt.slice(0, 10)}` : "";
const commit = info.commit ? `, commit ${info.commit}` : "";
const detail = built || commit ? `${built}${commit}` : " \u2014 build metadata unavailable";
return `${info.version} (published build${detail})`;
}
function findCliPackageRoot(startDir) {
let dir = startDir;
for (let i = 0; i < 8; i++) {
try {
const pkg = JSON.parse(fs25.readFileSync(path30.join(dir, "package.json"), "utf-8"));
if (pkg.name === "@mesh-tech/mesh-cli") return dir;
} catch {
}
const parent = path30.dirname(dir);
if (parent === dir) break;
dir = parent;
}
return null;
}
function resolveCliRuntime(opts) {
const vcs = opts?.vcs ?? true;
const here = path30.dirname(fileURLToPath3(import.meta.url));
const mode = detectRuntimeMode(here);
const root = findCliPackageRoot(here);
let version = "0.0.0";
if (root) {
try {
version = JSON.parse(fs25.readFileSync(path30.join(root, "package.json"), "utf-8")).version ?? version;
} catch {
}
}
const info = { version, mode };
if (!root) return info;
if (mode === "dist") {
try {
const meta = JSON.parse(
fs25.readFileSync(path30.join(root, "dist", "build-info.json"), "utf-8")
);
info.commit = meta.commit;
info.builtAt = meta.builtAt;
} catch {
}
return info;
}
if (!vcs) return info;
try {
const git = (args) => execFileSync20("git", args, {
cwd: root,
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
}).trim();
info.commit = git(["rev-parse", "--short", "HEAD"]);
info.builtAt = git(["log", "-1", "--format=%cs"]);
} catch {
}
return info;
}
var init_build_info = __esm({
"libs/mesh-cli/src/utils/build-info.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/docs/serve.ts
import { createServer as createServer8 } from "node:http";
import { readFile } from "node:fs/promises";
import { extname as extname2, join as join27, normalize, sep as sep5 } from "node:path";
async function serveDocsSite(args) {
const root = normalize(args.root);
const rootPrefix = root.endsWith(sep5) ? root : root + sep5;
const server = createServer8((req, res) => {
void (async () => {
const url = new URL(req.url ?? "/", "http://localhost");
const pathname = decodeURIComponent(url.pathname);
if (pathname === "/healthz") {
res.writeHead(200, { "content-type": "text/plain" }).end("ok");
return;
}
const candidates = [
pathname,
`${pathname}.html`,
`${pathname.replace(/\/$/, "")}/index.html`
];
if (pathname !== "/") candidates.push("/index.html");
for (const candidate of candidates) {
const filePath = normalize(join27(root, candidate));
if (filePath !== root && !filePath.startsWith(rootPrefix)) continue;
try {
const body = await readFile(filePath);
const type = CONTENT_TYPES[extname2(filePath)] ?? "application/octet-stream";
const cache = candidate.includes("/assets/") ? "public, max-age=31536000, immutable" : "no-cache";
res.writeHead(200, { "content-type": type, "cache-control": cache });
res.end(body);
return;
} catch {
}
}
res.writeHead(404, { "content-type": "text/plain" }).end("not found");
})().catch(() => {
res.writeHead(500).end("internal error");
});
});
await new Promise((resolve19, reject) => {
server.once("error", reject);
server.listen(args.port, "127.0.0.1", () => resolve19());
});
const address = server.address();
const port = typeof address === "object" && address ? address.port : args.port;
return {
url: `http://127.0.0.1:${port}`,
port,
close: () => new Promise((resolve19, reject) => {
server.close((error) => error ? reject(error) : resolve19());
})
};
}
var CONTENT_TYPES;
var init_serve = __esm({
"libs/mesh-cli/src/docs/serve.ts"() {
"use strict";
CONTENT_TYPES = {
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".mjs": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".json": "application/json; charset=utf-8",
".map": "application/json; charset=utf-8",
".svg": "image/svg+xml",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".txt": "text/plain; charset=utf-8",
".md": "text/markdown; charset=utf-8",
".webmanifest": "application/manifest+json"
};
}
});
// libs/mesh-cli/src/docs/registry-docs.ts
import { execFileSync as execFileSync21 } from "node:child_process";
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync26, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
import { tmpdir as tmpdir7 } from "node:os";
import path31 from "node:path";
function readDocsRegistryAuth(npmrcPath2 = homeNpmrcPath()) {
if (!existsSync23(npmrcPath2)) return null;
return parseNpmrcRegistryAuth(readFileSync26(npmrcPath2, "utf-8"));
}
function packumentUrl(endpoint, pkg) {
return `${endpoint.replace(/\/+$/, "")}/${pkg.replace(/\//g, "%2f")}`;
}
async function registryFetch(auth, url, fetchImpl, accept = "application/json") {
return fetchImpl(url, {
headers: {
authorization: `Bearer ${auth.token}`,
accept
}
});
}
function compareVersions(a, b) {
const parse4 = (v) => {
const parts = v.split(".").map((n) => Number.parseInt(n, 10) || 0);
while (parts.length < 3) parts.push(0);
return parts;
};
const pa = parse4(a);
const pb = parse4(b);
for (let i = 0; i < 3; i++) {
if (pa[i] !== pb[i]) return pa[i] - pb[i];
}
return 0;
}
async function listDocsVersions(auth, fetchImpl = fetch) {
const response = await registryFetch(auth, packumentUrl(auth.endpoint, DOCS_PACKAGE), fetchImpl).catch((error) => {
throw new Error(
`could not reach the package registry: ${error instanceof Error ? error.message : String(error)}`
);
});
if (response.status === 401 || response.status === 403) {
throw new Error(
`registry auth rejected the token (${response.status}). Refresh it: mesh registry login`
);
}
if (response.status === 404) {
throw new Error(
`${DOCS_PACKAGE} is not published yet \u2014 no release has shipped the docs artifact. In a mesh-platform checkout, use \`mesh docs start\` against the working tree instead.`
);
}
if (!response.ok) {
throw new Error(`registry returned ${response.status} for ${DOCS_PACKAGE}`);
}
const packument = await response.json();
const versions = Object.keys(packument.versions ?? {}).sort(compareVersions);
return { versions, latest: packument["dist-tags"]?.latest };
}
function docsCacheRoot() {
const base = process.env.XDG_CACHE_HOME ?? path31.join(homeDir(), ".cache");
return path31.join(base, "mesh", "docs");
}
function homeDir() {
return process.env.HOME ?? tmpdir7();
}
async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fetchImpl = fetch) {
const targetDir = path31.join(cacheRoot, version);
if (existsSync23(path31.join(targetDir, "dist", "index.html"))) return targetDir;
const listResponse = await registryFetch(auth, packumentUrl(auth.endpoint, DOCS_PACKAGE), fetchImpl);
if (!listResponse.ok) {
throw new Error(`registry returned ${listResponse.status} for ${DOCS_PACKAGE}`);
}
const packument = await listResponse.json();
const tarball = packument.versions?.[version]?.dist?.tarball;
if (!tarball) {
throw new Error(
`${DOCS_PACKAGE}@${version} is not in the registry. Run \`mesh docs list\` to see published versions.`
);
}
if (new URL(tarball).origin !== new URL(auth.endpoint).origin) {
throw new Error(
`${DOCS_PACKAGE}@${version} points its tarball at ${new URL(tarball).origin}, not the registry (${new URL(auth.endpoint).origin}) \u2014 refusing to send the registry token off-origin`
);
}
const tgzResponse = await registryFetch(auth, tarball, fetchImpl, "application/octet-stream");
if (!tgzResponse.ok) {
throw new Error(`downloading ${DOCS_PACKAGE}@${version} failed: ${tgzResponse.status}`);
}
mkdirSync13(cacheRoot, { recursive: true });
const tgzPath = path31.join(cacheRoot, `.${version}.tgz`);
writeFileSync17(tgzPath, Buffer.from(await tgzResponse.arrayBuffer()));
const staging = path31.join(cacheRoot, `.staging-${version}`);
rmSync6(staging, { recursive: true, force: true });
mkdirSync13(staging, { recursive: true });
try {
execFileSync21("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
stdio: ["pipe", "pipe", "pipe"]
});
} finally {
rmSync6(tgzPath, { force: true });
}
if (!existsSync23(path31.join(staging, "dist", "index.html"))) {
rmSync6(staging, { recursive: true, force: true });
throw new Error(`${DOCS_PACKAGE}@${version} unpacked without a dist/index.html \u2014 the artifact is malformed`);
}
rmSync6(targetDir, { recursive: true, force: true });
renameSync3(staging, targetDir);
return targetDir;
}
var DOCS_PACKAGE;
var init_registry_docs = __esm({
"libs/mesh-cli/src/docs/registry-docs.ts"() {
"use strict";
init_auth_preflight();
DOCS_PACKAGE = "@mesh-tech/docs";
}
});
// libs/mesh-cli/src/docs/start.ts
var start_exports = {};
__export(start_exports, {
DOCS_TMUX_SESSION: () => DOCS_TMUX_SESSION,
docsSessionExists: () => docsSessionExists,
runDocsList: () => runDocsList,
runDocsServeStatic: () => runDocsServeStatic,
runDocsStart: () => runDocsStart,
runDocsStop: () => runDocsStop,
shouldDetach: () => shouldDetach,
startDetached: () => startDetached,
tmuxAvailable: () => tmuxAvailable,
tmuxInstallHint: () => tmuxInstallHint,
tmuxServeArgs: () => tmuxServeArgs
});
import { execFileSync as execFileSync22 } from "node:child_process";
import { appendFileSync as appendFileSync2, existsSync as existsSync24, writeFileSync as writeFileSync18 } from "node:fs";
import path32 from "node:path";
function registryAuthOrThrow() {
const auth = readDocsRegistryAuth();
if (!auth) {
throw new Error(
"no CodeArtifact token in ~/.npmrc \u2014 run `mesh registry login` first (the docs artifact is role-gated the same way the packages are)."
);
}
return auth;
}
async function runDocsList() {
const auth = registryAuthOrThrow();
const { versions, latest } = await listDocsVersions(auth);
if (versions.length === 0) {
logInfo(`No ${DOCS_PACKAGE} versions published yet.`);
return;
}
logInfo(`Published docs versions (docs version == @mesh-tech/* baseline):`);
for (const version of [...versions].reverse()) {
const marker = version === latest ? " \u2190 latest" : "";
process.stdout.write(` ${version}${marker}
`);
}
}
function tmuxInstallHint(platform = process.platform) {
if (platform === "darwin") return "brew install tmux";
if (platform === "linux") return "sudo apt-get install tmux # or your distro's package manager";
return "install tmux via your package manager";
}
function tmuxAvailable() {
try {
execFileSync22("tmux", ["-V"], { stdio: ["pipe", "pipe", "pipe"] });
return true;
} catch {
return false;
}
}
function shouldDetach(args) {
return !args.foreground && !args.dev && args.isTTY;
}
function docsSessionExists() {
try {
execFileSync22("tmux", ["has-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
return true;
} catch {
return false;
}
}
function tmuxServeArgs(args) {
const serveArgs = ["docs", "serve-static", "--root", args.serveRoot, "--port", String(args.port)];
if (args.mode === "dist") {
return { command: ["mesh", ...serveArgs], cwd: args.repoRoot };
}
return { command: ["pnpm", "exec", "mesh", ...serveArgs], cwd: args.repoRoot };
}
async function startDetached(args) {
if (!tmuxAvailable()) {
throw new Error(
`tmux is not installed \u2014 the detached docs server runs in a tmux session named "${DOCS_TMUX_SESSION}".
Install it with: ${tmuxInstallHint()}
Or run in the foreground instead: mesh docs start --foreground`
);
}
if (docsSessionExists()) {
logInfo(`Replacing the docs server already running in tmux session "${DOCS_TMUX_SESSION}".`);
try {
execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
} catch {
}
}
const { command, cwd } = tmuxServeArgs(args);
execFileSync22(
"tmux",
["new-session", "-d", "-s", DOCS_TMUX_SESSION, "-c", cwd, "--", ...command],
{ stdio: ["pipe", "pipe", "pipe"] }
);
const deadline = Date.now() + 6e4;
const url = `http://127.0.0.1:${args.port}`;
let lastError = "";
while (Date.now() < deadline) {
try {
const response = await fetch(`${url}/healthz`);
if (response.ok) {
printReady(url, args.label);
return;
}
lastError = `HTTP ${response.status}`;
} catch (error) {
lastError = error instanceof Error ? error.message : String(error);
}
await new Promise((resolve19) => setTimeout(resolve19, 500));
}
throw new Error(
`the docs server did not answer at ${url} within 60s (${lastError || "no response"}).
Read its log with: tmux attach -t ${DOCS_TMUX_SESSION} (detach: Ctrl-B then D)`
);
}
function printReady(url, label) {
logSuccess(`Mesh docs (${label}) are live:`);
process.stdout.write(`
${url}
`);
logInfo(`Running detached in the tmux session "${DOCS_TMUX_SESSION}".`);
logInfo(` attach: tmux attach -t ${DOCS_TMUX_SESSION}`);
logInfo(` stop: mesh docs stop`);
}
function runDocsStop() {
try {
execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
logSuccess(`Stopped the docs server (tmux session "${DOCS_TMUX_SESSION}").`);
} catch {
logInfo(`No docs server is running (no tmux session named "${DOCS_TMUX_SESSION}").`);
}
}
async function runDocsServeStatic(args) {
const requestedPort = Number.parseInt(args.port, 10);
if (!Number.isInteger(requestedPort) || requestedPort < 0 || requestedPort > 65535) {
throw new Error(`Invalid --port: ${args.port}`);
}
if (!args.root || !existsSync24(path32.join(args.root, "index.html"))) {
throw new Error(
`--root ${args.root || "(empty)"} does not hold a built site (no index.html). This command exists so the detached tmux session can re-enter; you probably want \`mesh docs start\`.`
);
}
const server = await serveDocsSite({ root: args.root, port: requestedPort });
logSuccess(`Mesh docs serving at ${server.url} (from ${args.root})`);
await new Promise((resolve19) => {
const stop = () => void server.close().finally(() => resolve19());
process.once("SIGINT", stop);
process.once("SIGTERM", stop);
});
}
async function runDocsStart(args) {
const requestedPort = Number.parseInt(args.port, 10);
if (!Number.isInteger(requestedPort) || requestedPort < 0 || requestedPort > 65535) {
throw new Error(`Invalid --port: ${args.port}`);
}
const detached = shouldDetach({
foreground: args.foreground,
dev: args.dev,
isTTY: process.stdout.isTTY === true
});
if (detached && requestedPort === 0) {
throw new Error(
"--port 0 (pick a free port) needs a foreground server, because the detached session cannot report the port it chose back to this process. Use `mesh docs start --foreground --port 0`, or pass an explicit port to run detached."
);
}
const localRoots = discoverDocRoots(args.repoRoot);
const repoHasDocs = localRoots.roots.length > 0;
const registryMode = args.version !== void 0 || !repoHasDocs;
let serveRoot;
let label;
if (registryMode) {
const auth = registryAuthOrThrow();
const version = args.version === void 0 || args.version === "latest" ? (await listDocsVersions(auth)).latest : args.version;
if (!version) {
throw new Error(`the registry has no ${DOCS_PACKAGE} versions tagged latest`);
}
logInfo(`Fetching ${DOCS_PACKAGE}@${version} from the registry\u2026`);
const artifactDir = await fetchDocsArtifact(auth, version);
serveRoot = path32.join(artifactDir, "dist");
label = `@mesh-tech/* ${version} (published artifact)`;
} else {
if (localRoots.errors.length > 0) {
for (const error of localRoots.errors) logError(error);
throw new Error(`docs discovery failed with ${localRoots.errors.length} problem(s)`);
}
const appDir = path32.join(args.repoRoot, DOCS_APP_DIR);
if (args.dev) {
const result2 = runAssemble({
repoRoot: args.repoRoot,
outDir: path32.join(appDir, "content"),
configOut: path32.join(appDir, "zudoku.config.ts")
});
reportAssembly(result2);
const { spawn: spawn11 } = await import("node:child_process");
const child = spawn11("pnpm", ["exec", "zudoku", "dev", "--port", String(requestedPort)], {
cwd: appDir,
stdio: "inherit",
env: process.env
});
await new Promise((resolve19) => {
child.on("close", () => resolve19());
process.once("SIGINT", () => child.kill("SIGINT"));
process.once("SIGTERM", () => child.kill("SIGTERM"));
});
return;
}
const buildLog = path32.join(args.repoRoot, DOCS_APP_DIR, "build.log");
logInfo(`Preparing the docs from this checkout (assemble + build \u2014 log: ${path32.relative(args.repoRoot, buildLog)})\u2026`);
const result = runAssemble({
repoRoot: args.repoRoot,
outDir: path32.join(appDir, "content"),
configOut: path32.join(appDir, "zudoku.config.ts")
});
reportAssembly(result);
const { spawn: spawn10 } = await import("node:child_process");
writeFileSync18(buildLog, "");
const code = await new Promise((resolve19) => {
const child = spawn10("pnpm", ["exec", "zudoku", "build"], {
cwd: appDir,
stdio: ["ignore", "pipe", "pipe"],
env: process.env
});
child.stdout?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
child.stderr?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
child.on("error", () => resolve19(1));
child.on("close", (exitCode) => resolve19(exitCode ?? 1));
});
if (code !== 0) throw new Error(`zudoku build exited with code ${code} \u2014 see ${buildLog}`);
serveRoot = path32.join(appDir, "dist");
label = "this checkout";
}
if (detached) {
const runtime = resolveCliRuntime({ vcs: false });
await startDetached({
mode: runtime.mode,
repoRoot: args.repoRoot,
serveRoot,
port: requestedPort,
label
});
return;
}
const server = await serveDocsSite({ root: serveRoot, port: requestedPort });
logSuccess(`Mesh docs (${label}) serving at ${server.url}`);
logInfo("Press Ctrl-C to stop");
await new Promise((resolve19) => {
const stop = () => {
void server.close().finally(() => resolve19());
};
process.once("SIGINT", stop);
process.once("SIGTERM", stop);
});
}
var DOCS_TMUX_SESSION;
var init_start = __esm({
"libs/mesh-cli/src/docs/start.ts"() {
"use strict";
init_log();
init_build_info();
init_discover();
init_portal();
init_serve();
init_registry_docs();
DOCS_TMUX_SESSION = "mesh-docs";
}
});
// libs/mesh-cli/src/commands/docs.ts
import * as fs26 from "fs";
import * as path33 from "path";
import { createRequire } from "module";
import { pathToFileURL } from "url";
function findProjectRoot(start = process.cwd()) {
let dir = start;
while (true) {
if (fs26.existsSync(path33.join(dir, "package.json"))) return dir;
const parent = path33.dirname(dir);
if (parent === dir) return start;
dir = parent;
}
}
function resolvePath(projectRoot, candidate) {
return path33.isAbsolute(candidate) ? candidate : path33.join(projectRoot, candidate);
}
function ensureParentDir(filePath) {
fs26.mkdirSync(path33.dirname(filePath), { recursive: true });
}
function readConfig2(configPath) {
if (!fs26.existsSync(configPath)) {
throw new Error(`Docs config not found: ${configPath}`);
}
const raw = fs26.readFileSync(configPath, "utf-8");
const parsed = JSON.parse(raw);
if (parsed.openapi && !Array.isArray(parsed.openapi)) {
throw new Error(`Invalid docs config: 'openapi' must be an array`);
}
return parsed;
}
function validateOpenApiSpec(specPath) {
const content = fs26.readFileSync(specPath, "utf-8");
const parsed = JSON.parse(content);
if (typeof parsed.openapi !== "string") {
throw new Error(`Invalid OpenAPI spec (${specPath}): missing 'openapi'`);
}
if (!parsed.info || typeof parsed.info !== "object") {
throw new Error(`Invalid OpenAPI spec (${specPath}): missing 'info' object`);
}
if (!parsed.paths || typeof parsed.paths !== "object") {
throw new Error(`Invalid OpenAPI spec (${specPath}): missing 'paths' object`);
}
}
async function loadSymxchangeGenerator(projectRoot) {
const requireFromProject = createRequire(path33.join(projectRoot, "package.json"));
const entryPath = requireFromProject.resolve("@mesh-tech/jh-symitar-api");
const mod = await import(pathToFileURL(entryPath).href);
const fn = mod.generateSymxchangeOpenApi;
if (typeof fn !== "function") {
throw new Error("@mesh-tech/jh-symitar-api does not export generateSymxchangeOpenApi()");
}
return {
generateSymxchangeOpenApi: fn
};
}
async function buildDocs(configPathArg) {
const cwd = process.cwd();
const configPath = path33.isAbsolute(configPathArg ?? "") ? configPathArg : path33.resolve(cwd, configPathArg ?? "docs/docs.config.json");
const projectRoot = findProjectRoot(path33.dirname(configPath));
const config = readConfig2(configPath);
logInfo(`Using docs config: ${configPath}`);
const generatedSpecs = /* @__PURE__ */ new Set();
for (const task of config.openapi ?? []) {
if (task.kind === "file") {
const inputPath = resolvePath(projectRoot, task.input);
const outputPath = resolvePath(projectRoot, task.output ?? task.input);
if (!fs26.existsSync(inputPath)) {
throw new Error(`OpenAPI source file not found: ${inputPath}`);
}
if (inputPath !== outputPath) {
ensureParentDir(outputPath);
fs26.copyFileSync(inputPath, outputPath);
logSuccess(`Copied OpenAPI spec \u2192 ${path33.relative(projectRoot, outputPath)}`);
} else {
logInfo(`Using OpenAPI spec: ${path33.relative(projectRoot, inputPath)}`);
}
validateOpenApiSpec(outputPath);
generatedSpecs.add(outputPath);
continue;
}
if (task.kind === "symxchange") {
const outputPath = resolvePath(projectRoot, task.output);
const { generateSymxchangeOpenApi } = await loadSymxchangeGenerator(projectRoot);
const spec = await generateSymxchangeOpenApi({
enabled: task.enabled,
title: task.title,
version: task.version,
description: task.description,
basePath: task.basePath,
servers: task.servers
});
ensureParentDir(outputPath);
fs26.writeFileSync(outputPath, JSON.stringify(spec, null, 2));
validateOpenApiSpec(outputPath);
generatedSpecs.add(outputPath);
logSuccess(`Generated SymXchange OpenAPI \u2192 ${path33.relative(projectRoot, outputPath)}`);
continue;
}
const exhaustive = task;
throw new Error(`Unsupported OpenAPI task kind: ${exhaustive.kind}`);
}
for (const relPath of config.validate ?? []) {
const abs = resolvePath(projectRoot, relPath);
if (!fs26.existsSync(abs)) {
throw new Error(`OpenAPI spec to validate not found: ${abs}`);
}
validateOpenApiSpec(abs);
generatedSpecs.add(abs);
logSuccess(`Validated OpenAPI spec: ${path33.relative(projectRoot, abs)}`);
}
if (generatedSpecs.size === 0) {
logInfo("No docs tasks configured (nothing to do)");
return;
}
logSuccess(`Docs build complete (${generatedSpecs.size} OpenAPI file${generatedSpecs.size === 1 ? "" : "s"})`);
}
async function renderCliReference() {
const [{ createProgram: createProgram2 }, { extractCliReference: extractCliReference2, renderCliReferenceMarkdown: renderCliReferenceMarkdown2 }] = await Promise.all([
Promise.resolve().then(() => (init_program(), program_exports)),
Promise.resolve().then(() => (init_cli_reference(), cli_reference_exports))
]);
return renderCliReferenceMarkdown2(extractCliReference2(createProgram2()));
}
function guarded(action) {
return async () => {
try {
await action();
} catch (error) {
logError(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
}
};
}
async function runCliReference(opts) {
const repoRoot2 = findRepoRoot(process.cwd());
const outPath = resolvePath(repoRoot2, opts.out);
const markdown = await renderCliReference();
if (opts.check) {
const existing = fs26.existsSync(outPath) ? fs26.readFileSync(outPath, "utf-8") : null;
if (existing === markdown) {
logSuccess(`CLI reference is up to date (${path33.relative(repoRoot2, outPath)})`);
return;
}
throw new Error(
`${existing === null ? "CLI reference is missing" : "CLI reference is stale"}: ${path33.relative(repoRoot2, outPath)}
The commander tree changed. Regenerate and commit:
pnpm exec mesh docs cli-reference`
);
}
ensureParentDir(outPath);
fs26.writeFileSync(outPath, markdown);
logSuccess(`CLI reference \u2192 ${path33.relative(repoRoot2, outPath)}`);
}
async function runPortal(opts) {
const repoRoot2 = findRepoRoot(process.cwd());
await runPortalCommand(
{
out: opts.out,
assembleOnly: opts.assembleOnly,
serve: opts.serve,
port: opts.port,
check: opts.check,
printManifest: opts.printManifest,
diffBase: opts.diffBase,
manifestOut: opts.manifestOut
},
{ repoRoot: repoRoot2, checkCliReference: () => cliReferenceStaleness(repoRoot2) }
);
}
function registerDocsCommand(program2) {
const docs = program2.command("docs").description("Mesh documentation \u2014 the developer portal, the CLI reference, and app API docs");
docs.command("build").description("Generate app docs artifacts (OpenAPI specs, composed specs)").option("-c, --config <path>", "Path to docs config file", "docs/docs.config.json").action(async (opts) => {
try {
await buildDocs(opts.config);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(message);
process.exitCode = 1;
}
});
docs.command("cli-reference").description("Regenerate the `mesh` CLI reference markdown from the command tree").option("-o, --out <path>", "output markdown path", CLI_REFERENCE_PATH).option("--check", "verify only (CI): exit 1 when the committed reference is stale", false).action((opts) => guarded(() => runCliReference(opts))());
docs.command("portal").description(
"Assemble the Mesh developer portal from docs.json-discovered repo docs, then build/serve it with Zudoku"
).option("-o, --out <dir>", "assemble into this content directory and stop (implies --assemble-only)").option("--assemble-only", "write the content tree + generated zudoku.config.ts, then stop", false).option("--serve", "run `zudoku dev` against the assembled content (local preview)", false).option("-p, --port <port>", "port for --serve", "3000").option("--check", "CI gate: schema, reserved names, links, and CLI-reference staleness \u2014 no build", false).option("--print-manifest", "print the publish manifest (every published source path) to stdout", false).option("--diff-base <ref>", "also diff the publish set against a git ref (PR job summary)").option("--manifest-out <path>", "with --check: also write publish-manifest.json to this path").action(
(opts) => guarded(() => runPortal(opts))()
);
docs.command("start").description(
"Serve the Mesh docs locally \u2014 detached in a tmux session by default; the working tree in a mesh-platform checkout, or the published @mesh-tech/docs artifact (role-gated registry) anywhere else"
).option("-v, --version <version>", "docs version to serve (== the @mesh-tech/* baseline it describes); default: latest", void 0).option("-p, --port <port>", "port to serve on (0 picks a free one)", "4400").option("--dev", "in a checkout: run the Zudoku dev server (HMR) in the foreground, for authoring", false).option("--foreground", "serve in the foreground (the agent/CI path; automatic when not a TTY)", false).action(
(opts) => guarded(async () => {
const { runDocsStart: runDocsStart2 } = await Promise.resolve().then(() => (init_start(), start_exports));
await runDocsStart2({
repoRoot: findRepoRoot(process.cwd()),
version: opts.version,
port: opts.port,
dev: opts.dev,
foreground: opts.foreground
});
})()
);
docs.command("stop").description(`Stop the detached docs server (the tmux session "${"mesh-docs"}")`).action(
() => guarded(async () => {
const { runDocsStop: runDocsStop2 } = await Promise.resolve().then(() => (init_start(), start_exports));
runDocsStop2();
})()
);
docs.command("serve-static", { hidden: true }).option("--root <dir>", "built site root", "").option("--port <port>", "port", "4400").action(
(opts) => guarded(async () => {
const { runDocsServeStatic: runDocsServeStatic2 } = await Promise.resolve().then(() => (init_start(), start_exports));
await runDocsServeStatic2(opts);
})()
);
docs.command("list").description("List the published docs versions (each is an @mesh-tech/* baseline)").action(
() => guarded(async () => {
const { runDocsList: runDocsList2 } = await Promise.resolve().then(() => (init_start(), start_exports));
await runDocsList2();
})()
);
}
var CLI_REFERENCE_PATH;
var init_docs = __esm({
"libs/mesh-cli/src/commands/docs.ts"() {
"use strict";
init_log();
init_portal();
init_workflow_fingerprint();
CLI_REFERENCE_PATH = "docs/portal/generated/cli-reference.md";
}
});
// libs/mesh-cli/src/commands/hub/index.ts
import { execFileSync as execFileSync23 } from "node:child_process";
import * as fs27 from "node:fs";
import * as net12 from "node:net";
import * as os9 from "node:os";
import * as path34 from "node:path";
import chalk3 from "chalk";
function devSessionsDir() {
return path34.join(os9.tmpdir(), "mesh-dev-sessions");
}
function listDevSessions(dir = devSessionsDir()) {
if (!fs27.existsSync(dir)) return [];
const sessions = [];
for (const entry of fs27.readdirSync(dir)) {
if (!entry.endsWith(".json")) continue;
try {
const state = JSON.parse(
fs27.readFileSync(path34.join(dir, entry), "utf-8")
);
if (state && typeof state === "object" && state.devOutput) {
sessions.push({ name: entry.slice(0, -".json".length), state });
}
} catch {
}
}
return sessions;
}
function pickDevSession(sessions, opts) {
if (opts.session) {
const match = sessions.find((s) => s.name === opts.session);
if (!match) {
throw new MeshCliError(
`No dev session state found for '${opts.session}'. Known sessions: ${sessions.map((s) => s.name).join(", ") || "(none)"}`,
{ remediation: { command: "mesh dev # from the app repo, to start the stack session" } }
);
}
return match;
}
if (sessions.length === 0) {
throw new MeshCliError(
"No running `mesh dev` session found \u2014 the Hub needs a dev-local stack to observe.",
{ remediation: { command: "mesh dev # from your app repo, then re-run mesh hub dev" } }
);
}
if (opts.cwdAppRoot) {
const root = path34.resolve(opts.cwdAppRoot);
const match = sessions.find((s) => path34.resolve(s.state.appRoot) === root);
if (match) return match;
}
if (sessions.length === 1) return sessions[0];
throw new MeshCliError(
`Multiple dev sessions found and none matches this directory: ${sessions.map((s) => `${s.name} (${s.state.appRoot})`).join(", ")}`,
{ remediation: { command: "mesh hub dev --session <name>" } }
);
}
function parseTmuxEnv(output) {
const env = {};
for (const line of output.split("\n")) {
if (!line || line.startsWith("-")) continue;
const eq = line.indexOf("=");
if (eq <= 0) continue;
env[line.slice(0, eq)] = line.slice(eq + 1);
}
return env;
}
function readTmuxSessionEnv(sessionName) {
try {
const out = execFileSync23("tmux", ["show-environment", "-t", sessionName], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
return parseTmuxEnv(out);
} catch {
return {};
}
}
function tmuxSessionExists(sessionName) {
try {
execFileSync23("tmux", ["has-session", "-t", sessionName], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function forwardedSessionVars(sessionEnv) {
const out = {};
for (const [key, value] of Object.entries(sessionEnv)) {
if (FORWARD_PREFIXES.some((p) => key.startsWith(p))) out[key] = value;
}
return out;
}
function assembleHubEnv(session, sessionEnv, opts) {
const warnings = [];
const notes = [];
const platform = session.state.devOutput.platform;
if (!platform) {
throw new MeshCliError(
`Dev session '${session.name}' has no platform context in its state file \u2014 cannot derive the Hub scope.`,
{ remediation: { command: "mesh dev --kill && mesh dev # relaunch to refresh session state" } }
);
}
const isLocalPlatform = platform.tenant === "local";
if (isLocalPlatform) {
notes.push(
"Local platform session \u2014 the Hub runs from this checkout against `mesh start`. Its containerized Hub keeps :9000, so pass --port/--api-port to run both."
);
}
const hubTenant = platform.name ?? "mesh";
const scopeEnv = platform.env;
const scopeTenants = (opts.tenants ?? platform.tenant).split(",").map((t) => t.trim()).filter(Boolean);
if (scopeTenants.length === 0) {
throw new MeshCliError(
"Tenant scope resolved EMPTY (HUB_SCOPE_ENV set, no HUB_SCOPE_TENANTS) \u2014 the Hub would launch but show zero tenants everywhere.",
{ remediation: { command: "mesh hub dev --tenants <tenant>[,<tenant>\u2026]" } }
);
}
const forwarded = forwardedSessionVars(sessionEnv);
if (!Object.keys(forwarded).some((k) => k.startsWith("AWS_"))) {
warnings.push(
"No AWS_* vars found in the dev session env \u2014 the Hub API will fall back to ambient credentials (SSM reads may 403)."
);
}
if (!forwarded.DEV_USER_ID_TOKEN && !forwarded.DEV_USER_ACCESS_TOKEN) {
warnings.push(
`No DEV_USER_* identity in the dev session env \u2014 the Hub will show an unauthenticated state. Run \`mesh login mesh.${scopeEnv}\` and relaunch \`mesh dev\`.`
);
}
if (!forwarded.DEV_USER_TOKEN_URL) {
notes.push(
"Dev session predates the per-session token-server \u2014 starting a dedicated one so tokens stay fresh past ~1h."
);
}
const temporalAddress = sessionEnv.TEMPORAL_ADDRESS ?? (session.state.devOutput.tunnels["temporal"] ? tunnelClientAddress(session.state.devOutput.tunnels["temporal"]) : (
// The local platform runs Temporal as a published container, so there is
// no tunnel to record and none to demand — the address is fixed.
isLocalPlatform ? "localhost:7233" : void 0
));
if (!temporalAddress) {
throw new MeshCliError(
`Dev session '${session.name}' exposes no Temporal tunnel \u2014 the Hub can't reach the stack's Temporal.`,
{ remediation: { command: "mesh dev # relaunch; the session records tunnels.temporal in its state" } }
);
}
const apiEnv = {
...forwarded,
PORT: String(opts.apiPort),
TEMPORAL_ADDRESS: temporalAddress,
HUB_TENANT: hubTenant,
HUB_SCOPE_ENV: scopeEnv,
HUB_SCOPE_TENANTS: scopeTenants.join(","),
...isLocalPlatform ? localPlatformEnv2(sessionEnv) : {}
};
const uiEnv = {
...forwarded,
PORT: String(opts.uiPort),
API_URL: `http://localhost:${opts.apiPort}`,
HUB_TENANT: hubTenant,
// UI namespace parsing + session defaults land on the scoped tenant.
DEFAULT_ORG: scopeTenants[0],
DEFAULT_ENV: scopeEnv
};
return { apiEnv, uiEnv, scope: { hubTenant, scopeEnv, scopeTenants }, temporalAddress, warnings, notes };
}
function isPlatformRoot(dir) {
return fs27.existsSync(path34.join(dir, "apps", "hub", "api", "package.json")) && fs27.existsSync(path34.join(dir, "apps", "hub", "ui", "package.json"));
}
function resolvePlatformDir(explicit, cwd, env = process.env) {
if (explicit) {
const resolved = path34.resolve(explicit);
if (isPlatformRoot(resolved)) return resolved;
throw new MeshCliError(
`'${resolved}' is not a mesh-platform checkout (apps/hub/{api,ui} not found).`,
{ remediation: { command: "mesh hub dev --platform-dir <path-to-mesh-platform>" } }
);
}
if (env.MESH_PLATFORM_DIR) {
const resolved = path34.resolve(env.MESH_PLATFORM_DIR);
if (isPlatformRoot(resolved)) return resolved;
logWarn(
`MESH_PLATFORM_DIR='${resolved}' is not a mesh-platform checkout (apps/hub/{api,ui} not found) \u2014 ignoring it.`
);
}
let dir = path34.resolve(cwd);
for (; ; ) {
if (isPlatformRoot(dir)) return dir;
const parent = path34.dirname(dir);
if (parent === dir) break;
dir = parent;
}
throw new MeshCliError(
"Can't find a mesh-platform checkout (the Hub's source). Run from inside one, or point at one explicitly.",
{
remediation: {
command: "mesh hub dev --platform-dir <path-to-mesh-platform> # or export MESH_PLATFORM_DIR"
}
}
);
}
async function findFreePort4() {
return new Promise((resolve19, reject) => {
const server = net12.createServer();
server.listen(0, "127.0.0.1", () => {
const address = server.address();
server.close(
() => typeof address === "object" && address ? resolve19(address.port) : reject(new Error("no port"))
);
});
server.on("error", reject);
});
}
function isPortListening2(port) {
return new Promise((resolve19) => {
const socket = net12.connect({ host: "127.0.0.1", port, timeout: 400 });
const done = (ok) => {
socket.removeAllListeners();
socket.destroy();
resolve19(ok);
};
socket.once("connect", () => done(true));
socket.once("timeout", () => done(false));
socket.once("error", () => done(false));
});
}
function hubEnvDir() {
return path34.join(os9.tmpdir(), "mesh-dev-sessions", HUB_SESSION);
}
function parsePort(raw, fallback, source) {
if (raw === void 0) return fallback;
const n = Number(raw);
if (!Number.isInteger(n) || n < 1 || n > 65535) {
throw new MeshCliError(`Invalid port '${raw}' from ${source} \u2014 expected an integer 1\u201365535.`, {
remediation: { command: `mesh hub dev ${source.startsWith("--") ? source : "--port"} <1-65535>` }
});
}
return n;
}
function redactEnv(env) {
const out = {};
for (const [key, value] of Object.entries(env)) {
const sensitive = SENSITIVE_RE.test(key) && !key.endsWith("_URL") && !key.endsWith("_FILE");
out[key] = sensitive ? `<redacted ${value.length} chars>` : value;
}
return out;
}
async function hubDevAction(opts) {
if (opts.kill) {
if (tmuxSessionExists(HUB_SESSION)) {
execFileSync23("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
fs27.rmSync(hubEnvDir(), { recursive: true, force: true });
logSuccess(`Killed Hub session '${HUB_SESSION}'.`);
} else {
logInfo(`No '${HUB_SESSION}' tmux session running.`);
}
return;
}
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");
const apiPort = parsePort(opts.apiPort, DEFAULT_HUB_API_PORT, "--api-port");
const platformDir = resolvePlatformDir(opts.platformDir, process.cwd());
const cwdAppRoot = findAppRoot(process.cwd());
const session = pickDevSession(listDevSessions(), { session: opts.session, cwdAppRoot });
if (!tmuxSessionExists(session.name)) {
throw new MeshCliError(
`Dev session '${session.name}' has a state file but no live tmux session \u2014 its tunnels and token-server are gone.`,
{ remediation: { command: `mesh dev # from ${session.state.appRoot}` } }
);
}
const sessionEnv = readTmuxSessionEnv(session.name);
const assembled = assembleHubEnv(session, sessionEnv, { tenants: opts.tenants, uiPort, apiPort });
if (opts.printEnv) {
console.log(chalk3.bold("\nhub-api env:"));
for (const [k, v] of Object.entries(redactEnv(assembled.apiEnv))) console.log(` ${k}=${v}`);
console.log(chalk3.bold("\nhub-ui env:"));
for (const [k, v] of Object.entries(redactEnv(assembled.uiEnv))) console.log(` ${k}=${v}`);
return;
}
if (tmuxSessionExists(HUB_SESSION)) {
throw new MeshCliError(`A '${HUB_SESSION}' tmux session is already running.`, {
remediation: { command: "mesh hub dev --kill # then re-run mesh hub dev" }
});
}
for (const [label, port, flag] of [
["API", apiPort, "--api-port"],
["UI", uiPort, "--port"]
]) {
if (await isPortListening2(port)) {
throw new MeshCliError(
`Hub ${label} port ${port} is already in use \u2014 another process (a \`mesh start\` Hub, or another \`mesh hub dev\`) is listening there.`,
{ remediation: { command: `mesh hub dev ${flag} <free-port> # or free :${port} first` } }
);
}
}
logInfo(`Dev session : ${session.name} (${session.state.appRoot})`);
logInfo(`Hub source : ${platformDir}`);
logInfo(
`Scope : HUB_TENANT=${assembled.scope.hubTenant} HUB_SCOPE_ENV=${assembled.scope.scopeEnv} HUB_SCOPE_TENANTS=${assembled.scope.scopeTenants.join(",")}`
);
logInfo(`Temporal : ${assembled.temporalAddress}`);
for (const note of assembled.notes) logInfo(note);
for (const warning of assembled.warnings) logWarn(warning);
const apiDir = path34.join(platformDir, "apps", "hub", "api");
const uiDir = path34.join(platformDir, "apps", "hub", "ui");
execFileSync23("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
if (!assembled.apiEnv.DEV_USER_TOKEN_URL && assembled.apiEnv.DEV_USER_ID_TOKEN) {
const platform = session.state.devOutput.platform;
const credContext = `mesh.${platform.env}`;
const tokenPort = await findFreePort4();
const tokenUrl = `http://127.0.0.1:${tokenPort}`;
execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
stdio: "ignore"
});
execFileSync23("tmux", [
"send-keys",
"-t",
`${HUB_SESSION}:token-server`,
`npx mesh dev __token-server ${tokenPort} ${credContext}`,
"Enter"
]);
for (let i = 0; i < 15 && !await isPortListening2(tokenPort); i++) {
await new Promise((r) => setTimeout(r, 200));
}
assembled.apiEnv.DEV_USER_TOKEN_URL = tokenUrl;
assembled.uiEnv.DEV_USER_TOKEN_URL = tokenUrl;
logSuccess(`Dev-user token-server: ${tokenUrl} (context ${credContext})`);
}
const launch = (window, dir, env, cmd, createWindow) => {
const envFile = path34.join(hubEnvDir(), `${window}.env.sh`);
writeEnvFile(envFile, env);
if (createWindow) {
execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
}
execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
stdio: "ignore"
});
execFileSync23("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
};
launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
launch("ui", uiDir, assembled.uiEnv, `pnpm dev --port ${uiPort} --strictPort`, true);
logInfo("Waiting for the Hub to come up\u2026");
const apiUp = await waitForPort("127.0.0.1", apiPort, 9e4);
const uiUp = apiUp && await waitForPort("127.0.0.1", uiPort, 9e4);
if (!apiUp || !uiUp) {
throw new MeshCliError(
`Hub ${apiUp ? "UI" : "API"} did not start listening (api :${apiPort}, ui :${uiPort}).`,
{ remediation: { command: `tmux attach -t ${HUB_SESSION} # inspect the ${apiUp ? "ui" : "api"} window` } }
);
}
console.log("");
logSuccess(`Mesh Hub (dev-local scope ${assembled.scope.scopeTenants.join(",")} @ ${assembled.scope.scopeEnv})`);
console.log(` ${chalk3.bold.cyan(`http://localhost:${uiPort}`)} (also reachable on your tailnet)`);
console.log(` api http://localhost:${apiPort}`);
console.log(` logs tmux attach -t ${HUB_SESSION}`);
console.log(` stop mesh hub dev --kill`);
}
function registerHubCommands(program2) {
const hub = program2.command("hub").description("Local Hub over dev-local stacks");
hub.command("dev").description(
"Launch the current-code Hub (api + ui) against a running `mesh dev` session \u2014 env auto-assembled, zero hand-set vars"
).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("--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);
}
var HUB_SESSION, DEFAULT_HUB_UI_PORT, DEFAULT_HUB_API_PORT, FORWARD_PREFIXES, localPlatformEnv2, SENSITIVE_RE;
var init_hub = __esm({
"libs/mesh-cli/src/commands/hub/index.ts"() {
"use strict";
init_dev_launch();
init_dev();
init_errors();
init_stack();
init_log();
init_pulumi();
HUB_SESSION = "mesh-hub-dev";
DEFAULT_HUB_UI_PORT = Number(DEFAULT_HUB_PORT);
DEFAULT_HUB_API_PORT = 3002;
FORWARD_PREFIXES = ["AWS_", "DEV_USER_"];
localPlatformEnv2 = (sessionEnv) => ({
// The issuer the local `mesh login` tokens carry. Zitadel routes by Host
// header, so the host must stay `localhost` on both sides of the call.
ZITADEL_ISSUER: sessionEnv.ZITADEL_ISSUER ?? "http://localhost:8080",
// Postgres publishes on 5433; the Hub's ops schema lives in the `hub` database
// alongside the per-app ones.
OPS_DB_HOST: "localhost",
OPS_DB_PORT: "5433",
OPS_DB_NAME: "hub",
OPS_DB_USER: "postgres",
DATABASE_PASSWORD: "postgres",
PGSSLMODE: "disable",
DATABASE_URL: "postgres://postgres:postgres@localhost:5433/hub?sslmode=disable",
// The Hub's ADMIN plane. `mesh start` seeds the machine key this names, in the
// platform org with IAM_OWNER, so the Hub can administer each app tenant's own
// Zitadel org rather than only its own.
ZITADEL_OPSHUB_SECRET_NAME: "mesh/local/dev/zitadel/ops-hub",
SPICEDB_ENDPOINT: "localhost:50051",
SPICEDB_HTTP_ENDPOINT: "http://localhost:8443",
SPICEDB_HTTP_SCHEME: "http",
SPICEDB_PRESHARED_KEY: "local-dev-key",
LOKI_URL: "http://localhost:3100",
TEMPO_URL: "http://localhost:3200",
PROMETHEUS_URL: "http://localhost:9090",
TEMPORAL_UI_URL: "http://localhost:8233",
// The stack's namespace. Absent, the Hub's compliance schedules fail to
// register with "Temporal namespace is required".
TEMPORAL_NAMESPACE: "local-dev",
// Dagster discovery records written by `mesh dev --local` carry
// `host.docker.internal` (for the containerized `mesh start` hub). This hub
// runs on the host, where that name does not resolve — tell the Data
// section's resolver to swap it for localhost.
MESH_DAGSTER_LOCAL_HOST: "localhost"
});
SENSITIVE_RE = /TOKEN|SECRET|KEY|PASSWORD/i;
}
});
// libs/mesh-cli/src/commands/registry-publish.ts
import { execFileSync as execFileSync24, execSync } from "child_process";
import * as fs28 from "fs";
import * as path35 from "path";
function repoRoot() {
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
}
function computeSnapshotVersion(root) {
const sha = execSync("git rev-parse --short HEAD", { cwd: root, encoding: "utf-8" }).trim();
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
return `0.0.0-dev.${stamp}.${sha}`;
}
function setPackageVersion(file, version) {
const content = fs28.readFileSync(file, "utf-8");
const next = content.replace(/"version"(\s*:\s*)"[^"]*"/, `"version"$1"${version}"`);
fs28.writeFileSync(file, next);
}
function dirtyPackageJsons(root) {
const out = execSync("git diff --name-only", { cwd: root, encoding: "utf-8" });
return new Set(
out.split("\n").map((s) => s.trim()).filter((s) => s.endsWith("package.json"))
);
}
function registerRegistryPublish(registry) {
registry.command("publish").description(
"Publish an in-development snapshot of @mesh-tech packages to CodeArtifact (dev dist-tag)"
).argument("[context]", 'Platform context for Zitadel auth (e.g. "mesh.dev")').option(
"--snapshot",
"Publish a dev snapshot build (required \u2014 real releases go through changesets/CI)",
false
).option("--tag <tag>", "dist-tag to publish the snapshot under", "dev").option(
"--only <pkgs>",
"Comma-separated substring filter, e.g. --only agent-sdk,app-kit (default: all publishable)"
).option("--dry-run", "Build + resolve versions but do not upload", false).option("--role <arn>", "IAM role ARN for registry access").action(async (context, opts) => {
if (!opts.snapshot) {
logError(
"mesh registry publish currently supports only snapshot (dev) builds.\n\nRe-run with --snapshot. Real releases go through changesets + the\nchangeset-version CI workflow, not this command."
);
process.exit(1);
}
const root = repoRoot();
const awsCreds = resolveCredentials(context, opts.role);
const env = Object.keys(awsCreds).length > 0 ? { ...process.env, ...awsCreds } : void 0;
logInfo("Authenticating with CodeArtifact...");
if (!codeartifactLogin(env)) {
logError("CodeArtifact login failed. Run: mesh registry login <context>");
process.exit(1);
}
const listPath = path35.join(root, "scripts/lib/publishable-packages.json");
const all = JSON.parse(fs28.readFileSync(listPath, "utf-8"));
const patterns = (opts.only ?? "").split(",").map((s) => s.trim()).filter(Boolean);
const selected = patterns.length ? all.filter((p) => patterns.some((pat) => p.dir.includes(pat) || p.name.includes(pat))) : all;
if (selected.length === 0) {
logError(`No publishable package matched --only '${opts.only}'`);
process.exit(1);
}
const version = computeSnapshotVersion(root);
logInfo(`Snapshot version: ${version} (dist-tag '${opts.tag}')`);
const preDirty = dirtyPackageJsons(root);
const backups = [];
const stamp = (dir) => {
const file = path35.join(root, dir, "package.json");
if (!fs28.existsSync(file)) return;
backups.push({ file, content: fs28.readFileSync(file, "utf-8") });
setPackageVersion(file, version);
};
try {
for (const p of selected) {
stamp(p.dir);
for (const sub of p.subPackages ?? []) {
stamp(path35.join(p.dir, typeof sub === "string" ? sub : sub.dir));
}
}
const args = [
"scripts/publish-packages.sh",
"--tag",
opts.tag,
"--skip-latest",
// Pass the TS-resolved DIRS (not the raw patterns) so the shell's
// dir-substring --only filter selects exactly the set we stamped —
// a name-only pattern would otherwise stamp here but be skipped there.
...patterns.length ? ["--only", selected.map((p) => p.dir).join(",")] : [],
...opts.dryRun ? ["--dry-run"] : []
];
logInfo(`Publishing ${selected.length} package(s)...`);
execFileSync24("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
} finally {
for (const b of backups) fs28.writeFileSync(b.file, b.content);
logInfo("Restored source package.json versions.");
const collateral = [...dirtyPackageJsons(root)].filter((f) => !preDirty.has(f));
if (collateral.length) {
try {
execFileSync24("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
logInfo(`Reverted ${collateral.length} package.json file(s) re-vendored by build:publish.`);
} catch {
logError(
`Could not auto-revert build:publish side-effects \u2014 run \`git checkout --\` on:
${collateral.join("\n ")}`
);
}
}
}
logSuccess(`Published snapshot ${version} under dist-tag '${opts.tag}'.`);
console.log(
"\nPin these EXACTLY in the consumer repo (prereleases are never auto-resolved by ^/~):"
);
for (const p of selected) console.log(` "${p.name}": "${version}",`);
console.log(
`
Or always grab the newest dev build:
pnpm add ${selected.map((p) => `${p.name}@${opts.tag}`).join(" ")}`
);
});
}
var init_registry_publish = __esm({
"libs/mesh-cli/src/commands/registry-publish.ts"() {
"use strict";
init_log();
init_registry();
}
});
// libs/mesh-cli/src/commands/registry.ts
var registry_exports = {};
__export(registry_exports, {
CONTEXT_IGNORED_NOTICE: () => CONTEXT_IGNORED_NOTICE,
NOT_AUTHORIZED_RERUN: () => NOT_AUTHORIZED_RERUN,
RegistryNotAuthorizedError: () => RegistryNotAuthorizedError,
applyGrantToUserNpmrc: () => applyGrantToUserNpmrc,
checkExistingToken: () => checkExistingToken,
codeartifactLogin: () => codeartifactLogin,
describeAwsChainFailure: () => describeAwsChainFailure,
describeBrokerFailure: () => describeBrokerFailure,
registerRegistryCommands: () => registerRegistryCommands,
registrySignInAllowed: () => registrySignInAllowed,
renderRegistrySessionLine: () => renderRegistrySessionLine,
repairUnscopedRegistry: () => repairUnscopedRegistry,
resolveCredentials: () => resolveCredentials,
resolvePublisherRoleArn: () => resolvePublisherRoleArn,
runRegistryLogin: () => runRegistryLogin,
runRegistryStatus: () => runRegistryStatus,
shouldRemintAfter: () => shouldRemintAfter,
shouldRepairUserNpmrc: () => shouldRepairUserNpmrc
});
import { execFileSync as execFileSync25 } from "child_process";
import * as fs29 from "fs";
import * as path36 from "path";
function getEndpoint(env) {
try {
const result = execFileSync25("aws", [
"codeartifact",
"get-repository-endpoint",
"--domain",
CA_DOMAIN,
"--repository",
CA_REPOSITORY,
"--format",
"npm",
"--region",
CA_REGION,
"--output",
"text",
"--query",
"repositoryEndpoint"
], {
encoding: "utf-8",
env: env ? { ...process.env, ...env } : void 0,
stdio: ["pipe", "pipe", "pipe"]
});
return result.trim() || null;
} catch {
return null;
}
}
function codeartifactLogin(env) {
try {
execFileSync25("aws", [
"codeartifact",
"login",
"--tool",
"npm",
"--domain",
CA_DOMAIN,
"--repository",
CA_REPOSITORY,
"--namespace",
CA_NAMESPACE,
"--region",
CA_REGION
], {
encoding: "utf-8",
env: env ? { ...process.env, ...env } : void 0,
stdio: ["pipe", "pipe", "pipe"]
});
return true;
} catch {
return false;
}
}
function parseTenantEnv2(context) {
const parts = context.split(".");
if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
return { tenant: parts[0], env: parts[1] };
}
async function getRegistrySsmExport(tenant, env) {
try {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? CA_REGION;
const ssm = new SSMClient5({ region });
const ssmPath = `/mesh-platform/${tenant}/${env}/registry`;
const response = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
if (!response.Parameter?.Value) return null;
return JSON.parse(response.Parameter.Value);
} catch {
return null;
}
}
function resolvePublisherRoleArn({
roleArnFlag,
ssmArn
}) {
const resolved = roleArnFlag || ssmArn;
if (!resolved) {
throw new Error(
"No publisher role: pass --role or ensure the registry stack exported publisherRoleArn."
);
}
return resolved;
}
function assumeRole2(roleArn) {
try {
const result = execFileSync25("aws", [
"sts",
"assume-role",
"--role-arn",
roleArn,
"--role-session-name",
"mesh-registry",
"--output",
"json"
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
const creds = JSON.parse(result).Credentials;
return {
AWS_ACCESS_KEY_ID: creds.AccessKeyId,
AWS_SECRET_ACCESS_KEY: creds.SecretAccessKey,
AWS_SESSION_TOKEN: creds.SessionToken
};
} catch {
return null;
}
}
function assumeRoleWithWebIdentity2(roleArn, idToken, sessionName) {
try {
const result = execFileSync25("aws", [
"sts",
"assume-role-with-web-identity",
"--role-arn",
roleArn,
"--web-identity-token",
idToken,
"--role-session-name",
sessionName,
"--output",
"json"
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
const creds = JSON.parse(result).Credentials;
return {
AWS_ACCESS_KEY_ID: creds.AccessKeyId,
AWS_SECRET_ACCESS_KEY: creds.SecretAccessKey,
AWS_SESSION_TOKEN: creds.SessionToken
};
} catch {
return null;
}
}
function resolveCredentials(context, roleArn) {
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SESSION_TOKEN) {
logInfo("Using existing AWS credentials from environment");
return {};
}
if (context) {
const meshCreds = readCredentials(context);
if (meshCreds && new Date(meshCreds.expiresAt) > /* @__PURE__ */ new Date()) {
const targetRole = roleArn || DEFAULT_REGISTRY_ROLE;
if (targetRole) {
const sessionName = (meshCreds.email ?? "mesh-registry").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
logInfo(`Assuming registry role via Zitadel (${meshCreds.email ?? context})`);
const creds = assumeRoleWithWebIdentity2(targetRole, meshCreds.idToken, sessionName);
if (creds) {
logSuccess("Assumed registry role via Zitadel");
return creds;
}
logWarn("Zitadel JWT role assumption failed \u2014 falling back to AWS profile");
} else {
logInfo(`Zitadel auth available (${meshCreds.email}) but no registry role configured \u2014 using AWS profile`);
}
} else if (meshCreds) {
logWarn(`Zitadel token expired for ${context}. Run: mesh login ${context}`);
} else {
logWarn(`No Zitadel credentials for ${context}. Run: mesh login ${context}`);
}
}
if (roleArn) {
logInfo(`Assuming ${roleArn.split("/").pop()} via AWS SSO`);
const creds = assumeRole2(roleArn);
if (creds) {
logSuccess("Assumed registry role via AWS SSO");
return creds;
}
logWarn("Role assumption failed \u2014 trying current credentials directly");
}
logInfo("Using current AWS profile");
return {};
}
function findProjectRoot2() {
let dir = process.cwd();
while (true) {
if (fs29.existsSync(path36.join(dir, "pnpm-workspace.yaml")) || fs29.existsSync(path36.join(dir, "pnpm-lock.yaml")) || fs29.existsSync(path36.join(dir, "Pulumi.yaml")) || fs29.existsSync(path36.join(dir, ".npmrc"))) {
return dir;
}
const parent = path36.dirname(dir);
if (parent === dir) return null;
dir = parent;
}
}
function ensureProjectNpmrc(endpoint) {
const projectRoot = findProjectRoot2() ?? process.cwd();
const npmrcPath2 = path36.join(projectRoot, ".npmrc");
const scopeLine = `${CA_NAMESPACE}:registry=${endpoint}`;
if (!fs29.existsSync(npmrcPath2)) {
fs29.writeFileSync(npmrcPath2, scopeLine + "\n", "utf-8");
return { created: true, updated: false, path: npmrcPath2 };
}
const content = fs29.readFileSync(npmrcPath2, "utf-8");
const scopeRegex = new RegExp(`^${CA_NAMESPACE.replace("/", "\\/")}:registry=.+$`, "m");
if (scopeRegex.test(content)) {
if (content.includes(scopeLine)) {
return { created: false, updated: false, path: npmrcPath2 };
}
const updated = content.replace(scopeRegex, scopeLine);
fs29.writeFileSync(npmrcPath2, updated, "utf-8");
return { created: false, updated: true, path: npmrcPath2 };
}
const separator = content.endsWith("\n") ? "" : "\n";
fs29.writeFileSync(npmrcPath2, content + separator + scopeLine + "\n", "utf-8");
return { created: false, updated: true, path: npmrcPath2 };
}
function applyGrantToUserNpmrc(grant, npmrcPath2 = homeNpmrcPath()) {
let existing = "";
try {
existing = fs29.readFileSync(npmrcPath2, "utf-8");
} catch {
}
const content = upsertNpmrcLines(existing, [
{ key: `${grant.scope}:registry`, value: grant.endpoint },
{ key: `${npmrcAuthKeyForEndpoint(grant.endpoint)}:_authToken`, value: grant.authorizationToken }
]);
fs29.mkdirSync(path36.dirname(npmrcPath2), { recursive: true });
fs29.writeFileSync(npmrcPath2, content, { mode: 384 });
try {
fs29.chmodSync(npmrcPath2, 384);
} catch {
}
return { path: npmrcPath2 };
}
function describeBrokerFailure(failure) {
switch (failure.kind) {
case "no-broker":
return {
message: "The registry publishes no broker \u2014 using the AWS paths.",
fatal: false
};
case "no-session":
return {
message: "No registry session \u2014 using the AWS paths.",
hint: "mesh registry login",
fatal: false
};
case "unauthenticated":
return {
message: "The registry broker rejected your session (expired or wrong audience).",
hint: "mesh registry logout && mesh registry login",
fatal: false
};
case "not-authorized":
return {
// The whole message lives in the body, not in a `remediation.command`:
// the answer here is "ask a human for a role", which is not a command
// anyone can run, and rendering a URL behind "→ run:" reads as one.
message: `Your Mesh account${failure.email ? ` (${failure.email})` : ""} is not authorized to read ${CA_NAMESPACE} packages.` + (failure.requiredRoles?.length ? `
Needs one of these Zitadel roles: ${failure.requiredRoles.join(", ")}` : "") + (failure.requestUrl ? `
Request access: ${failure.requestUrl}` : "\n Ask a Mesh platform admin to grant your account registry access.") + `
${NOT_AUTHORIZED_RERUN}`,
fatal: true
};
case "unavailable":
return {
message: `Registry broker unavailable (${failure.detail}) \u2014 falling back to the AWS paths.`,
fatal: false
};
}
}
function registrySignInAllowed(opts, hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
return Boolean(opts.device) || hasTty;
}
function shouldRemintAfter(failure) {
return failure.kind === "not-authorized";
}
async function tryBrokerLogin(opts) {
if (opts.broker === false) return { outcome: { kind: "skipped-flag", flag: "--no-broker" } };
if (opts.ci) return { outcome: { kind: "skipped-flag", flag: "--ci" } };
let identity;
try {
identity = await resolveRegistryIdentity();
} catch (err) {
const detail2 = err.message.split("\n")[0] ?? String(err);
logWarn(`Registry broker unavailable (${detail2}) \u2014 falling back to the AWS paths.`);
return { outcome: { kind: "unavailable", detail: detail2 } };
}
logRegistryIdentity(identity);
const session = await ensureRegistrySession({
device: opts.device,
interactive: registrySignInAllowed(opts)
});
if (!session.ok) {
if (session.reason === "login-failed") {
throw new MeshCliError(`Registry sign-in failed: ${session.detail}`, {
remediation: { command: opts.device ? "mesh registry login --device" : "mesh registry login" }
});
}
logWarn(`No registry session (${session.detail}) \u2014 falling back to the AWS paths.`);
return { outcome: { kind: "no-session", detail: session.detail } };
}
logInfo(`Registry session: ${session.email}`);
let result = await requestGrant(session.context, identity);
if (!result.ok && (result.failure.kind === "unauthenticated" || result.failure.kind === "unavailable")) {
let fresh = null;
try {
fresh = await resolveRegistryIdentity({ refresh: true });
} catch {
}
if (fresh && identityChanged(identity, fresh)) {
logInfo(`Registry identity changed since it was cached \u2014 retrying with ${fresh.registryBroker}`);
identity = fresh;
result = await requestGrant(session.context, identity);
}
}
if (!result.ok && shouldRemintAfter(result.failure)) {
const { rolesChanged } = await refreshRegistrySession(session.context);
if (rolesChanged) {
logInfo("Your sign-in now carries a role it did not have \u2014 retrying\u2026");
result = await requestGrant(session.context, identity);
}
}
if (result.ok) return { grant: result.grant };
const { message, hint, fatal } = describeBrokerFailure(result.failure);
if (fatal) {
throw new RegistryNotAuthorizedError(message);
}
logWarn(message);
if (hint) logInfo(` Try: ${hint}`);
const detail = result.failure.kind === "unavailable" ? result.failure.detail : result.failure.kind;
return { outcome: { kind: "unavailable", detail } };
}
async function requestGrant(sessionContext, identity) {
logInfo(`Requesting a registry token from ${identity.registryBroker} (no AWS credentials needed)\u2026`);
return fetchRegistryGrant(sessionContext, identity.registryBroker, {
...realGrantDeps(),
timeoutMs: PUBLISHED_BROKER_TIMEOUT_MS
});
}
function shouldRepairUserNpmrc(opts) {
return !opts.ci;
}
function repairUnscopedRegistry(npmrcPath2 = homeNpmrcPath()) {
let original;
try {
original = fs29.readFileSync(npmrcPath2, "utf-8");
} catch {
return { removed: null };
}
const { content, removed } = stripUnscopedCodeArtifactRegistry(original);
if (!removed) return { removed: null };
const backupPath = `${npmrcPath2}.bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
try {
fs29.writeFileSync(backupPath, original, "utf-8");
fs29.writeFileSync(npmrcPath2, content, "utf-8");
} catch (err) {
return { removed: null, error: err instanceof Error ? err.message : String(err) };
}
return { removed, backupPath };
}
function renderRegistrySessionLine(session, now = Date.now()) {
if (!session) return "No registry session \u2014 run: mesh registry login";
return `Registry session: ${session.email} (expires in ${describeSessionExpiry(session.expiresAt, now)})`;
}
async function checkExistingToken() {
const npmrcPath2 = homeNpmrcPath();
if (!fs29.existsSync(npmrcPath2)) return { valid: false, state: "missing", reason: "no ~/.npmrc" };
const hijackLine = findUnscopedCodeArtifactRegistry(
fs29.readFileSync(npmrcPath2, "utf-8")
) ?? void 0;
const probe = await probeRegistryToken({ npmrcPath: npmrcPath2 });
if (probe.state === "missing") {
return { valid: false, state: probe.state, reason: "no CodeArtifact token in ~/.npmrc", hijackLine };
}
if (probe.state === "expired") {
return {
valid: false,
state: probe.state,
reason: probe.detail ?? "the CodeArtifact token is expired",
hijackLine
};
}
if (probe.state === "unreachable") {
return {
valid: false,
state: probe.state,
reason: `could not reach the registry to verify the token (${probe.detail ?? "unknown error"})`,
hijackLine
};
}
if (hijackLine) {
return { valid: false, state: probe.state, reason: "unscoped CodeArtifact registry", hijackLine };
}
return { valid: true, state: probe.state, endpoint: getEndpoint() ?? void 0 };
}
async function runRegistryStatus(opts) {
const { valid, state, endpoint, reason, hijackLine } = await (opts?.check ?? checkExistingToken)();
if (hijackLine) {
logWarn(
"~/.npmrc sets CodeArtifact as your DEFAULT registry \u2014 every public npm install goes through it and will 401 when the 12h token expires:"
);
logInfo(` ${hijackLine.trim()}`);
logInfo("Fix: mesh registry login # removes it (backup taken) and keeps @mesh-tech scoped");
}
const unverifiable = state === "unreachable" && !hijackLine;
if (!valid && !unverifiable) {
logWarn(`Registry auth is not usable${reason ? ` \u2014 ${reason}` : ""}. Run: mesh registry login`);
return 1;
}
if (unverifiable) {
logWarn(`Could not verify the CodeArtifact token in ~/.npmrc \u2014 ${reason}.`);
logInfo("Nothing to fix from here; retry when the registry is reachable.");
} else {
logSuccess("CodeArtifact token in ~/.npmrc is valid");
if (endpoint) {
logInfo(`Endpoint: ${endpoint}`);
}
}
logInfo(renderRegistrySessionLine(opts?.session ?? readRegistrySession()));
const projectRoot = findProjectRoot2();
if (projectRoot) {
const npmrcPath2 = path36.join(projectRoot, ".npmrc");
if (fs29.existsSync(npmrcPath2)) {
const content = fs29.readFileSync(npmrcPath2, "utf-8");
if (content.includes(`${CA_NAMESPACE}:registry=`)) {
logSuccess(`Project .npmrc configured (${npmrcPath2})`);
} else {
logWarn(`Project .npmrc exists but missing ${CA_NAMESPACE} scope (${npmrcPath2})`);
logInfo("Run: mesh registry login");
}
} else {
logWarn(`No .npmrc in project root (${projectRoot})`);
logInfo("Run: mesh registry login");
}
}
return 0;
}
function ensureSsoProfile(profile) {
const awsConfig = readAwsConfig();
if (awsConfig && !awsConfigHasProfile(awsConfig, profile)) {
throw new MeshCliError(
`AWS profile '${profile}' is not defined in ~/.aws/config \u2014 \`aws sso login --profile ${profile}\` cannot work until it exists.
If you only need to INSTALL @mesh-tech packages, drop --profile: \`mesh registry login\` uses the
Zitadel-gated broker and needs no AWS account at all.
If you DEPLOY and genuinely need this profile \u2014 one-time setup: \`aws configure sso --profile ${profile}\`,
or add the session + profile by hand:
${SSO_SESSION_SNIPPET}
[profile ${profile}]
sso_session = mesh
sso_account_id = <account id>
sso_role_name = <role>
region = us-east-2`,
{ remediation: { command: `aws configure sso --profile ${profile}` } }
);
}
const probe = () => execFileSync25("aws", ["sts", "get-caller-identity", "--profile", profile], {
stdio: ["ignore", "pipe", "pipe"],
timeout: 2e4
});
try {
probe();
logInfo(`AWS SSO session for profile '${profile}' is live`);
return;
} catch {
logInfo(`AWS SSO session for profile '${profile}' is stale \u2014 opening browser login\u2026`);
}
execFileSync25("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
try {
probe();
} catch (err) {
throw new MeshCliError(
`AWS SSO login for profile '${profile}' completed but the session still fails an identity check.`,
{
remediation: { command: `aws sts get-caller-identity --profile ${profile} # see the underlying error` },
cause: err
}
);
}
logSuccess(`AWS SSO session refreshed (profile '${profile}')`);
}
async function runRegistryLogin(context, opts) {
let roleArn = opts.role;
if (opts.publish) {
let ssmArn;
if (!opts.role) {
if (!context) {
throw new MeshCliError(
'`--publish` requires a platform context, e.g. "mesh registry login mesh.dev --publish".'
);
}
const parsed = parseTenantEnv2(context);
if (!parsed) {
throw new MeshCliError(
`Could not parse tenant/env from context "${context}" (expected "<tenant>.<env>").`
);
}
const registryExport = await getRegistrySsmExport(parsed.tenant, parsed.env);
ssmArn = registryExport?.publisherRoleArn;
}
try {
roleArn = resolvePublisherRoleArn({ roleArnFlag: opts.role, ssmArn });
} catch (err) {
throw new MeshCliError(err.message);
}
logInfo(`Publisher role resolved: ${roleArn}`);
}
let outcome;
if (opts.publish) {
outcome = { kind: "skipped-flag", flag: "--publish" };
} else if (opts.profile) {
outcome = { kind: "skipped-profile", profile: opts.profile };
} else {
if (context && opts.broker !== false && !opts.ci) logInfo(CONTEXT_IGNORED_NOTICE);
const attempt = await tryBrokerLogin(opts);
if ("grant" in attempt) {
const { path: npmrcPath2 } = applyGrantToUserNpmrc(attempt.grant);
logSuccess(
`Registry token issued by the Mesh broker \u2192 ${npmrcPath2}` + (attempt.grant.expiresAt ? ` (expires ${attempt.grant.expiresAt})` : "")
);
if (shouldRepairUserNpmrc(opts)) {
const repair = repairUnscopedRegistry();
if (repair.removed) {
logWarn(
"Removed an unscoped CodeArtifact `registry=` from ~/.npmrc \u2014 it routed ALL npm traffic (public packages included) through CodeArtifact."
);
logInfo(` removed: ${repair.removed.trim()}`);
if (repair.backupPath) logInfo(` backup: ${repair.backupPath}`);
}
}
if (!opts.ci && !opts.skipNpmrc) {
const result = ensureProjectNpmrc(attempt.grant.endpoint);
if (result.created) {
logSuccess(`Created ${result.path} with ${CA_NAMESPACE} registry scope`);
} else if (result.updated) {
logSuccess(`Updated ${CA_NAMESPACE} registry in ${result.path}`);
} else {
logInfo(`${result.path} already configured`);
}
}
logSuccess(`Ready \u2014 run pnpm install to fetch ${CA_NAMESPACE} packages`);
return;
}
outcome = attempt.outcome;
}
let env;
if (opts.profile) {
ensureSsoProfile(opts.profile);
env = { ...process.env, AWS_PROFILE: opts.profile };
for (const key of ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]) {
delete env[key];
}
} else {
const awsCreds = resolveCredentials(context, roleArn);
env = Object.keys(awsCreds).length > 0 ? { ...process.env, ...awsCreds } : void 0;
}
logInfo("Authenticating with CodeArtifact...");
if (!codeartifactLogin(env)) {
throw new MeshCliError(describeAwsChainFailure(outcome));
}
logSuccess("CodeArtifact token refreshed (valid for 12 hours)");
if (shouldRepairUserNpmrc(opts)) {
const repair = repairUnscopedRegistry();
if (repair.removed) {
logWarn(
`Removed an unscoped CodeArtifact \`registry=\` from ~/.npmrc \u2014 it routed ALL npm traffic (public packages included) through CodeArtifact, so every install broke when the 12h token expired. ${CA_NAMESPACE} stays scoped; public packages now go to npmjs.`
);
logInfo(` removed: ${repair.removed.trim()}`);
if (repair.backupPath) logInfo(` backup: ${repair.backupPath}`);
} else if (repair.error) {
logWarn(`Could not repair ~/.npmrc (${repair.error}) \u2014 remove any unscoped \`registry=\` line by hand.`);
}
}
const endpoint = getEndpoint(env);
if (!endpoint) {
logWarn("Could not retrieve registry endpoint \u2014 .npmrc scope not configured");
logInfo("You may need to manually add to .npmrc:");
logInfo(` ${CA_NAMESPACE}:registry=https://mesh-platform-<ACCOUNT>.d.codeartifact.${CA_REGION}.amazonaws.com/npm/${CA_REPOSITORY}/`);
return;
}
if (!opts.ci && !opts.skipNpmrc) {
const result = ensureProjectNpmrc(endpoint);
if (result.created) {
logSuccess(`Created ${result.path} with ${CA_NAMESPACE} registry scope`);
} else if (result.updated) {
logSuccess(`Updated ${CA_NAMESPACE} registry in ${result.path}`);
} else {
logInfo(`${result.path} already configured`);
}
}
logSuccess(`Ready \u2014 run pnpm install to fetch ${CA_NAMESPACE} packages`);
}
function describeAwsChainFailure(outcome) {
switch (outcome.kind) {
case "skipped-profile":
return `AWS profile '${outcome.profile}' cannot read the Mesh registry (aws codeartifact login failed).
--profile is for people who DEPLOY with an AWS account that is allowed to read the registry
(Trabian's mesh-dev profile, or a tenant deployer role). Any other profile fails here.
To install packages you do not need AWS at all \u2014 drop the flag:
mesh registry login`;
case "unavailable":
return `Registry broker unavailable (${outcome.detail}) and this machine has no AWS path either.
Retry in a minute: mesh registry login
If you deploy and have an AWS account: mesh registry login --profile mesh-dev`;
case "no-session":
return `No registry session on this machine (${outcome.detail}) and the AWS chain failed too.
Sign in once \u2014 on a headless box: mesh registry login --device
In CI with AWS creds: mesh registry login --ci`;
case "skipped-flag":
return `CodeArtifact login failed with the AWS credential chain (${outcome.flag} skips the registry broker).
This path needs AWS credentials that can read the registry \u2014 ambient keys, a live SSO
session, or --profile <deployer-profile>.
To install packages you do not need AWS at all \u2014 drop the flag:
mesh registry login`;
}
}
function registerRegistryCommands(program2) {
const registry = program2.command("registry").description("Manage npm registry auth for @mesh-tech packages");
registry.command("login").description(
"Sign in to the Mesh package registry (browser, or device code with --device) and configure ~/.npmrc + the project .npmrc. Needs no platform context and no AWS account."
).argument(
"[context]",
"Platform context \u2014 only meaningful with --publish (publisher role from that context's SSM export) or on the explicit AWS chain; ignored on the default read path, which is global"
).option("--device", "Sign in with the device-code flow (headless box, SSH, no browser)").option("--role <arn>", "IAM role ARN for registry access").option("--ci", "CI mode \u2014 AWS chain only (ambient credentials), refresh token only, skip project .npmrc setup").option("--skip-npmrc", "Skip project .npmrc setup").option("--profile <name>", "AWS SSO profile of an account allowed to READ the registry (deployers only \u2014 e.g. mesh-dev); skips the broker, auto-runs `aws sso login` when the session is stale").option(
"--no-broker",
"Skip the Zitadel-gated registry broker and go straight to the AWS credential chain"
).option(
"--publish",
"Assume the registry's publisher role for CodeArtifact publish access (opt-in privileged path \u2014 resolves publisherRoleArn from the registry's SSM export unless --role is given)"
).action(async (context, opts) => {
await runRegistryLogin(context, opts);
});
registry.command("logout").description("Forget the package-registry session (platform logins under `mesh login` are untouched)").action(() => {
clearRegistrySession();
logSuccess("Registry session cleared. ~/.npmrc is untouched \u2014 its token expires on its own within 12 hours.");
});
registry.command("status").description("Check current registry authentication status \u2014 the ~/.npmrc token and the registry session").action(async () => {
const code = await runRegistryStatus();
if (code !== 0) process.exit(code);
});
registerRegistryPublish(registry);
}
var CA_DOMAIN, CA_REPOSITORY, CA_NAMESPACE, CA_REGION, DEFAULT_REGISTRY_ROLE, RegistryNotAuthorizedError, NOT_AUTHORIZED_RERUN, CONTEXT_IGNORED_NOTICE;
var init_registry = __esm({
"libs/mesh-cli/src/commands/registry.ts"() {
"use strict";
init_log();
init_errors();
init_auth_preflight();
init_registry_broker();
init_registry_identity();
init_login();
init_registry_publish();
CA_DOMAIN = "mesh-platform";
CA_REPOSITORY = "mesh-packages";
CA_NAMESPACE = "@mesh-tech";
CA_REGION = "us-east-2";
DEFAULT_REGISTRY_ROLE = null;
RegistryNotAuthorizedError = class extends MeshCliError {
constructor(message) {
super(message);
this.name = "RegistryNotAuthorizedError";
}
};
NOT_AUTHORIZED_RERUN = "Once granted, run: mesh registry login (it re-mints your sign-in; still refused? mesh registry logout && mesh registry login)";
CONTEXT_IGNORED_NOTICE = "The registry is global \u2014 the context argument is no longer needed (ignored).";
}
});
// libs/mesh-cli/src/commands/init/wizard.ts
var wizard_exports = {};
__export(wizard_exports, {
INIT_REPO_FIX: () => INIT_REPO_FIX,
INTERRUPTED_EXIT_CODE: () => INTERRUPTED_EXIT_CODE,
NO_SESSION_NO_TTY_MESSAGE: () => NO_SESSION_NO_TTY_MESSAGE,
TENANT_REQUIRED_MESSAGE: () => TENANT_REQUIRED_MESSAGE,
WIZARD_STEPS: () => WIZARD_STEPS,
classifyRepo: () => classifyRepo,
describeMode: () => describeMode,
registryStep: () => registryStep,
renderWizardSummary: () => renderWizardSummary,
repoStep: () => repoStep,
resolveAnswers: () => resolveAnswers,
runInitWizard: () => runInitWizard,
runWizardSteps: () => runWizardSteps,
stepBlocks: () => stepBlocks,
wizardExitCode: () => wizardExitCode,
wizardNextCommands: () => wizardNextCommands
});
import * as fs30 from "fs";
import * as path37 from "path";
import { execFileSync as execFileSync26 } from "child_process";
import chalk4 from "chalk";
function wizardExitCode(steps) {
return steps.some((s) => s.status === "fail") ? 1 : 0;
}
function wizardNextCommands(mode, steps = []) {
const repo = steps.find((s) => s.name === STEP_LABELS.repo);
const first = repo && repo.status !== "pass" && repo.fix ? repo.fix : "mesh create-app";
return mode === "local" ? [first] : [first, "mesh deploy up # from the app directory, once it exists"];
}
function stepBlocks(result) {
return result.status === "fail" && result.blocking !== false;
}
function describeMode(mode) {
return mode === "local" ? "local only" : `deployed platform (${mode.platform})`;
}
function renderWizardSummary(outcome, opts = {}) {
const paint = opts.color === false ? { green: (s) => s, red: (s) => s, yellow: (s) => s, dim: (s) => s, cyan: (s) => s } : chalk4;
const icon = (status) => status === "pass" ? paint.green("\u2714") : status === "fail" ? paint.red("\u2718") : status === "warn" ? paint.yellow("\u25B2") : paint.dim("\u2013");
const lines = [`Setup summary \u2014 tenant ${outcome.tenant} (${describeMode(outcome.mode)})`];
for (const step of outcome.steps) {
lines.push(` ${icon(step.status)} ${step.name.padEnd(18)} ${step.detail}`);
if (step.fix && step.status !== "pass") lines.push(` ${paint.dim("\u2192 run:")} ${paint.cyan(step.fix)}`);
}
lines.push("", "Next:");
for (const cmd of outcome.next) lines.push(` ${cmd}`);
return lines.join("\n");
}
function resolveAnswers(opts, recorded, interactive) {
const open = [];
let tenant = opts.tenant;
if (tenant !== void 0 && !isValidTenantName(tenant)) {
throw new MeshCliError(`Invalid tenant name '${tenant}' (lowercase alphanumeric + dashes, starting with a letter).`);
}
if (!tenant && (opts.yes || !interactive) && recorded) tenant = recorded.tenant;
if (!tenant) {
if (!interactive) throw new MeshCliError(TENANT_REQUIRED_MESSAGE);
open.push("tenant");
}
let mode;
if (opts.local && opts.platform) {
throw new MeshCliError("--local and --platform are mutually exclusive.");
}
if (opts.platform) mode = { platform: opts.platform };
else if (opts.local) mode = "local";
else if (opts.yes || !interactive) {
mode = recorded && recorded.platform !== "local" ? { platform: recorded.platform } : "local";
} else {
open.push("mode");
}
return { tenant, mode, open };
}
function classifyRepo(cwd) {
if (isInsidePlatformMonorepo(cwd)) return { kind: "platform-monorepo", root: resolveTargetRoot(cwd) };
if (shouldBootstrapAppsRepo(cwd)) return { kind: "empty-apps-repo", root: cwd };
const root = resolveTargetRoot(cwd);
const inRepo = fs30.existsSync(path37.join(root, ".git"));
if (!inRepo) {
const hasContent = fs30.readdirSync(cwd).some((entry) => !entry.startsWith("."));
return { kind: hasContent ? "no-repo" : "empty-dir", root: cwd };
}
const hasWorkspace = fs30.existsSync(path37.join(root, "pnpm-workspace.yaml")) || fs30.existsSync(path37.join(root, "pnpm-lock.yaml"));
const hasApps = fs30.existsSync(path37.join(root, "apps")) || fs30.existsSync(path37.join(root, "tenants"));
if (hasWorkspace || hasApps) return { kind: "apps-repo", root };
return { kind: "other-repo", root };
}
function gitInit(dir) {
execFileSync26("git", ["init", "-q"], { cwd: dir, stdio: ["ignore", "ignore", "pipe"] });
}
async function runWizardSteps(ctx, steps) {
const results = [];
for (const step of steps) {
const result = await step.run(ctx);
results.push(result);
if (stepBlocks(result)) break;
}
return results;
}
async function inquirerPrompts() {
const [{ default: input2 }, { default: select }, { default: confirm }] = await Promise.all([
import("@inquirer/input"),
import("@inquirer/select"),
import("@inquirer/confirm")
]);
return {
input: (args) => input2(args),
select: (args) => select(args),
confirm: (args) => confirm(args)
};
}
function isInterrupt(err) {
return err instanceof Error && err.name === "ExitPromptError";
}
async function runInitWizard(opts, deps = {}) {
const cwd = deps.cwd ?? process.cwd();
const interactive = deps.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
const remote = deps.remote ?? isRemoteEnvironment();
const recorded = findMeshJson(cwd)?.data ?? null;
const origLog = console.log;
if (opts.json) console.log = (...a) => console.error(...a);
try {
const answers = resolveAnswers(opts, recorded, interactive);
const prompts = deps.prompts ?? (answers.open.length ? await inquirerPrompts() : {});
let tenant = answers.tenant;
if (!tenant) {
const detected = recorded?.tenant ?? (() => {
try {
const t = detectLocalTenant(cwd);
return t === "local" ? void 0 : t;
} catch {
return void 0;
}
})();
tenant = await prompts.input({
message: "Tenant name",
default: detected,
validate: (v) => isValidTenantName(v) ? true : "lowercase letters, digits and dashes, starting with a letter"
});
}
let mode = answers.mode;
if (!mode) {
const choice = await prompts.select({
message: "Where does this tenant run?",
choices: [
{ name: "Local only (mesh start / mesh dev \u2014 no deployed platform yet)", value: "local" },
{ name: `A deployed Mesh platform (I have a ${tenant}.<env> context)`, value: "deployed" }
],
default: recorded && recorded.platform !== "local" ? "deployed" : "local"
});
if (choice === "local") mode = "local";
else {
const env = await prompts.input({
message: "Environment",
default: recorded && recorded.platform !== "local" ? recorded.platform : "dev",
validate: (v) => /^[a-z][a-z0-9-]*$/.test(v) ? true : "lowercase letters, digits and dashes"
});
mode = { platform: env };
}
}
const ctx = {
cwd,
tenant,
mode,
context: mode === "local" ? null : `${tenant}.${mode.platform}`,
device: opts.device,
profile: opts.profile,
interactive,
remote,
yes: Boolean(opts.yes),
skipRepo: Boolean(opts.skipRepo),
prompts,
seams: deps.seams
};
logInfo(`Setting up tenant '${tenant}' (${describeMode(mode)})\u2026`);
const steps = await runWizardSteps(ctx, deps.steps ?? WIZARD_STEPS);
const { kind, root } = classifyRepo(cwd);
if (!opts.skipRepo && (kind === "apps-repo" || kind === "empty-apps-repo")) {
const written = writeMeshJson(root, { tenant, platform: mode === "local" ? "local" : mode.platform });
logInfo(`Recorded tenant + mode in ${written}`);
}
const outcome = { tenant, mode, steps, next: wizardNextCommands(mode, steps) };
const code = wizardExitCode(steps);
if (opts.json) {
console.log = origLog;
emitJsonPayload({ ok: code === 0, ...outcome });
} else {
console.log("");
console.log(renderWizardSummary(outcome));
console.log("");
}
return code;
} catch (err) {
if (isInterrupt(err)) {
console.error("");
logWarn("Interrupted \u2014 nothing was written.");
return INTERRUPTED_EXIT_CODE;
}
throw err;
} finally {
console.log = origLog;
}
}
var NO_SESSION_NO_TTY_MESSAGE, TENANT_REQUIRED_MESSAGE, INTERRUPTED_EXIT_CODE, STEP_LABELS, registryStep, platformStep, INIT_REPO_FIX, repoStep, skillsStep, WIZARD_STEPS;
var init_wizard = __esm({
"libs/mesh-cli/src/commands/init/wizard.ts"() {
"use strict";
init_log();
init_errors();
init_auth_preflight();
init_registry();
init_registry_identity();
init_mesh_json();
init_login();
init_create_app();
init_skills();
init_dev_local();
NO_SESSION_NO_TTY_MESSAGE = "No registry session and no terminal to sign in with.\n On a headless box: mesh registry login --device (one-time, prints a code)\n In CI with AWS creds: mesh registry login --ci";
TENANT_REQUIRED_MESSAGE = "--tenant is required (no TTY for interactive mode)";
INTERRUPTED_EXIT_CODE = 130;
STEP_LABELS = {
registry: "Registry access",
platform: "Platform sign-in",
repo: "Repo",
skills: "Agent skills"
};
registryStep = {
name: "registry",
async run(ctx) {
const name = STEP_LABELS.registry;
const probe = await (ctx.seams?.probe ?? probeRegistryToken)();
if (probe.state === "fresh") {
const session2 = readRegistrySession();
return { name, status: "pass", detail: `Registry access OK (${session2 ? `signed in as ${session2.email}` : "token valid"})` };
}
if (probe.state === "unreachable") {
return {
name,
status: "warn",
detail: `could not reach the registry to verify access (${probe.detail ?? "unknown error"})`,
fix: "mesh registry login"
};
}
if (!ctx.interactive && !ctx.device && !ctx.profile) {
logWarn(NO_SESSION_NO_TTY_MESSAGE);
return { name, status: "fail", detail: NO_SESSION_NO_TTY_MESSAGE.split("\n")[0], fix: "mesh registry login --device", blocking: true };
}
try {
const login2 = ctx.seams?.runRegistryLogin ?? (async (opts) => {
const { runRegistryLogin: runRegistryLogin2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
await runRegistryLogin2(void 0, opts);
});
await login2({ device: ctx.device, profile: ctx.profile });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (err instanceof RegistryNotAuthorizedError) {
return {
name,
status: "fail",
detail: `not authorized \u2014 ${message.split("\n")[0]}`,
fix: "ask a Mesh platform admin for the registry:read role, then: mesh registry login",
blocking: false
};
}
return {
name,
status: "fail",
detail: message.split("\n")[0],
fix: ctx.device || ctx.remote ? "mesh registry login --device" : "mesh registry login",
blocking: true
};
}
const session = readRegistrySession();
return { name, status: "pass", detail: `Registry access OK (${session ? `signed in as ${session.email}` : "token written"})` };
}
};
platformStep = {
name: "platform",
async run(ctx) {
const name = STEP_LABELS.platform;
if (!ctx.context) return { name, status: "skip", detail: "local only \u2014 no platform to sign in to" };
const context = ctx.context;
const cached = readCredentials(context);
if (cached && tokenStillValid(cached.expiresAt)) {
return { name, status: "pass", detail: `signed in to ${context}${cached.email ? ` as ${cached.email}` : ""}` };
}
const config = await discoverConfigForContext(context);
if (!config) {
logWarn(renderNoConfigHelp(context));
return { name, status: "fail", detail: `no configuration found for ${context} (see above)`, fix: `mesh login ${context}` };
}
if (!ctx.interactive && !ctx.device) {
return { name, status: "fail", detail: `no session for ${context} and no terminal to sign in with`, fix: `mesh login ${context} --device` };
}
try {
await runLoginFlow(context, config, { device: ctx.device });
} catch (err) {
return { name, status: "fail", detail: `sign-in failed: ${err.message}`, fix: `mesh login ${context}${ctx.device ? " --device" : ""}` };
}
const creds = readCredentials(context);
return {
name,
status: "pass",
detail: `signed in to ${context}${creds?.email ? ` as ${creds.email}` : ""} \u2014 tenant registration: mesh init app-tenant --context ${context} --hub-url <hub api url>`
};
}
};
INIT_REPO_FIX = "git init && mesh init";
repoStep = {
name: "repo",
async run(ctx) {
const name = STEP_LABELS.repo;
if (ctx.skipRepo) return { name, status: "skip", detail: "--skip-repo" };
const { kind, root } = classifyRepo(ctx.cwd);
switch (kind) {
case "platform-monorepo":
return { name, status: "skip", detail: "inside mesh-platform \u2014 apps here link workspace packages; nothing to set up" };
case "empty-dir": {
const go = ctx.yes || !ctx.interactive ? true : await ctx.prompts.confirm({
message: `This folder is not a git repo \u2014 initialize ${root} as your ${ctx.tenant}-mesh-apps repo here?`,
default: true
});
if (!go) {
return { name, status: "skip", detail: "folder left as is", fix: `cd <your ${ctx.tenant}-mesh-apps clone> && mesh init` };
}
try {
(ctx.seams?.gitInit ?? gitInit)(root);
} catch (err) {
const why = err instanceof Error && "stderr" in err && err.stderr ? String(err.stderr).trim() : "";
return {
name,
status: "fail",
detail: `git init failed \u2014 is git installed and ${root} writable?${why ? ` (${why})` : ""}`,
fix: INIT_REPO_FIX
};
}
const created = bootstrapAppsRepo(root, ctx.tenant);
return { name, status: "pass", detail: `initialized a git repo and bootstrapped the apps repo: ${created.join(", ")}` };
}
case "no-repo":
return {
name,
status: "skip",
detail: `not in a git repo, and this folder already holds files \u2014 clone (or git init) your ${ctx.tenant}-mesh-apps repo and re-run mesh init there`,
fix: INIT_REPO_FIX
};
case "other-repo":
return {
name,
status: "warn",
detail: "this repo is not a Mesh apps repo (no pnpm workspace, no apps/)",
fix: "mesh create-app # scaffolds an app and the workspace files around it"
};
case "empty-apps-repo": {
const go = ctx.yes || !ctx.interactive ? true : await ctx.prompts.confirm({ message: `Empty repo \u2014 bootstrap the ${ctx.tenant}-mesh-apps layout here?`, default: true });
if (!go) return { name, status: "skip", detail: "empty repo left as is", fix: "mesh create-app" };
const created = bootstrapAppsRepo(root, ctx.tenant);
return { name, status: "pass", detail: `bootstrapped apps repo: ${created.join(", ")}` };
}
case "apps-repo": {
const missing = [];
if (!fs30.existsSync(path37.join(root, "package.json"))) missing.push("package.json");
if (!fs30.existsSync(path37.join(root, "pnpm-workspace.yaml")) && !fs30.existsSync(path37.join(root, "pnpm-lock.yaml"))) {
missing.push("pnpm-workspace.yaml");
}
if (missing.length) {
return { name, status: "warn", detail: `apps repo missing ${missing.join(", ")}`, fix: "mesh create-app # writes the workspace files" };
}
return { name, status: "pass", detail: `apps repo at ${root}` };
}
}
}
};
skillsStep = {
name: "skills",
async run(ctx) {
const name = STEP_LABELS.skills;
if (ctx.skipRepo) return { name, status: "skip", detail: "--skip-repo" };
const { kind, root } = classifyRepo(ctx.cwd);
if (kind !== "apps-repo" && kind !== "empty-apps-repo") {
return { name, status: "skip", detail: "no apps repo to install skills into" };
}
try {
const complete = syncSkills(root);
return complete ? { name, status: "pass", detail: "base skills + Intent discovery in sync" } : { name, status: "pass", detail: "base skills synced \u2014 after pnpm install, run: mesh skills sync (platform skills)" };
} catch (err) {
return { name, status: "fail", detail: `skills sync failed: ${err.message}`, fix: "mesh skills sync", blocking: false };
}
}
};
WIZARD_STEPS = [registryStep, platformStep, repoStep, skillsStep];
}
});
// libs/mesh-cli/src/commands/init.ts
import chalk5 from "chalk";
import * as fs31 from "fs";
import * as path38 from "path";
function isLocalContext(context) {
return context === LOGIN_CONTEXT;
}
async function probeHubTenants(hubUrl, accessToken) {
try {
const response = await fetch(`${hubUrl}/tenants`, {
headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
signal: AbortSignal.timeout(5e3)
});
if (response.status === 401) return { ok: false, reason: "unauthenticated against the Hub API" };
if (!response.ok) return { ok: false, reason: `Hub API returned ${response.status}` };
return { ok: true, tenants: await response.json() };
} catch {
return { ok: false, reason: `Hub API unreachable at ${hubUrl}` };
}
}
function readHomeNpmrc() {
try {
return fs31.readFileSync(homeNpmrcPath(), "utf-8");
} catch {
return "";
}
}
function registryFixCommand(profile) {
return profile ? `mesh registry login --profile ${profile}` : "mesh registry login";
}
async function registryChecks(args) {
const { fix, profile } = args;
const probe = args.probe ?? probeRegistryToken;
const login2 = args.login ?? (async () => {
const { runRegistryLogin: runRegistryLogin2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
await runRegistryLogin2(void 0, profile ? { profile } : {});
});
const REGISTRY_FIX = registryFixCommand(profile);
let tokenState = await probe({ npmrcPath: homeNpmrcPath() });
let hijack = findUnscopedCodeArtifactRegistry(readHomeNpmrc());
let repaired = false;
let loginError = null;
if (fix && (tokenState.state === "expired" || tokenState.state === "missing" || hijack)) {
try {
await login2();
repaired = true;
tokenState = await probe({ npmrcPath: homeNpmrcPath() });
hijack = findUnscopedCodeArtifactRegistry(readHomeNpmrc());
} catch (err) {
loginError = err instanceof Error ? err.message : String(err);
}
}
const suffix = repaired ? " (--fix)" : "";
const checks = [];
if (loginError) {
checks.push({
name: "Registry access",
status: "fail",
detail: `token refresh failed: ${loginError}`,
fix: REGISTRY_FIX
});
} else if (tokenState.state === "fresh") {
checks.push({
name: "Registry access",
status: "pass",
detail: `CodeArtifact token in ~/.npmrc is accepted by the registry${suffix}`
});
} else if (tokenState.state === "unreachable") {
checks.push({
name: "Registry access",
status: "warn",
detail: `could not reach the registry to verify the token (${tokenState.detail ?? "unknown error"})`,
fix: REGISTRY_FIX
});
} else {
checks.push({
name: "Registry access",
status: "fail",
detail: tokenState.state === "expired" ? `the CodeArtifact token in ~/.npmrc is expired (${tokenState.detail ?? "rejected by the registry"})` : "no CodeArtifact token in ~/.npmrc",
fix: REGISTRY_FIX
});
}
checks.push(
hijack ? {
name: "Public npm not hijacked",
status: "fail",
detail: `~/.npmrc makes CodeArtifact the DEFAULT registry (${hijack.trim()}) \u2014 every public package resolves through it and 401s when the 12h token expires`,
fix: REGISTRY_FIX
} : {
name: "Public npm not hijacked",
status: "pass",
detail: `no unscoped CodeArtifact registry= in ~/.npmrc${repaired ? " (--fix removed it)" : ""}`
}
);
return checks;
}
async function runChecks2(args) {
const { root, tenant, context, hubUrl, fix, profile } = args;
const checks = [];
const local = isLocalContext(context);
const creds = readCredentials(context);
const expired = creds ? new Date(creds.expiresAt).getTime() < Date.now() : true;
checks.push(
creds && !expired ? { name: "CLI auth", status: "pass", detail: `logged in (${context}${creds.email ? `, ${creds.email}` : ""})` } : {
name: "CLI auth",
status: "fail",
detail: creds ? `credentials for '${context}' are expired` : `no credentials for context '${context}'`,
fix: `mesh login ${context}`
}
);
const accessToken = creds && !expired ? creds.accessToken : null;
if (local) {
checks.push({ name: "Registry access", status: "pass", detail: "local context \u2014 no registry required" });
checks.push({ name: "Public npm not hijacked", status: "pass", detail: "local context \u2014 no registry required" });
} else {
checks.push(...await registryChecks({ fix, profile }));
}
const hub = await probeHubTenants(hubUrl, accessToken);
if (!hub.ok) {
const fixCmd = local ? "mesh start" : `mesh login ${context}`;
checks.push({ name: "Platform reachable", status: "fail", detail: hub.reason, fix: fixCmd });
checks.push({
name: `App tenant '${tenant}'`,
status: "warn",
detail: "not verifiable while the platform registry is unreachable",
fix: fixCmd
});
} else {
checks.push({
name: "Platform reachable",
status: "pass",
detail: `Hub API at ${hubUrl} (${hub.tenants.length} tenant(s) registered)`
});
const registered = hub.tenants.some((t) => t.name === tenant);
checks.push(
registered ? { name: `App tenant '${tenant}'`, status: "pass", detail: "registered in the platform registry" } : {
name: `App tenant '${tenant}'`,
status: local ? "warn" : "fail",
detail: local ? "not registered yet \u2014 auto-created on the first `mesh dev` run of an app in this repo" : "not registered \u2014 register it on the platform stack and deploy",
fix: local ? "mesh dev" : `mesh tenant add ${tenant} # in the platform repo, then: mesh deploy up`
}
);
}
if (local) {
checks.push({ name: "Deployer role", status: "pass", detail: "local context \u2014 no deployer role required" });
} else {
const stackFiles = fs31.existsSync(root) ? fs31.readdirSync(root).filter((f) => /^Pulumi\..*\.yaml$/.test(f)) : [];
const hasRole = stackFiles.some(
(f) => /mesh:deployerRole/.test(fs31.readFileSync(path38.join(root, f), "utf-8"))
);
checks.push(
hasRole ? { name: "Deployer role", status: "pass", detail: "mesh:deployerRole configured in stack config" } : {
name: "Deployer role",
status: "warn",
detail: "no mesh:deployerRole in Pulumi.*.yaml \u2014 `mesh deploy` will not be able to assume a role",
fix: "mesh create-app # scaffolds stack config with the deployer role"
}
);
}
const shapeProblems = [];
if (!fs31.existsSync(path38.join(root, "package.json"))) shapeProblems.push("package.json");
const hasWorkspace = fs31.existsSync(path38.join(root, "pnpm-workspace.yaml")) || fs31.existsSync(path38.join(root, "pnpm-lock.yaml"));
if (!hasWorkspace) shapeProblems.push("pnpm-workspace.yaml");
checks.push(
shapeProblems.length === 0 ? { name: "Repo shape", status: "pass", detail: "pnpm workspace layout present" } : {
name: "Repo shape",
status: "warn",
detail: `missing: ${shapeProblems.join(", ")}`,
fix: "mesh create-app # scaffold an app (workspace files included)"
}
);
const skillsOk = fix ? syncSkills(root) : syncSkills(root, { check: true });
checks.push(
skillsOk ? { name: "Agent skills", status: "pass", detail: "base skills + Intent discovery in sync" } : {
name: "Agent skills",
status: fix ? "pass" : "fail",
detail: fix ? "synced (--fix)" : "missing or stale",
fix: "mesh skills sync"
}
);
return checks;
}
function registerInitCommands(program2) {
const init = program2.command("init").enablePositionalOptions().description(
"Set up this machine and repo for a tenant \u2014 a guided wizard with no subcommand (tenant, local or deployed, package-registry sign-in, repo, skills); `init platform` scaffolds a platform repo, `init app-tenant` is the apps-repo doctor"
).option("--tenant <name>", "app tenant this repo belongs to (prompted when omitted on a TTY)").option("--local", "local only \u2014 mesh start / mesh dev, no deployed platform (the default)").option("--platform <env>", "a deployed platform's environment; signs in to <tenant>.<env>").option("--device", "device-code sign-in for the registry and platform steps (headless / SSH)").option("--profile <aws-profile>", "deployers only: AWS profile that can read the registry (skips the broker)").option("--skip-repo", "do not bootstrap, check or record anything in the current repo", false).option("--yes", "accept every default and never prompt", false).option("--json", "one JSON document on stdout ({tenant, mode, steps, next}); human output on stderr", false).action(
async (opts) => {
const { runInitWizard: runInitWizard2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
const code = await runInitWizard2(opts);
if (code !== 0) process.exitCode = code;
}
);
init.command("platform <tenant>").description(
"Scaffold a tenant PLATFORM repo (core + platform Pulumi layers on @mesh-tech/infra-components) that deploys with `mesh deploy` unmodified \u2014 the proven mesh-sandbox shape, annotated."
).option("--env <env>", "environment the first stack targets", "dev").option("--region <region>", "AWS region", "us-east-2").option("--domain <domain>", "base public domain (ingress zone becomes {env}.{domain})", "example.com").option("--profile <profile>", "AWS profile the stack config references", "default").option("--state-bucket <bucket>", "Pulumi state S3 bucket (created by an operator)", void 0).option("--dir <path>", "target directory (default: ./{tenant}-mesh-platform)").action(async (tenant, opts) => {
if (!/^[a-z][a-z0-9-]*$/.test(tenant)) {
throw new MeshCliError(`Invalid tenant name '${tenant}' (lowercase alphanumeric + dashes).`);
}
const targetDir = path38.resolve(opts.dir ?? `${tenant}-mesh-platform`);
if (fs31.existsSync(targetDir) && fs31.readdirSync(targetDir).length > 0) {
throw new MeshCliError(`Target directory is not empty: ${targetDir}`, {
remediation: { command: `mesh init platform ${tenant} --dir <empty-dir>` }
});
}
const { copyTemplate: copyTemplate2 } = await Promise.resolve().then(() => (init_create_app(), create_app_exports));
const { findPackageRoot: findPackageRoot2 } = await Promise.resolve().then(() => (init_stack(), stack_exports));
const templateDir = path38.join(findPackageRoot2(), "templates", "platform-repo");
const context = {
tenant,
env: opts.env,
region: opts.region,
domain: opts.domain,
profile: opts.profile,
stateBucket: opts.stateBucket ?? `${tenant}-mesh-platform-pulumi-state`
};
fs31.mkdirSync(targetDir, { recursive: true });
copyTemplate2(templateDir, targetDir, context);
syncSkills(targetDir);
logInfo(`Platform repo scaffolded at ${targetDir}`);
console.log("");
console.log("Next steps:");
console.log(` cd ${path38.relative(process.cwd(), targetDir) || "."}`);
console.log(` mesh login ${tenant}.${opts.env} # or an operator context with access`);
console.log(" pnpm install");
console.log(" cd core && mesh deploy up");
console.log(" cd ../platform && mesh deploy up");
console.log("");
logInfo(`Review the annotated stack configs (Pulumi.${tenant}-${opts.env}.yaml) before the first deploy.`);
});
init.command("app-tenant").description(
"Check (and with --fix, repair) everything an app-tenant repo needs: auth, registry, tenant registration, repo shape, agent skills. Re-run any time \u2014 it is the doctor."
).option("--tenant <name>", "app tenant (default: mesh:tenant from Pulumi config, else 'local')").option("--context <ctx>", "login context to check against (default: 'local' when the local platform is targeted)", LOGIN_CONTEXT).option("--hub-url <url>", "platform Hub API base URL", LOCAL_HUB_API).option("--fix", "apply developer-scope fixes (registry token, skills sync)", false).option("--profile <profile>", "AWS SSO profile --fix logs into CodeArtifact with (e.g. mesh-dev)").option("--json", "machine-readable output", false).action(async (opts) => {
const root = resolveTargetRoot();
const tenant = opts.tenant ?? detectLocalTenant(root);
if (!opts.json) {
logInfo(`Checking app-tenant repo ${root} (tenant '${tenant}', context '${opts.context}')\u2026`);
}
const origLog = console.log;
if (opts.json) console.log = (...a) => console.error(...a);
let checks;
try {
checks = await runChecks2({
root,
tenant,
context: opts.context,
hubUrl: opts.hubUrl.replace(/\/+$/, ""),
fix: opts.fix,
profile: opts.profile
});
} finally {
console.log = origLog;
}
const ok = !checks.some((c) => c.status === "fail");
if (opts.json) {
emitJsonPayload({ ok, root, tenant, context: opts.context, checks });
} else {
console.log("");
for (const check of checks) {
const icon = check.status === "pass" ? chalk5.green("\u2714") : check.status === "warn" ? chalk5.yellow("\u25B2") : chalk5.red("\u2718");
console.log(` ${icon} ${check.name.padEnd(24)} ${check.detail}`);
if (check.fix && check.status !== "pass") {
console.log(` ${chalk5.dim("\u2192 run:")} ${chalk5.cyan(check.fix)}`);
}
}
console.log("");
}
if (!ok) {
throw new MeshCliError("app-tenant checks failed \u2014 apply the fixes above and re-run.", {
remediation: { command: "mesh init app-tenant --fix" }
});
}
});
}
var LOCAL_HUB_API;
var init_init = __esm({
"libs/mesh-cli/src/commands/init.ts"() {
"use strict";
init_log();
init_errors();
init_auth_preflight();
init_login();
init_skills();
init_dev_local();
init_seed_zitadel();
LOCAL_HUB_API = "http://localhost:4568";
}
});
// libs/mesh-cli/src/commands/install-shim.ts
import { execFileSync as execFileSync27 } from "child_process";
import * as fs32 from "fs";
import * as os10 from "os";
import * as path39 from "path";
function shimScript() {
return `#!/bin/sh
# mesh \u2014 canonical launcher shim (installed by \`mesh install-shim\`).
# Walks up from the current directory to the nearest workspace mesh CLI and runs
# it. That resolves to bin/mesh.mjs, which runs the CLI from TypeScript source \u2014
# so this always runs the LOCAL source of whatever worktree/app you are in, with
# no build step. Nothing here is version-pinned; do not edit by hand.
#
# Outside a workspace it hands off to a globally installed mesh CLI.
# Without that hand-off the shim shadows \`npm i -g @mesh-tech/mesh-cli\` on the
# PATH and \`mesh login\` is unreachable from the home directory \u2014 which is the
# very first thing a new developer runs.
# True when $1 is another copy of this shim. Reads the marker line with the
# shell alone \u2014 no head/grep \u2014 so a stripped PATH cannot break the check.
_mesh_is_shim() {
_n=0
while [ "$_n" -lt 6 ] && IFS= read -r _line; do
case $_line in *"canonical launcher shim"*) return 0 ;; esac
_n=$((_n + 1))
done < "$1"
return 1
}
# Walk up to the nearest workspace CLI. Parameter expansion, not \`dirname\`:
# an unavailable dirname used to leave $dir empty and spin this loop forever.
dir=$(pwd)
while [ -n "$dir" ]; do
if [ -x "$dir/node_modules/.bin/mesh" ]; then
exec "$dir/node_modules/.bin/mesh" "$@"
fi
[ "$dir" = "/" ] && break
parent=\${dir%/*}
[ -n "$parent" ] || parent=/
dir=$parent
done
# Not in a workspace: exec the next \`mesh\` on PATH that is not another copy of
# this shim (matched on the marker line above, so we can never exec ourselves).
self_dir=\${0%/*}
[ "$self_dir" = "$0" ] && self_dir=.
self_dir=$(CDPATH= cd -- "$self_dir" 2>/dev/null && pwd -P)
saved_ifs=$IFS
IFS=:
set -f
for entry in $PATH; do
IFS=$saved_ifs
set +f
[ -n "$entry" ] || entry=.
candidate=$entry/mesh
# -f as well as -x: [ -x ] is true for a DIRECTORY named mesh, which would be
# taken for the CLI and exec'd into a bare 126 without ever reaching the real
# one further down PATH. A shell's own PATH search skips directories.
if [ -f "$candidate" ] && [ -x "$candidate" ]; then
entry_dir=$(CDPATH= cd -- "$entry" 2>/dev/null && pwd -P)
if [ -n "$entry_dir" ] && [ "$entry_dir" != "$self_dir" ] && ! _mesh_is_shim "$candidate"; then
exec "$candidate" "$@"
fi
fi
IFS=:
set -f
done
IFS=$saved_ifs
set +f
echo "mesh: not inside a Mesh workspace (no node_modules/.bin/mesh found)," >&2
echo " and no global mesh CLI found on PATH." >&2
echo " Install it: npm i -g @mesh-tech/mesh-cli" >&2
echo " Or cd into your app/repo (after 'pnpm install') and use 'pnpm exec mesh'." >&2
exit 1
`;
}
function defaultShimDir() {
return path39.join(os10.homedir(), ".local", "bin");
}
function dirOnPath(dir, pathEnv) {
if (!pathEnv) return false;
return pathEnv.split(path39.delimiter).some((p) => p === dir);
}
function registerInstallShimCommand(program2) {
program2.command("install-shim").description("Install a build-free `mesh` shim on your PATH so bare `mesh` runs the local source").option("--dir <dir>", "Directory to install into (must be on your PATH)", defaultShimDir()).option("-f, --force", "Overwrite an existing file at the target path", false).action((opts) => {
const dir = path39.resolve(opts.dir);
const target = path39.join(dir, "mesh");
if (fs32.existsSync(target) && !opts.force) {
logWarn(`A file already exists at ${target}.`);
logInfo("Re-run with --force to overwrite it, or pass --dir <other-path-on-PATH>.");
return;
}
try {
fs32.mkdirSync(dir, { recursive: true });
fs32.writeFileSync(target, shimScript(), { mode: 493 });
fs32.chmodSync(target, 493);
} catch (err) {
logError(`Could not write the shim to ${target}: ${err.message}`);
process.exitCode = 1;
return;
}
logSuccess(`Installed mesh shim \u2192 ${target}`);
if (!dirOnPath(dir, process.env.PATH)) {
logWarn(`${dir} is not on your PATH.`);
logInfo(`Add it, e.g.: echo 'export PATH="${dir}:$PATH"' >> ~/.zshrc && source ~/.zshrc`);
} else {
logInfo("Bare `mesh <cmd>` now runs the local source of whatever workspace you're in.");
}
const diagPath = path39.join(os10.tmpdir(), "mesh-shim-selftest.log");
let stdout = "";
let stderr = "";
let status = 0;
let ok = true;
try {
stdout = execFileSync27(target, ["--help"], {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"]
});
} catch (err) {
ok = false;
const e = err;
status = typeof e.status === "number" ? e.status : 1;
stdout = e.stdout?.toString?.() ?? "";
stderr = e.stderr?.toString?.() ?? String(e.message ?? err);
}
const diag = [
`mesh install-shim self-test \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
`cwd: ${process.cwd()}`,
`shim: ${target}`,
`node: ${process.execPath} (${process.version})`,
`PATH: ${process.env.PATH ?? ""}`,
`command: mesh --help`,
`exit: ${status}`,
`--- stdout (${stdout.length} bytes) ---`,
stdout,
`--- stderr (${stderr.length} bytes) ---`,
stderr,
""
].join("\n");
try {
fs32.writeFileSync(diagPath, diag);
} catch {
}
if (ok && /Commands:|Usage:/.test(stdout)) {
logSuccess(`Self-test passed: \`mesh --help\` ran cleanly. Diagnostic: ${diagPath}`);
} else {
logError(`Self-test FAILED (exit ${status}). Full diagnostic written to:`);
logError(` ${diagPath}`);
process.exitCode = 1;
}
});
}
var init_install_shim = __esm({
"libs/mesh-cli/src/commands/install-shim.ts"() {
"use strict";
init_log();
}
});
// libs/mesh-cli/src/commands/local/hub-local.ts
import { execFile as execFile3, execFileSync as execFileSync28 } from "child_process";
import * as fs33 from "fs";
import * as os11 from "os";
import * as path40 from "path";
import { promisify as promisify2 } from "util";
function cacheDir() {
return meshCacheDir("hub-local");
}
function npmrcPath() {
const p = path40.join(os11.homedir(), ".npmrc");
if (!fs33.existsSync(p) || !fs33.readFileSync(p, "utf-8").includes("codeartifact")) {
throw new MeshCliError(
"The local Hub builds from the published @mesh-tech/hub tarball, which needs registry auth.",
{ remediation: { command: REGISTRY_LOGIN_FIX } }
);
}
return p;
}
function imageExists(tag) {
try {
execFileSync28("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
return true;
} catch {
return false;
}
}
function ensureHubAuthImage() {
if (imageExists(HUB_AUTH_IMAGE)) return;
logInfo(`Building ${HUB_AUTH_IMAGE} (Hub auth proxy)\u2026`);
const hubStackDir = path40.join(findPackageRoot(), "stack", "hub");
execFileSync28(
"docker",
["build", "-f", path40.join(hubStackDir, "Dockerfile.auth"), "-t", HUB_AUTH_IMAGE, hubStackDir],
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
);
logSuccess(`Built ${HUB_AUTH_IMAGE}`);
}
function hasRegistryAuth() {
const p = path40.join(os11.homedir(), ".npmrc");
return fs33.existsSync(p) && fs33.readFileSync(p, "utf-8").includes("codeartifact");
}
function localHubVersion() {
const versions = HUB_IMAGES.map((name) => {
try {
const out = execFileSync28("docker", ["images", name, "--format", "{{.Tag}}"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"]
});
return out.split("\n").map((t) => t.trim()).filter((t) => t && t !== "<none>");
} catch {
return [];
}
});
const shared = versions[0].filter(
(v) => v.endsWith(`-${LOCAL_IMAGE_REV}`) && versions.every((list) => list.includes(v))
);
if (shared.length === 0) return void 0;
return shared.sort((a, b) => a.localeCompare(b, void 0, { numeric: true })).at(-1);
}
async function fetchTarball() {
const dir = cacheDir();
logInfo(`Resolving ${HUB_PACKAGE} from the registry\u2026`);
const heartbeat = startHeartbeat(`fetching the ${HUB_PACKAGE} tarball from CodeArtifact`);
try {
const { stdout } = await execFileAsync2(
"npm",
["pack", `${HUB_PACKAGE}@latest`, "--pack-destination", dir, "--json"],
{ encoding: "utf-8" }
);
const info = JSON.parse(stdout)[0];
return { tarball: path40.join(dir, info.filename), version: info.version };
} catch (err) {
const authFailure = isNpmAuthError(err);
const cached = fs33.readdirSync(dir).filter((f) => f.startsWith("mesh-tech-hub-") && f.endsWith(".tgz")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true })).at(-1);
if (!cached) {
if (authFailure) {
throw new MeshCliError(
`The registry rejected the ${HUB_PACKAGE} tarball fetch (E401 Unauthorized) \u2014 your CodeArtifact token is expired or invalid. (npm's "try npm login" advice does not apply to this registry.)`,
{ remediation: { command: REGISTRY_LOGIN_FIX }, cause: err }
);
}
throw err;
}
const version = cached.replace("mesh-tech-hub-", "").replace(/\.tgz$/, "");
if (authFailure) {
logWarn(
`Registry rejected the fetch (E401 \u2014 CodeArtifact token expired). Using the cached hub tarball v${version}; refresh with: ${REGISTRY_LOGIN_FIX}`
);
} else {
logInfo(`Registry unavailable \u2014 using the cached hub tarball v${version}`);
}
return { tarball: path40.join(dir, cached), version };
} finally {
heartbeat.stop();
}
}
function planWithHubRefresh(probeState, builtVersion) {
if (probeState !== "expired" && probeState !== "missing") return { action: "refresh" };
if (builtVersion) {
return {
action: "use-local",
version: builtVersion,
warning: `CodeArtifact token is ${probeState} \u2014 using the already-built local Hub images (v${builtVersion}). To pick up a newer published Hub: ${REGISTRY_LOGIN_FIX}, then re-run mesh start --with-hub.`
};
}
return {
action: "fail",
message: probeState === "expired" ? "Your CodeArtifact token is expired or rejected \u2014 the Hub build would fail minutes in with E401." : "No CodeArtifact registry auth found in ~/.npmrc \u2014 the Hub builds from the published @mesh-tech/hub tarball.",
remediation: REGISTRY_LOGIN_FIX
};
}
async function ensureHubImages() {
const npmrc = npmrcPath();
const { tarball, version: packageVersion } = await fetchTarball();
const version = `${packageVersion}-${LOCAL_IMAGE_REV}`;
const tags = HUB_IMAGES.map((name) => `${name}:${version}`);
if (tags.every(imageExists)) {
logInfo(`Local Hub images ready (v${version})`);
return version;
}
const context = path40.join(cacheDir(), `context-${version}`);
fs33.rmSync(context, { recursive: true, force: true });
fs33.mkdirSync(context, { recursive: true });
execFileSync28("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
stdio: ["ignore", "pipe", "pipe"]
});
const hubStackDir = path40.join(findPackageRoot(), "stack", "hub");
for (const [name, dockerfile] of [
["mesh-local-hub-api", "Dockerfile.api"],
["mesh-local-hub-ui", "Dockerfile.ui"]
]) {
const tag = `${name}:${version}`;
if (imageExists(tag)) continue;
logInfo(`Building ${tag} from the published tarball\u2026`);
execFileSync28(
"docker",
[
"build",
"-f",
path40.join(hubStackDir, dockerfile),
"-t",
tag,
"--secret",
`id=npmrc,src=${npmrc}`,
context
],
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
);
logSuccess(`Built ${tag}`);
}
return version;
}
function tarballNameForImageVersion(version) {
if (version.endsWith("-src")) return null;
return `mesh-tech-hub-${version.replace(/-r\d+$/, "")}.tgz`;
}
function readHubCompiledAuthz(version) {
const context = path40.join(cacheDir(), `context-${version}`);
const file = path40.join(context, HUB_COMPILED_AUTHZ_REL);
if (!fs33.existsSync(file)) {
const tarball = tarballNameForImageVersion(version);
if (!tarball || !fs33.existsSync(path40.join(cacheDir(), tarball))) return null;
fs33.mkdirSync(context, { recursive: true });
try {
execFileSync28(
"tar",
["-xzf", path40.join(cacheDir(), tarball), "-C", context, "--strip-components", "1", `package/${HUB_COMPILED_AUTHZ_REL}`],
{ stdio: ["ignore", "pipe", "pipe"] }
);
} catch {
return null;
}
if (!fs33.existsSync(file)) return null;
}
const parsed = JSON.parse(fs33.readFileSync(file, "utf-8"));
if (typeof parsed.zed !== "string" || !parsed.metadata || typeof parsed.metadata !== "object") return null;
return { zed: parsed.zed, metadata: parsed.metadata };
}
function readWorkspaceCatalog(repoRoot2) {
const text = fs33.readFileSync(path40.join(repoRoot2, "pnpm-workspace.yaml"), "utf-8");
const marker = "\ncatalog:\n";
const at = text.indexOf(marker);
if (at === -1) return {};
const catalog = {};
for (const line of text.slice(at + marker.length).split("\n")) {
if (line.trim() !== "" && !/^\s/.test(line)) break;
const m = /^\s+"?([^":\s]+)"?:\s*"?([^"\s#]+)"?/.exec(line);
if (m) catalog[m[1]] = m[2];
}
return catalog;
}
function normalizeHubManifests(contextDir, catalog) {
const DEP_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
const touched = [];
for (const entry of fs33.readdirSync(contextDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const manifest = path40.join(contextDir, entry.name, "package.json");
if (!fs33.existsSync(manifest)) continue;
const pkg = JSON.parse(fs33.readFileSync(manifest, "utf-8"));
let changed = false;
if (pkg.devDependencies) {
delete pkg.devDependencies;
changed = true;
}
for (const field of DEP_FIELDS) {
const deps = pkg[field];
if (!deps) continue;
for (const [name, spec] of Object.entries(deps)) {
if (typeof spec !== "string") continue;
if (spec.startsWith("workspace:")) {
delete deps[name];
changed = true;
} else if (spec.startsWith("catalog:")) {
const key = spec.slice("catalog:".length) || name;
const range = catalog[name] ?? catalog[key];
if (!range) {
throw new MeshCliError(
`${entry.name}/package.json depends on "${name}": "${spec}", which the workspace catalog does not define.`,
{ remediation: { command: "Add the dependency to the `catalog:` block in pnpm-workspace.yaml" } }
);
}
deps[name] = range;
changed = true;
}
}
}
if (changed) {
fs33.writeFileSync(manifest, `${JSON.stringify(pkg, null, 2)}
`);
touched.push(`${entry.name}/package.json`);
}
}
return touched;
}
async function buildHubImagesFromSource(repoRoot2) {
const hubDir = path40.join(repoRoot2, "apps", "hub");
if (!fs33.existsSync(path40.join(hubDir, "package.json"))) {
throw new MeshCliError(
`--hub-from-source needs a mesh-platform checkout; no apps/hub under ${repoRoot2}.`,
{ remediation: { command: "Run mesh start from a mesh-platform checkout, or drop --hub-from-source" } }
);
}
const npmrc = npmrcPath();
const version = `${JSON.parse(fs33.readFileSync(path40.join(hubDir, "package.json"), "utf-8")).version}-src`;
const build = startHeartbeat("building apps/hub (api + ui) from source");
try {
for (const pkg of ["@mesh-tech/hub-api", "@mesh-tech/hub-ui"]) {
await execFileAsync2("pnpm", ["--filter", pkg, "build"], { cwd: repoRoot2, maxBuffer: 64 * 1024 * 1024 });
}
} finally {
build.stop();
}
const context = path40.join(cacheDir(), `context-${version}`);
fs33.rmSync(context, { recursive: true, force: true });
fs33.mkdirSync(context, { recursive: true });
await execFileAsync2("pnpm", ["pack", "--pack-destination", context], { cwd: hubDir, maxBuffer: 64 * 1024 * 1024 });
const tarball = fs33.readdirSync(context).find((f) => f.endsWith(".tgz"));
if (!tarball) throw new MeshCliError("pnpm pack produced no tarball for apps/hub");
execFileSync28("tar", ["-xzf", path40.join(context, tarball), "-C", context, "--strip-components", "1"], {
stdio: ["ignore", "pipe", "pipe"]
});
const rewritten = normalizeHubManifests(context, readWorkspaceCatalog(repoRoot2));
if (rewritten.length > 0) logInfo(`Normalized ${rewritten.length} manifest(s) for npm: ${rewritten.join(", ")}`);
const hubStackDir = path40.join(findPackageRoot(), "stack", "hub");
for (const [name, dockerfile] of [
["mesh-local-hub-api", "Dockerfile.api"],
["mesh-local-hub-ui", "Dockerfile.ui"]
]) {
const tag = `${name}:${version}`;
logInfo(`Building ${tag} from source\u2026`);
execFileSync28(
"docker",
["build", "-f", path40.join(hubStackDir, dockerfile), "-t", tag, "--secret", `id=npmrc,src=${npmrc}`, context],
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
);
logSuccess(`Built ${tag}`);
}
return version;
}
var execFileAsync2, HUB_PACKAGE, HUB_IMAGES, LOCAL_IMAGE_REV, HUB_AUTH_IMAGE, HUB_COMPILED_AUTHZ_REL;
var init_hub_local = __esm({
"libs/mesh-cli/src/commands/local/hub-local.ts"() {
"use strict";
init_log();
init_errors();
init_auth_preflight();
init_stack();
init_cache_home();
execFileAsync2 = promisify2(execFile3);
HUB_PACKAGE = "@mesh-tech/hub";
HUB_IMAGES = ["mesh-local-hub-api", "mesh-local-hub-ui"];
LOCAL_IMAGE_REV = "r2";
HUB_AUTH_IMAGE = "mesh-local-hub-auth:v7.7.1-r1";
HUB_COMPILED_AUTHZ_REL = "api/dist/authz/compiled.json";
}
});
// libs/mesh-cli/src/commands/local/seed-hub-catalog.ts
function withHubCatalogRefs(pointer, metadataRef) {
return {
spicedb: { instanceRefs: [] },
...pointer,
opsHubMetadataRef: metadataRef,
mode: typeof pointer.mode === "string" ? pointer.mode : "policy-engine"
};
}
async function publishHubAuthzCatalog(compiled, awsConfig) {
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient5(awsConfig);
await ssm.send(
new PutParameterCommand({
Name: HUB_OPS_HUB_METADATA_PARAM,
Type: "String",
// The blob is ~7.5 KB — past the 4 KB standard tier, within Advanced (8 KB),
// the tier the platform's Export uses for the same param.
Tier: "Advanced",
Overwrite: true,
Value: JSON.stringify(compiled.metadata),
Description: "Hub role catalog \u2014 ops-hub-metadata (local analog of MeshHub's SpiceDBSchema export)"
})
);
let current = {};
try {
const existing = await ssm.send(new GetParameterCommand2({ Name: HUB_AUTHZ_POINTER_PARAM }));
current = JSON.parse(existing.Parameter?.Value ?? "{}");
} catch {
}
await ssm.send(
new PutParameterCommand({
Name: HUB_AUTHZ_POINTER_PARAM,
Type: "String",
Overwrite: true,
Value: JSON.stringify(withHubCatalogRefs(current, HUB_OPS_HUB_METADATA_PARAM)),
Description: "Hub authz pointer (local analog of the platform Pulumi program)"
})
);
return { bundles: catalogBundleNames(compiled.metadata) };
}
function catalogBundleNames(metadata) {
const bundles = metadata.bundles;
if (Array.isArray(bundles)) {
return bundles.map((b) => b && typeof b === "object" && typeof b.name === "string" ? b.name : null).filter((n) => n !== null);
}
if (bundles && typeof bundles === "object") return Object.keys(bundles);
return [];
}
var HUB_AUTHZ_POINTER_PARAM, HUB_OPS_HUB_METADATA_PARAM;
var init_seed_hub_catalog = __esm({
"libs/mesh-cli/src/commands/local/seed-hub-catalog.ts"() {
"use strict";
init_seed();
HUB_AUTHZ_POINTER_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/apps/hub/stacks/local/authz`;
HUB_OPS_HUB_METADATA_PARAM = `${HUB_AUTHZ_POINTER_PARAM}/ops-hub-metadata`;
}
});
// libs/mesh-cli/src/commands/local/index.ts
import * as fs34 from "fs";
import * as path41 from "path";
import chalk6 from "chalk";
function crashError(crashed) {
return new MeshCliError(
`Service(s) exited during startup: ${crashed.map((s) => s.name).join(", ")}`,
{
remediation: {
command: `docker compose -p ${COMPOSE_PROJECT} logs ${crashed[0].name}`
}
}
);
}
async function waitForStack(io = {
probe: probeEndpoint,
services: stackServices,
overlayServices: hubOverlayServices
}) {
const deadline = Date.now() + WAIT_TIMEOUT_MS;
let pending = STACK_ENDPOINTS.filter((e) => e.probe.kind !== "none");
while (true) {
const results = await Promise.all(pending.map((e) => io.probe(e)));
const stillDown = [];
for (let i = 0; i < pending.length; i++) {
if (results[i]) {
logSuccess(`${pending[i].label} is up (${pending[i].url})`);
} else {
stillDown.push(pending[i]);
}
}
pending = stillDown;
const crashed = crashedServices(io.services(), {
overlayServices: io.overlayServices(),
overlayStarted: false
});
if (crashed.length > 0) throw crashError(crashed);
if (pending.length === 0) break;
if (Date.now() > deadline) {
throw new MeshCliError(
`Timed out waiting for: ${pending.map((e) => e.label).join(", ")}`,
{ remediation: { command: "mesh status" } }
);
}
logInfo(`Waiting for ${pending.map((e) => e.label).join(", ")} \u2026`);
await new Promise((resolve19) => setTimeout(resolve19, WAIT_POLL_MS));
}
}
function printEndpoints(hubRunning) {
console.log("");
if (hubRunning) {
console.log(chalk6.bold.cyan("\u2605 Mesh Hub \u2014 your operations dashboard (start here)"));
console.log(` ${"Hub UI".padEnd(34)} ${chalk6.bold.cyan(`http://localhost:${hubPort()}`)}`);
console.log(
` ${"".padEnd(34)} ${chalk6.dim("sign in: admin@local.mesh or dev@local.mesh / LocalDev1!")}`
);
console.log(` ${"Hub API".padEnd(34)} ${chalk6.cyan("http://localhost:4568")}`);
console.log(
` ${"".padEnd(34)} ${chalk6.dim("tenants, apps, workflows, logs, traces \u2014 local runs self-register here")}`
);
} else {
logWarn("Hub is not running \u2014 it is the platform's front door. Enable it with:");
logInfo(` ${REGISTRY_LOGIN_FIX} (one-time, to build the Hub images \u2014 no AWS account needed)`);
logInfo(" mesh start (the Hub joins automatically)");
}
console.log("");
console.log(chalk6.bold("Local platform endpoints"));
for (const endpoint of STACK_ENDPOINTS) {
console.log(` ${endpoint.label.padEnd(34)} ${chalk6.cyan(endpoint.url)}`);
if (endpoint.hint) {
console.log(` ${"".padEnd(34)} ${chalk6.dim(endpoint.hint)}`);
}
}
console.log("");
console.log(chalk6.bold("AWS SDK wiring (local fabric)"));
console.log(chalk6.dim(" Point any AWS SDK process at the local registry \u2014 no code changes:"));
for (const [key, value] of Object.entries(localAwsEnv())) {
console.log(` export ${key}=${chalk6.cyan(value)}`);
}
console.log("");
console.log(chalk6.bold("Login"));
console.log(` mesh login ${LOGIN_CONTEXT}`);
console.log(
` ${chalk6.dim(`test users: ${TEST_USERS.map((u) => u.email).join(", ")} (password: ${TEST_USERS[0].password})`)}`
);
printStartHere(hubRunning);
}
function startHereLines(hubRunning, up) {
const dev = TEST_USERS[0];
const mailbox = STACK_ENDPOINTS.find((e) => e.service === "mailpit");
const flag = (service) => up?.get(service) === false ? chalk6.red(" (not responding \u2014 see Endpoints below)") : "";
const lines = ["", chalk6.bold.cyan("\u2605 Start here")];
if (hubRunning) {
lines.push(
` ${"Hub".padEnd(10)} ${chalk6.bold.cyan(`http://localhost:${hubPort()}`)}${flag("hub-ui")}`,
` ${"".padEnd(10)} ${chalk6.dim(`sign in as ${dev.email} / ${dev.password} \u2014 an account already exists; do not Register`)}`
);
}
if (mailbox) {
lines.push(
` ${"Mailbox".padEnd(10)} ${chalk6.cyan(mailbox.url)}${flag("mailpit")}`,
` ${"".padEnd(10)} ${chalk6.dim("every email the local platform sends (sign-up, verification, password reset) lands here \u2014 nothing leaves your machine")}`
);
}
lines.push(
` ${"Your app".padEnd(10)} ${chalk6.cyan("cd apps/<name> && mesh dev")} ${chalk6.dim("no app yet? mesh create-app")}`,
` ${"Check-up".padEnd(10)} ${chalk6.cyan("mesh status")}`,
""
);
return lines;
}
function printStartHere(hubRunning, up) {
for (const line of startHereLines(hubRunning, up)) console.log(line);
}
function portMoveHints() {
return /* @__PURE__ */ new Map([
[
Number(hubPort()),
"MESH_HUB_PORT=<free port> mesh start (or mesh start --no-hub, which starts everything else)"
]
]);
}
function repoRootForHubSource() {
for (let dir = process.cwd(); ; ) {
if (fs34.existsSync(path41.join(dir, "apps", "hub", "package.json"))) return dir;
const parent = path41.dirname(dir);
if (parent === dir) break;
dir = parent;
}
return path41.resolve(findPackageRoot(), "..", "..");
}
function registerLocalCommands(program2) {
program2.command("start").description("Start the full-local Mesh platform (Docker only \u2014 no AWS, no VPN)").option("--no-seed", "skip first-boot seeding (tenant registry, artifacts bucket)").option("--no-hub", "start without the Hub (API + UI)").option(
"--with-hub",
"force-refresh the Hub to the latest published @mesh-tech/hub (the Hub is included by default when its images exist or registry auth is available)"
).option(
"--hub-from-source",
"build the Hub images from THIS checkout's apps/hub instead of the published tarball (the only way to exercise a Hub change locally)"
).option(
"--takeover",
"re-own a stack that another checkout started (may recreate shared containers with THIS checkout's config)",
false
).option(
"--skip-port-check",
"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)",
false
).action(async (opts) => {
ensureDockerAvailable();
const foreign = stackOwnedElsewhere();
if (foreign && !opts.takeover) {
throw new MeshCliError(
`The local platform is already running, started from a different checkout:
${foreign}
Re-running start from here may recreate shared containers with this checkout's config and disrupt work using the current stack.`,
{
remediation: {
command: "mesh status # inspect the running stack (apps deploy tenant-scoped via `mesh dev --local`)\n mesh start --takeover # intentionally re-own the stack from this checkout"
}
}
);
}
if (foreign && opts.takeover) {
logWarn(`Taking over the running stack (previously driven from ${foreign}).`);
}
if (!opts.skipPortCheck && !stackServices().some((s) => s.state === "running")) {
const ports = hostPortsOf([...STACK_ENDPOINTS, ...opts.hub ? hubEndpoints() : []]);
const conflicts = await findPortConflicts(ports, `${COMPOSE_PROJECT}-`);
if (conflicts.length > 0) {
throw new MeshCliError(describePortConflicts(conflicts, portMoveHints()), {
remediation: { command: "mesh start # once the port is free" }
});
}
}
let hubVersion;
if (opts.hub) {
if (opts.hubFromSource) {
hubVersion = await buildHubImagesFromSource(repoRootForHubSource());
} else if (opts.withHub) {
const probe = await probeRegistryToken();
const plan = planWithHubRefresh(probe.state, localHubVersion());
if (plan.action === "fail") {
throw new MeshCliError(plan.message, { remediation: { command: plan.remediation } });
}
if (plan.action === "use-local") {
logWarn(plan.warning);
hubVersion = plan.version;
} else {
hubVersion = await ensureHubImages();
}
} else {
hubVersion = localHubVersion();
if (!hubVersion && hasRegistryAuth()) {
const probe = await probeRegistryToken();
if (probe.state === "expired") {
logWarn(
`CodeArtifact token is expired \u2014 starting without the Hub. Fix: ${REGISTRY_LOGIN_FIX}, then: mesh start --with-hub`
);
} else {
try {
hubVersion = await ensureHubImages();
} catch (err) {
logWarn(
`Hub image build failed (${err instanceof Error ? err.message : err}) \u2014 starting without the Hub.`
);
}
}
}
}
}
logInfo("Starting the local Mesh platform (docker compose project: mesh-local)\u2026");
await composeStreamed(["up", "-d"]);
compose(["restart", "prometheus", "blackbox-exporter"], { inherit: true });
await waitForStack();
let hubAuth = readHubAuth();
if (opts.seed) {
logInfo("Seeding local platform (tenant registry, artifacts bucket)\u2026");
await seedLocalPlatform();
logInfo("Seeding Zitadel (Platform project, Mesh CLI app, Hub auth, test users)\u2026");
const zitadel = await seedZitadel(LOCAL_AWS_CONFIG);
hubAuth = zitadel.hubAuth;
if (hubVersion) {
const compiled = readHubCompiledAuthz(hubVersion);
if (compiled) {
const { bundles } = await publishHubAuthzCatalog(compiled, LOCAL_AWS_CONFIG);
logSuccess(`Hub role catalog seeded: ${bundles.length} role(s) (${bundles.join(", ")})`);
} else {
logWarn(
`Hub v${hubVersion} ships no compiled role catalog (api/dist/authz/compiled.json) \u2014 Users \u2192 Roles will be empty. Hub \u2265 2.1.0 publishes one.`
);
}
}
try {
const reconciled = await reconcileRegistryFromZitadel();
if (reconciled.tenants.length > 0) {
logSuccess(
`Hub registry reconciled from Zitadel: ${reconciled.tenants.length} app tenant(s) (${reconciled.tenants.join(", ")}), ${reconciled.apps} app(s)`
);
}
} catch (err) {
logWarn(`Registry reconcile skipped: ${err instanceof Error ? err.message : err}`);
}
logInfo("Verifying the local AWS fabric (8 KB Advanced-tier round-trip)\u2026");
await verifyFabric();
} else {
logWarn("Skipping seed (--no-seed).");
}
if (hubVersion) {
if (!hubAuth) {
logWarn("Hub auth is not seeded yet (started with --no-seed?) \u2014 Hub sign-in needs one seeded run: mesh start");
}
if (!opts.seed && hubPort() !== DEFAULT_HUB_PORT) {
logWarn(
`--no-seed with MESH_HUB_PORT=${hubPort()}: this port's redirect URI is registered during seeding \u2014 if Hub sign-in fails with "redirect_uri is missing in the client configuration", re-run without --no-seed.`
);
}
ensureHubAuthImage();
const hubEnv = { MESH_HUB_VERSION: hubVersion };
if (hubAuth) {
hubEnv.MESH_HUB_OAUTH2_CLIENT_ID = hubAuth.clientId;
hubEnv.MESH_HUB_OAUTH2_CLIENT_SECRET = hubAuth.clientSecret;
hubEnv.MESH_HUB_OAUTH2_COOKIE_SECRET = hubAuth.cookieSecret;
}
await composeStreamed(["up", "-d"], { hub: true, env: hubEnv });
if (opts.seed) {
compose(["restart", "hub-api"], { hub: true, env: hubEnv });
}
const overlayServices = hubOverlayServices();
for (const endpoint of hubEndpoints()) {
logInfo(`Waiting for ${endpoint.label}\u2026`);
let up = await probeEndpoint(endpoint);
for (let i = 0; i < 60 && !up; i++) {
const crashed = crashedServices(stackServices(), {
overlayServices,
overlayStarted: true
});
if (crashed.length > 0) throw crashError(crashed);
await new Promise((resolve19) => setTimeout(resolve19, 2e3));
up = await probeEndpoint(endpoint);
}
if (!up) {
throw new MeshCliError(`Timed out waiting for ${endpoint.label} (${endpoint.url}).`, {
remediation: {
command: `docker compose -p ${COMPOSE_PROJECT} logs ${endpoint.service}`
}
});
}
logSuccess(`${endpoint.label} is up (${endpoint.url})`);
}
}
logSuccess("Local Mesh platform is running.");
printEndpoints(!!hubVersion);
});
program2.command("stop").description("Stop the local Mesh platform").option("--destroy", "also remove volumes (resets databases and seeds)", false).option("--force", "stop even when the stack was started from a different checkout", false).action((opts) => {
ensureDockerAvailable();
const foreign = stackOwnedElsewhere();
if (foreign && !opts.force) {
throw new MeshCliError(
`The running local platform was started from a different checkout (${foreign}) \u2014 another project may be using it.`,
{ remediation: { command: "mesh stop --force # stop it anyway" } }
);
}
const args = ["down"];
if (opts.destroy) args.push("-v");
compose(args, { inherit: true, hub: true });
logSuccess(opts.destroy ? "Local platform stopped and volumes destroyed." : "Local platform stopped.");
});
program2.command("status").description("Show local platform component health, endpoints, and ports").option("--json", "machine-readable output", false).action(async (opts) => {
ensureDockerAvailable();
const services = stackServices();
if (services.length === 0) {
if (opts.json) {
console.log(JSON.stringify({ running: false, services: [], endpoints: [] }));
return;
}
throw new MeshCliError("The local Mesh platform is not running.", {
remediation: { command: "mesh start" }
});
}
const overlayServices = hubOverlayServices();
const overlayStarted = hubOverlayRunning(services, overlayServices);
const notStarted = (s) => !overlayStarted && overlayServices.has(s.name) && s.state === "exited";
const hasHub = hubApiRunning(services);
const endpoints = hasHub ? [...STACK_ENDPOINTS, ...hubEndpoints()] : STACK_ENDPOINTS;
const probes = await Promise.all(
endpoints.map(async (endpoint) => ({
service: endpoint.service,
label: endpoint.label,
url: endpoint.url,
up: await probeEndpoint(endpoint)
}))
);
let fabric = null;
if (probes.find((p) => p.service === "ministack")?.up) {
try {
await verifyFabric();
fabric = { ok: true };
} catch (err) {
fabric = { ok: false, error: err instanceof Error ? err.message : String(err) };
}
}
if (opts.json) {
console.log(
JSON.stringify({
running: true,
services,
endpoints: probes,
fabric,
awsEnv: localAwsEnv()
})
);
if (fabric && !fabric.ok) process.exitCode = 1;
return;
}
printStartHere(hasHub, new Map(probes.map((p) => [p.service, p.up])));
console.log(chalk6.bold("Containers"));
for (const service of services) {
const ok = service.state === "running" && (service.health === void 0 || service.health === "healthy");
const oneShotDone = ONE_SHOT_SERVICES.has(service.name) && service.state === "exited";
const idle = notStarted(service);
const icon = ok || oneShotDone ? chalk6.green("\u25CF") : idle ? chalk6.dim("\u25CB") : chalk6.red("\u25CF");
const health = service.health ? ` (${service.health})` : "";
const note = idle ? chalk6.dim(" (Hub not started this run)") : "";
console.log(` ${icon} ${service.name.padEnd(22)} ${service.state}${health}${note}`);
}
console.log("");
console.log(chalk6.bold("Endpoints"));
for (const probe of probes) {
const icon = probe.up ? chalk6.green("\u25CF") : chalk6.red("\u25CF");
console.log(` ${icon} ${probe.label.padEnd(34)} ${chalk6.cyan(probe.url)}`);
}
if (fabric) {
const icon = fabric.ok ? chalk6.green("\u25CF") : chalk6.red("\u25CF");
const detail = fabric.ok ? "8 KB Advanced-tier round-trip OK" : fabric.error;
console.log(` ${icon} ${"AWS fabric check".padEnd(34)} ${chalk6.dim(detail ?? "")}`);
}
const down = probes.filter((p) => !p.up);
const crashed = crashedServices(services, { overlayServices, overlayStarted });
if (fabric && !fabric.ok) process.exitCode = 1;
if (down.length > 0 || crashed.length > 0) {
console.log("");
logError(
`${down.length + crashed.length} component(s) unhealthy \u2014 inspect with: docker compose -p mesh-local logs <service>`
);
process.exitCode = 1;
}
});
}
var WAIT_TIMEOUT_MS, WAIT_POLL_MS;
var init_local = __esm({
"libs/mesh-cli/src/commands/local/index.ts"() {
"use strict";
init_log();
init_errors();
init_stack();
init_auth_preflight();
init_hub_local();
init_auth_provision();
init_seed();
init_seed_zitadel();
init_seed_hub_catalog();
init_helpers();
WAIT_TIMEOUT_MS = 18e4;
WAIT_POLL_MS = 3e3;
}
});
// libs/mesh-cli/src/commands/secrets/exec.ts
import { spawn as spawn8 } from "child_process";
import {
SecretsManagerClient as SecretsManagerClient4,
GetSecretValueCommand as GetSecretValueCommand4
} from "@aws-sdk/client-secrets-manager";
function logInfo2(msg) {
console.log(`\x1B[36m\u2139\x1B[0m ${msg}`);
}
function logSuccess2(msg) {
console.log(`\x1B[32m\u2713\x1B[0m ${msg}`);
}
function logError2(msg) {
console.error(`\x1B[31m\u2717\x1B[0m ${msg}`);
}
function parseArgs(args) {
const options = {};
let commandArgs = [];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--") {
commandArgs = args.slice(i + 1);
break;
}
if (arg.startsWith("--secret=")) {
options.secret = arg.substring("--secret=".length);
} else if (arg === "--secret" && args[i + 1]) {
options.secret = args[++i];
} else if (arg.startsWith("--db-host=")) {
options.dbHost = arg.substring("--db-host=".length);
} else if (arg === "--db-host" && args[i + 1]) {
options.dbHost = args[++i];
}
}
return { options, commandArgs };
}
async function fetchSecret(client, secretId) {
const response = await client.send(
new GetSecretValueCommand4({ SecretId: secretId })
);
if (!response.SecretString) {
throw new Error("Secret has no string value");
}
try {
return JSON.parse(response.SecretString);
} catch {
throw new Error("Secret is not valid JSON");
}
}
function rewriteDatabaseHost(databaseUrl, newHost) {
try {
const url = new URL(databaseUrl);
const originalHost = url.host;
url.hostname = newHost;
if (!newHost.includes(":")) {
url.port = url.port || "5432";
}
logInfo2(`Rewrote DATABASE_URL host: ${originalHost} \u2192 ${url.host}`);
return url.toString();
} catch {
logError2(`Failed to parse DATABASE_URL for host rewrite`);
return databaseUrl;
}
}
async function execCommand2(args) {
const { options, commandArgs } = parseArgs(args);
if (!options.secret || commandArgs.length === 0) {
logError2("Usage: mesh secrets exec --secret=<name-or-arn> [options] -- <command>");
logInfo2("");
logInfo2("Options:");
logInfo2(" --secret=<name-or-arn> Secret name or ARN in Secrets Manager");
logInfo2(" --db-host=<host> Rewrite DATABASE_URL host (for dev tunneling)");
logInfo2("");
logInfo2("Examples:");
logInfo2(
" mesh secrets exec --secret=mesh/tenant/stage/app/secret -- pnpm dev"
);
logInfo2(
" mesh secrets exec --secret=mesh/tenant/stage/app/db --db-host=localhost -- pnpm dev"
);
process.exit(1);
}
const secretsManager = new SecretsManagerClient4({});
logInfo2(`Fetching secret: ${options.secret}`);
let secretValues;
try {
secretValues = await fetchSecret(secretsManager, options.secret);
} catch (error) {
logError2(`Failed to fetch secret: ${error}`);
process.exit(1);
}
const envKeys = Object.keys(secretValues);
logSuccess2(`Loaded ${envKeys.length} environment variable(s) from secret`);
if (options.dbHost && secretValues.DATABASE_URL) {
secretValues.DATABASE_URL = rewriteDatabaseHost(
secretValues.DATABASE_URL,
options.dbHost
);
}
const env = {
...process.env,
...secretValues
};
const [cmd, ...cmdArgs] = commandArgs;
logInfo2(`Running: ${commandArgs.join(" ")}`);
console.log("");
const child = spawn8(cmd, cmdArgs, { stdio: "inherit", env });
child.on("exit", (code) => {
process.exit(code ?? 0);
});
child.on("error", (error) => {
logError2(`Failed to start command: ${error.message}`);
process.exit(1);
});
}
var init_exec2 = __esm({
"libs/mesh-cli/src/commands/secrets/exec.ts"() {
"use strict";
}
});
// libs/secrets/src/index.ts
import {
SecretsManagerClient as SecretsManagerClient5,
GetSecretValueCommand as GetSecretValueCommand5
} from "@aws-sdk/client-secrets-manager";
function instanceIndexSecretId(secretPrefix) {
return `${secretPrefix}/${INSTANCE_INDEX_SUFFIX}`;
}
function isInstanceKey(key) {
return key.length > 0 && !key.includes("/") && !key.startsWith(".") && !key.endsWith(".config");
}
function instanceKeyFromSecretName(secretPrefix, secretName) {
if (!secretName.startsWith(`${secretPrefix}/`)) return null;
const key = secretName.slice(secretPrefix.length + 1);
return isInstanceKey(key) ? key : null;
}
function parseInstanceIndex(secretString) {
let parsed;
try {
parsed = JSON.parse(secretString);
} catch {
parsed = void 0;
}
const instances = parsed?.instances;
if (!Array.isArray(instances) || !instances.every((k) => typeof k === "string")) {
throw new Error(
`Malformed instance index (expected {"instances": string[]}). Rebuild it with: mesh secrets reindex external/<name>`
);
}
return instances.filter(isInstanceKey);
}
function buildInstanceIndexValue(keys) {
const instances = [...new Set([...keys].filter(isInstanceKey))].sort();
const index = { version: 1, instances };
return JSON.stringify(index);
}
var INSTANCE_INDEX_SUFFIX;
var init_src3 = __esm({
"libs/secrets/src/index.ts"() {
"use strict";
INSTANCE_INDEX_SUFFIX = ".index";
}
});
// libs/mesh-cli/src/commands/secrets/set.ts
import {
SSMClient as SSMClient2,
GetParametersByPathCommand
} from "@aws-sdk/client-ssm";
import {
SecretsManagerClient as SecretsManagerClient6,
GetSecretValueCommand as GetSecretValueCommand6,
PutSecretValueCommand,
CreateSecretCommand
} from "@aws-sdk/client-secrets-manager";
import input from "@inquirer/input";
import password from "@inquirer/password";
async function ensureAwsCredentialsForStack(stack) {
if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN) return;
if (!stack) return;
const appRoot = findAppRoot(process.cwd());
if (!appRoot) return;
const roleArn = readStackConfig(appRoot, stack, "mesh:deployerRole");
if (!roleArn) return;
const resolved = await resolveAwsCredentials(roleArn, appRoot, stack);
if (resolved) {
Object.assign(process.env, resolved.env);
delete process.env.AWS_PROFILE;
}
}
function normalizeEntry(raw) {
if (raw.type === "group" && raw.fields) {
return {
type: "group",
name: raw.name ?? "",
description: raw.description ?? "",
fields: normalizeFields(raw.fields)
};
}
return {
type: "field",
name: raw.name ?? "",
description: raw.description ?? "",
secret: raw.secret ?? false,
optional: raw.optional ?? false
};
}
function normalizeFields(raw) {
return Object.fromEntries(
Object.entries(raw).map(([key, value]) => [key, normalizeEntry(value)])
);
}
async function discoverExternalServices(ssmClient, tenant, platformEnv) {
const services = /* @__PURE__ */ new Map();
const basePath = `/mesh-platform/${tenant}/${platformEnv}`;
let nextToken;
do {
const response = await ssmClient.send(
new GetParametersByPathCommand({
Path: basePath,
Recursive: true,
NextToken: nextToken
})
);
for (const param of response.Parameters ?? []) {
if (!param.Name || !param.Value) continue;
const metaMatch = param.Name.match(
/\/external-services\/([^/]+)\/meta$/
);
if (metaMatch?.[1]) {
const serviceName = metaMatch[1];
const meta = JSON.parse(param.Value);
const existing = services.get(serviceName);
services.set(serviceName, { ...existing, meta });
}
const credMatch = param.Name.match(
/\/external-services\/([^/]+)\/credentials$/
);
if (credMatch?.[1]) {
const serviceName = credMatch[1];
const raw = JSON.parse(param.Value);
const schema = {
fields: normalizeFields(raw.fields),
keyedBy: raw.keyedBy
};
const existing = services.get(serviceName);
if (existing) {
existing.schema = schema;
} else {
services.set(serviceName, { meta: {}, schema });
}
}
}
nextToken = response.NextToken;
} while (nextToken);
return services;
}
async function promptFields(fields, existing, result, opts, depth = 0) {
for (const [fieldKey, entry] of Object.entries(fields)) {
if (entry.type === "group") {
const indent = " ".repeat(depth);
console.error("");
console.error(`${indent}\u2500\u2500 ${entry.name} \u2500\u2500`);
const existingGroup = existing[fieldKey] ?? {};
const resultGroup = {};
await promptFields(entry.fields, existingGroup, resultGroup, opts, depth + 1);
result[fieldKey] = resultGroup;
} else {
if (entry.optional && !opts.all) {
const existingVal2 = existing[fieldKey];
if (existingVal2 !== void 0 && existingVal2 !== "") {
result[fieldKey] = existingVal2;
}
continue;
}
const existingVal = existing[fieldKey];
const hasExisting = !!existingVal && existingVal !== "";
const message = entry.description ? `${entry.name} (${entry.description})` : entry.name;
const label = entry.optional ? `${message} [optional]` : message;
let value;
if (entry.secret) {
value = await password({ message: label, mask: "*" });
if (!value && hasExisting) {
value = existingVal;
}
} else {
value = await input({
message: label,
default: hasExisting ? existingVal : void 0
});
}
result[fieldKey] = value || (hasExisting ? existingVal : "");
}
}
}
function displaySummary(fields, values, depth = 0) {
const indent = " ".repeat(depth);
for (const [key, entry] of Object.entries(fields)) {
if (entry.type === "group") {
console.error(`${indent}${entry.name}:`);
const group = values[key] ?? {};
displaySummary(entry.fields, group, depth + 1);
} else {
const val = values[key];
if (!val) continue;
const display = entry.secret ? "********" : String(val);
console.error(`${indent}${entry.name}: ${display}`);
}
}
}
async function setCommand(servicePath, opts) {
const stackOpt = resolveStackOption(opts);
const context = detectContext(stackOpt);
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
const ssmClient = new SSMClient2({ region });
const smClient = new SecretsManagerClient6({ region });
const services = await discoverExternalServices(
ssmClient,
context.tenant,
context.platformEnv
);
if (services.size === 0) {
logError(
'No external services found. Deploy an ExternalService first:\n new mesh.apps.ExternalService("symitar", { env, credentials: { ... } })'
);
process.exit(1);
}
let serviceName;
if (servicePath) {
serviceName = servicePath.replace(/^external\//, "");
}
if (!serviceName || !services.has(serviceName)) {
if (serviceName) {
logError(`External service "${serviceName}" not found.`);
}
console.error("\nAvailable external services:");
for (const [name, svc2] of services) {
const desc = svc2.meta?.description ? ` \u2014 ${svc2.meta.description}` : "";
const type = svc2.meta?.type ? ` (${svc2.meta.type})` : "";
console.error(` ${name}${type}${desc}`);
}
console.error(`
Usage: mesh secrets set external/<name>`);
process.exit(serviceName ? 1 : 0);
}
const svc = services.get(serviceName);
const schema = svc.schema;
if (!schema) {
logError(`No credential schema found for "${serviceName}".`);
process.exit(1);
}
const secretPrefix = svc.meta.secretPrefix;
const secretId = opts.key ? `${secretPrefix}/${opts.key}` : secretPrefix;
if (schema.keyedBy && !opts.key) {
logError(
`"${serviceName}" is multi-instance (keyed by ${schema.keyedBy}). Specify an instance:
mesh secrets set external/${serviceName} --key=<${schema.keyedBy}>`
);
process.exit(1);
}
if (opts.key && !isInstanceKey(opts.key)) {
logError(
`Invalid instance key "${opts.key}": keys must not contain "/", start with ".", or end with ".config" \u2014 those names are reserved for the index and config mirrors.`
);
process.exit(1);
}
let existing = {};
try {
const response = await smClient.send(
new GetSecretValueCommand6({ SecretId: secretId })
);
if (response.SecretString) {
existing = JSON.parse(response.SecretString);
}
} catch (err) {
if (!isResourceNotFound(err)) {
throw err;
}
}
if (opts.json) {
let values2;
try {
values2 = JSON.parse(opts.json);
} catch {
logError(`Invalid JSON input. Provide valid JSON, e.g.: --json='{"key": "value"}'`);
return;
}
const merged2 = deepMerge(existing, values2);
await writeSecret(smClient, secretId, merged2, svc.meta);
await writeConfigMirror(smClient, secretId, schema, merged2, svc.meta);
if (opts.key) {
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
}
logSuccess(`Credentials written to ${secretId}`);
return;
}
console.error("");
logInfo(`Setting credentials for: ${svc.meta.description || serviceName}`);
if (opts.key) {
logInfo(`Instance: ${opts.key}`);
}
logInfo(`Secret: ${secretId}`);
const values = {};
await promptFields(schema.fields, existing, values, { all: !!opts.all });
console.error("");
const merged = deepMerge(existing, values);
await writeSecret(smClient, secretId, merged, svc.meta);
await writeConfigMirror(smClient, secretId, schema, merged, svc.meta);
if (opts.key) {
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
}
logSuccess(`Credentials written to ${secretId}`);
console.error("\nStored fields:");
displaySummary(schema.fields, merged);
console.error("");
}
function isResourceNotFound(err) {
const e = err;
return e?.name === "ResourceNotFoundException" || e?.__type === "ResourceNotFoundException" || e?.Code === "ResourceNotFoundException" || e?.code === "ResourceNotFoundException" || typeof e?.message === "string" && e.message.includes("ResourceNotFoundException");
}
function deepMerge(target, source) {
const result = { ...target };
for (const [key, value] of Object.entries(source)) {
if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
result[key] = deepMerge(
result[key],
value
);
} else {
result[key] = value;
}
}
return result;
}
async function writeSecret(client, secretId, values, meta) {
const secretString = JSON.stringify(values);
try {
await client.send(
new PutSecretValueCommand({
SecretId: secretId,
SecretString: secretString
})
);
} catch (err) {
if (isResourceNotFound(err)) {
await client.send(
new CreateSecretCommand({
Name: secretId,
Description: `External service credentials for ${meta.name} (instance)`,
SecretString: secretString,
Tags: [
{ Key: "mesh:type", Value: "external-service" },
{ Key: "mesh:service", Value: meta.name }
]
})
);
} else {
throw err;
}
}
}
function extractNonSecretFields(fields, values) {
const result = {};
for (const [key, entry] of Object.entries(fields)) {
if (entry.type === "group") {
const groupValues = values[key] ?? {};
const filtered = extractNonSecretFields(entry.fields, groupValues);
if (Object.keys(filtered).length > 0) {
result[key] = filtered;
}
} else if (!entry.secret && values[key] !== void 0) {
result[key] = values[key];
}
}
return result;
}
async function addKeyToInstanceIndex(client, secretPrefix, key, serviceName) {
const indexId = instanceIndexSecretId(secretPrefix);
try {
let keys = [];
try {
const res = await client.send(new GetSecretValueCommand6({ SecretId: indexId }));
keys = parseInstanceIndex(res.SecretString ?? "");
} catch (err) {
if (!isResourceNotFound(err)) throw err;
}
if (keys.includes(key)) return;
const secretString = buildInstanceIndexValue([...keys, key]);
try {
await client.send(
new PutSecretValueCommand({ SecretId: indexId, SecretString: secretString })
);
} catch (err) {
if (!isResourceNotFound(err)) throw err;
await client.send(
new CreateSecretCommand({
Name: indexId,
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
SecretString: secretString,
Tags: [
{ Key: "mesh:type", Value: "external-service-index" },
{ Key: "mesh:service", Value: serviceName }
]
})
);
logInfo(
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack (it creates the index) before writing keys, or import this secret into the stack before its next deploy.`
);
}
} catch (err) {
logInfo(
`Warning: failed to record instance "${key}" in ${indexId} \u2014 listInstances() will not see it until you run: mesh secrets reindex external/${serviceName} (${err})`
);
}
}
async function writeConfigMirror(client, secretId, schema, merged, meta) {
const configId = `${secretId}/.config`;
const configValues = extractNonSecretFields(schema.fields, merged);
const secretString = JSON.stringify(configValues);
try {
try {
await client.send(
new PutSecretValueCommand({
SecretId: configId,
SecretString: secretString
})
);
} catch (err) {
if (isResourceNotFound(err)) {
await client.send(
new CreateSecretCommand({
Name: configId,
Description: `Non-secret credential fields for ${meta.name} (Hub display)`,
SecretString: secretString,
Tags: [{ Key: "mesh:type", Value: "external-service-config" }]
})
);
} else {
throw err;
}
}
} catch (err) {
logInfo(`Warning: failed to write .config mirror to ${configId}: ${err}`);
}
}
var init_set = __esm({
"libs/mesh-cli/src/commands/secrets/set.ts"() {
"use strict";
init_src3();
init_context();
init_log();
init_stack_flag();
init_pulumi();
init_aws_auth();
}
});
// libs/mesh-cli/src/commands/secrets/reindex.ts
import { SSMClient as SSMClient3 } from "@aws-sdk/client-ssm";
import {
SecretsManagerClient as SecretsManagerClient7,
ListSecretsCommand,
GetSecretValueCommand as GetSecretValueCommand7,
PutSecretValueCommand as PutSecretValueCommand2,
CreateSecretCommand as CreateSecretCommand2
} from "@aws-sdk/client-secrets-manager";
async function scanInstanceKeys(client, secretPrefix) {
const keys = [];
let nextToken;
do {
const response = await client.send(
new ListSecretsCommand({
Filters: [{ Key: "name", Values: [`${secretPrefix}/`] }],
NextToken: nextToken
})
);
for (const secret of response.SecretList ?? []) {
const key = secret.Name ? instanceKeyFromSecretName(secretPrefix, secret.Name) : null;
if (key) keys.push(key);
}
nextToken = response.NextToken;
} while (nextToken);
return keys;
}
function indexAlreadyMatches(missing, stale, indexExists, malformed) {
return missing.length === 0 && stale.length === 0 && indexExists && !malformed;
}
async function reindexCommand(servicePath, opts) {
const stackOpt = resolveStackOption(opts);
const context = detectContext(stackOpt);
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
const ssmClient = new SSMClient3({ region });
const smClient = new SecretsManagerClient7({ region });
const services = await discoverExternalServices(
ssmClient,
context.tenant,
context.platformEnv
);
const serviceName = servicePath?.replace(/^external\//, "");
if (!serviceName || !services.has(serviceName)) {
if (serviceName) {
logError(`External service "${serviceName}" not found.`);
}
const keyed = [...services.entries()].filter(([, s]) => s.schema?.keyedBy);
console.error("\nMulti-instance external services:");
for (const [name, svc2] of keyed) {
console.error(` ${name} (keyed by ${svc2.schema.keyedBy})`);
}
if (keyed.length === 0) console.error(" (none)");
console.error(`
Usage: mesh secrets reindex external/<name>`);
process.exit(serviceName ? 1 : 0);
}
const svc = services.get(serviceName);
if (!svc.schema?.keyedBy) {
logError(
`"${serviceName}" is single-instance (no keyedBy) \u2014 it has no instance index.`
);
process.exit(1);
}
const secretPrefix = svc.meta.secretPrefix;
const indexId = instanceIndexSecretId(secretPrefix);
const actual = [...new Set(await scanInstanceKeys(smClient, secretPrefix))].sort();
let listed = [];
let indexExists = true;
let malformed = false;
try {
const res = await smClient.send(new GetSecretValueCommand7({ SecretId: indexId }));
try {
listed = parseInstanceIndex(res.SecretString ?? "");
} catch {
malformed = true;
logInfo(`Existing index at ${indexId} is malformed \u2014 rebuilding from scratch.`);
}
} catch (err) {
if (!isResourceNotFound(err)) throw err;
indexExists = false;
logInfo(`No index at ${indexId} yet \u2014 it will be created.`);
}
const missing = actual.filter((k) => !listed.includes(k));
const stale = listed.filter((k) => !actual.includes(k));
logInfo(`Instances in Secrets Manager: ${actual.length ? actual.join(", ") : "(none)"}`);
if (missing.length > 0) logInfo(`Missing from index: ${missing.join(", ")}`);
if (stale.length > 0) logInfo(`Stale in index (secret gone): ${stale.join(", ")}`);
if (indexAlreadyMatches(missing, stale, indexExists, malformed)) {
logSuccess(`Index at ${indexId} already matches \u2014 nothing to do.`);
return;
}
if (opts.dryRun) {
logInfo(`Dry run \u2014 would write ${indexId} with ${actual.length} instance(s).`);
return;
}
await writeInstanceIndex(smClient, secretPrefix, serviceName, actual);
logSuccess(`Index rebuilt: ${indexId} now lists ${actual.length} instance(s).`);
}
async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
const indexId = instanceIndexSecretId(secretPrefix);
const secretString = buildInstanceIndexValue(keys);
try {
await client.send(
new PutSecretValueCommand2({ SecretId: indexId, SecretString: secretString })
);
} catch (err) {
if (!isResourceNotFound(err)) throw err;
await client.send(
new CreateSecretCommand2({
Name: indexId,
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
SecretString: secretString,
Tags: [
{ Key: "mesh:type", Value: "external-service-index" },
{ Key: "mesh:service", Value: serviceName }
]
})
);
logInfo(
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack BEFORE reindex next time, or import this secret into the stack before its next deploy.`
);
}
}
var init_reindex = __esm({
"libs/mesh-cli/src/commands/secrets/reindex.ts"() {
"use strict";
init_src3();
init_context();
init_log();
init_stack_flag();
init_set();
}
});
// libs/mesh-cli/src/commands/secrets/migrate-config.ts
import {
SSMClient as SSMClient4,
GetParametersByPathCommand as GetParametersByPathCommand2
} from "@aws-sdk/client-ssm";
import {
SecretsManagerClient as SecretsManagerClient8,
GetSecretValueCommand as GetSecretValueCommand8,
PutSecretValueCommand as PutSecretValueCommand3,
CreateSecretCommand as CreateSecretCommand3,
ListSecretsCommand as ListSecretsCommand2
} from "@aws-sdk/client-secrets-manager";
function normalizeEntry2(raw) {
if (raw.type === "group" && raw.fields) {
return {
type: "group",
name: raw.name ?? "",
description: raw.description ?? "",
fields: normalizeFields2(raw.fields)
};
}
return {
type: "field",
name: raw.name ?? "",
description: raw.description ?? "",
secret: raw.secret ?? false,
optional: raw.optional ?? false
};
}
function normalizeFields2(raw) {
return Object.fromEntries(
Object.entries(raw).map(([key, value]) => [key, normalizeEntry2(value)])
);
}
async function discoverExternalServices2(ssmClient, tenant, platformEnv) {
const services = /* @__PURE__ */ new Map();
const basePath = `/mesh-platform/${tenant}/${platformEnv}`;
let nextToken;
do {
const response = await ssmClient.send(
new GetParametersByPathCommand2({
Path: basePath,
Recursive: true,
NextToken: nextToken
})
);
for (const param of response.Parameters ?? []) {
if (!param.Name || !param.Value) continue;
const metaMatch = param.Name.match(/\/external-services\/([^/]+)\/meta$/);
if (metaMatch?.[1]) {
const serviceName = metaMatch[1];
const meta = JSON.parse(param.Value);
const existing = services.get(serviceName);
services.set(serviceName, { ...existing, meta });
}
const credMatch = param.Name.match(/\/external-services\/([^/]+)\/credentials$/);
if (credMatch?.[1]) {
const serviceName = credMatch[1];
const raw = JSON.parse(param.Value);
const schema = {
fields: normalizeFields2(raw.fields),
keyedBy: raw.keyedBy
};
const existing = services.get(serviceName);
if (existing) {
existing.schema = schema;
} else {
services.set(serviceName, { meta: {}, schema });
}
}
}
nextToken = response.NextToken;
} while (nextToken);
return services;
}
function extractNonSecretFields2(fields, values) {
const result = {};
for (const [key, entry] of Object.entries(fields)) {
if (entry.type === "group") {
const groupValues = values[key] ?? {};
const filtered = extractNonSecretFields2(entry.fields, groupValues);
if (Object.keys(filtered).length > 0) {
result[key] = filtered;
}
} else if (!entry.secret && values[key] !== void 0) {
result[key] = values[key];
}
}
return result;
}
function isResourceNotFound2(err) {
const e = err;
return e?.name === "ResourceNotFoundException" || e?.__type === "ResourceNotFoundException" || e?.Code === "ResourceNotFoundException" || e?.code === "ResourceNotFoundException" || typeof e?.message === "string" && e.message.includes("ResourceNotFoundException");
}
async function readSecret(client, secretId) {
try {
const res = await client.send(new GetSecretValueCommand8({ SecretId: secretId }));
return res.SecretString ? JSON.parse(res.SecretString) : null;
} catch (err) {
if (isResourceNotFound2(err)) return null;
throw err;
}
}
async function writeSecret2(client, secretId, values, description) {
const secretString = JSON.stringify(values);
try {
await client.send(new PutSecretValueCommand3({ SecretId: secretId, SecretString: secretString }));
} catch (err) {
if (isResourceNotFound2(err)) {
await client.send(new CreateSecretCommand3({
Name: secretId,
Description: description,
SecretString: secretString,
Tags: [{ Key: "mesh:type", Value: "external-service-config" }]
}));
} else {
throw err;
}
}
}
async function secretExists(client, secretId) {
try {
await client.send(new GetSecretValueCommand8({ SecretId: secretId }));
return true;
} catch (err) {
if (isResourceNotFound2(err)) return false;
throw err;
}
}
async function listInstanceKeys(client, secretPrefix) {
const prefix2 = `${secretPrefix}/`;
const keys = [];
let nextToken;
do {
const response = await client.send(
new ListSecretsCommand2({
Filters: [{ Key: "name", Values: [prefix2] }],
NextToken: nextToken
})
);
for (const secret of response.SecretList ?? []) {
if (secret.Name?.startsWith(prefix2)) {
const key = secret.Name.slice(prefix2.length);
if (key && !key.includes("/") && !key.endsWith(".config")) {
keys.push(key);
}
}
}
nextToken = response.NextToken;
} while (nextToken);
return keys;
}
async function migrateService(smClient, meta, schema, opts) {
let migrated = 0;
let skipped = 0;
let errors = 0;
async function migrateOneSecret(secretId, label) {
const configId = `${secretId}/.config`;
try {
if (!opts.force) {
const exists = await secretExists(smClient, configId);
if (exists) {
logInfo(` SKIP ${label} \u2014 .config already exists`);
skipped++;
return;
}
}
const values = await readSecret(smClient, secretId);
if (!values) {
logInfo(` SKIP ${label} \u2014 main secret empty or missing`);
skipped++;
return;
}
const configValues = extractNonSecretFields2(schema.fields, values);
if (opts.dryRun) {
const fieldCount = Object.keys(configValues).length;
logInfo(` DRY-RUN ${label} \u2192 ${configId} (${fieldCount} non-secret fields)`);
migrated++;
return;
}
const desc = `Non-secret credential fields for ${meta.name} (Hub display)`;
await writeSecret2(smClient, configId, configValues, desc);
logSuccess(` OK ${label} \u2192 ${configId}`);
migrated++;
} catch (err) {
logError(` FAIL ${label}: ${err}`);
errors++;
}
}
await migrateOneSecret(meta.secretPrefix, meta.name);
if (schema.keyedBy) {
const keys = await listInstanceKeys(smClient, meta.secretPrefix);
for (const key of keys) {
await migrateOneSecret(`${meta.secretPrefix}/${key}`, `${meta.name}/${key}`);
}
}
return { migrated, skipped, errors };
}
async function migrateConfigCommand(opts) {
const context = detectContext(resolveStackOption(opts));
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
const ssmClient = new SSMClient4({ region });
const smClient = new SecretsManagerClient8({ region });
logInfo(`Migrating .config mirrors for ${context.tenant}/${context.platformEnv}`);
if (opts.dryRun) logInfo("DRY-RUN mode \u2014 no writes");
if (opts.force) logInfo("FORCE mode \u2014 overwrite existing .config");
const services = await discoverExternalServices2(ssmClient, context.tenant, context.platformEnv);
if (services.size === 0) {
logInfo("No external services found.");
return;
}
let totalMigrated = 0;
let totalSkipped = 0;
let totalErrors = 0;
for (const [name, svc] of services) {
if (!svc.schema) {
logInfo(`Skipping ${name}: no credential schema`);
continue;
}
logInfo(`
Migrating: ${name}`);
const result = await migrateService(smClient, svc.meta, svc.schema, {
force: !!opts.force,
dryRun: !!opts.dryRun
});
totalMigrated += result.migrated;
totalSkipped += result.skipped;
totalErrors += result.errors;
}
console.error("");
logInfo(`Done: ${totalMigrated} migrated, ${totalSkipped} skipped, ${totalErrors} errors`);
if (totalErrors > 0) {
process.exit(1);
}
}
var init_migrate_config = __esm({
"libs/mesh-cli/src/commands/secrets/migrate-config.ts"() {
"use strict";
init_context();
init_log();
init_stack_flag();
}
});
// libs/mesh-cli/src/commands/secrets/index.ts
import { Option as Option2 } from "commander";
function registerSecretsCommands(program2) {
const secrets = program2.command("secrets").description("AWS Secrets Manager utilities");
secrets.command("exec").description("Run command with secrets as environment variables").allowUnknownOption(true).action(async (_opts, cmd) => {
const args = cmd.args;
await execCommand2(args);
});
secrets.command("set [service]").description("Set credentials for an external service (e.g., mesh secrets set external/symitar)").option("--key <key>", "Instance key for multi-instance services (e.g., FI ID)").option("--all", "Prompt for all fields including optional ones").option("--json <json>", "Non-interactive: provide values as JSON").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
await setCommand(service, opts);
});
secrets.command("reindex [service]").description(
"Rebuild a multi-instance external service's instance index (the {prefix}/.index secret listInstances() reads) from the per-key secrets actually in Secrets Manager \u2014 the repair for an instance secret created or deleted out of band"
).option("--dry-run", "Show what would change without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
await reindexCommand(service, opts);
});
secrets.command("migrate-config").description("Backfill .config mirror secrets for all external services").option("--force", "Overwrite existing .config mirrors").option("--dry-run", "Show what would be written without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (opts) => {
await migrateConfigCommand(opts);
});
}
var init_secrets = __esm({
"libs/mesh-cli/src/commands/secrets/index.ts"() {
"use strict";
init_exec2();
init_set();
init_reindex();
init_migrate_config();
}
});
// libs/mesh-cli/src/commands/site.ts
import { createHash as createHash4 } from "node:crypto";
import { createReadStream } from "node:fs";
import { readdir, readFile as readFile2, stat } from "node:fs/promises";
import { join as join37, relative as relative9, resolve as resolve16, sep as sep6 } from "node:path";
async function describeSiteHttpError(res, target, site) {
if (res.status === 403) {
return `not allowed to publish "${site}" \u2014 you are authenticated but lack the grant. Need one of: sites:${site}:publish, sites:*:publish, or studio-admin.`;
}
if (res.status === 404) {
return `no such site or version for "${site}" \u2014 check \`mesh site versions ${site}\`.`;
}
return describeHttpError(res, target, { id: site });
}
async function hashFile(absolute) {
const h = createHash4("sha256");
await new Promise((res, rej) => {
createReadStream(absolute).on("data", (chunk) => h.update(chunk)).on("end", () => res()).on("error", rej);
});
return h.digest("hex");
}
async function walk(root) {
const out = [];
async function visit(dir) {
for (const entry of await readdir(dir, { withFileTypes: true })) {
if (SKIP_ENTRIES.has(entry.name)) continue;
const absolute = join37(dir, entry.name);
if (entry.isSymbolicLink()) {
logWarn(`skipping symlink ${relative9(root, absolute)}`);
continue;
}
if (entry.isDirectory()) {
await visit(absolute);
continue;
}
if (!entry.isFile()) continue;
const info = await stat(absolute);
out.push({
// Manifest paths are always forward-slashed, whatever the host OS uses,
// so a site published from Windows resolves identically to one from a Mac.
path: relative9(root, absolute).split(sep6).join("/"),
absolute,
hash: await hashFile(absolute),
size: info.size
});
}
}
await visit(root);
return out.sort((a, b) => a.path.localeCompare(b.path));
}
function human(bytes) {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} kB`;
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
}
async function pooled(items, limit, worker) {
let next = 0;
const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {
for (; ; ) {
const index = next;
next += 1;
if (index >= items.length) return;
await worker(items[index]);
}
});
await Promise.all(runners);
}
async function publish(name, dir, opts, target) {
const root = resolve16(dir);
const info = await stat(root).catch(() => null);
if (!info?.isDirectory()) throw new Error(`${root} is not a directory`);
logInfo(`Hashing ${root}\u2026`);
const files = await walk(root);
if (files.length === 0) throw new Error(`${root} contains no files`);
const total = files.reduce((n, f) => n + f.size, 0);
logInfo(`${files.length} files, ${human(total)}`);
if (!files.some((f) => f.path === "index.html")) {
logWarn("no index.html at the root \u2014 deep links and the site root will 404");
}
const unique = [...new Set(files.map((f) => f.hash))];
const checkRes = await agentApiSend(target, `/v1/sites/${encodeURIComponent(name)}/blobs/check`, {
method: "POST",
json: { hashes: unique }
});
if (!checkRes.ok) throw new Error(await describeSiteHttpError(checkRes, target, name));
const { missing } = await checkRes.json();
const missingSet = new Set(missing);
const toUpload = files.filter((f) => missingSet.has(f.hash));
const seen = /* @__PURE__ */ new Set();
const uploads = toUpload.filter((f) => seen.has(f.hash) ? false : (seen.add(f.hash), true));
const uploadBytes = uploads.reduce((n, f) => n + f.size, 0);
const reused = files.length - uploads.length;
logInfo(
`${uploads.length} to upload (${human(uploadBytes)}); ${reused} already present (${human(total - uploadBytes)})`
);
let done = 0;
await pooled(uploads, UPLOAD_CONCURRENCY, async (file) => {
const body = await readFile2(file.absolute);
const res = await agentApiSend(
target,
`/v1/sites/${encodeURIComponent(name)}/blobs/${file.hash}`,
{ method: "PUT", body, contentType: "application/octet-stream" }
);
if (!res.ok) {
throw new Error(
`upload failed for ${file.path} (${human(file.size)}): ` + await describeSiteHttpError(res, target, name)
);
}
done += 1;
logInfo(` [${done}/${uploads.length}] ${file.path} (${human(file.size)})`);
});
const fileMap = Object.fromEntries(files.map((f) => [f.path, f.hash]));
const commitRes = await agentApiSend(target, `/v1/sites/${encodeURIComponent(name)}/versions`, {
method: "POST",
json: { ...opts.version ? { version: opts.version } : {}, files: fileMap, activate: opts.activate }
});
if (!commitRes.ok) {
if (commitRes.status === 409) {
const body = await commitRes.json().catch(() => ({}));
throw new Error(
`commit rejected: ${body.missing?.length ?? "some"} blobs are missing server-side. Re-run to upload them.`
);
}
throw new Error(await describeSiteHttpError(commitRes, target, name));
}
const { version, activated } = await commitRes.json();
logSuccess(`Published ${name} version ${version}`);
if (activated) {
logInfo(`Live at /sites/${name}/`);
} else {
logInfo(`Not activated. Serve it with: mesh site rollback ${name} ${version}`);
logInfo(`Or preview it at /sites/${name}/@${version}/`);
}
}
async function listVersions(name, target) {
const res = await agentApiFetch(target, `/v1/sites/${encodeURIComponent(name)}/versions`);
if (!res.ok) throw new Error(await describeSiteHttpError(res, target, name));
const { versions } = await res.json();
if (versions.length === 0) {
logInfo(`No versions published for "${name}".`);
return;
}
for (const v of versions) {
const marker = v.current ? "*" : " ";
logInfo(`${marker} ${v.version} ${v.createdAt} ${String(v.fileCount).padStart(5)} files ${v.publisher}`);
}
logInfo("");
logInfo("* = currently served");
}
async function rollback(name, version, target) {
const res = await agentApiSend(target, `/v1/sites/${encodeURIComponent(name)}/rollback`, {
method: "POST",
json: { version }
});
if (!res.ok) throw new Error(await describeSiteHttpError(res, target, name));
logSuccess(`${name} now serves ${version}`);
}
async function run(opts, body) {
let target;
try {
target = resolveTarget(opts);
} catch (error) {
logError(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
return;
}
try {
await body(target);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logError(message);
const hint = describeNetworkError(error, target);
if (hint !== message) logError(hint);
process.exitCode = 1;
}
}
function registerSiteCommands(program2) {
const site = program2.command("site").description("Publish and manage OAuth-protected static sites served by Studio");
targetOptions(
site.command("publish <name> <dir>").description(
"Publish a built directory as a new version\n\nUploads only files the server does not already hold, so a rebuild moves\nonly what changed."
).option(
"--version-id <id>",
"Version id (default: a UTC timestamp; CI usually passes a git sha)"
).option("--no-activate", "Publish without pointing the site at it")
).action(
async (name, dir, opts) => {
await run(
opts,
(target) => publish(name, dir, { version: opts.versionId, activate: opts.activate }, target)
);
}
);
targetOptions(
site.command("versions <name>").description("List published versions, newest first")
).action(async (name, opts) => {
await run(opts, (target) => listVersions(name, target));
});
targetOptions(
site.command("rollback <name> <version>").description("Point the site at an already-published version")
).action(
async (name, version, opts) => {
await run(opts, (target) => rollback(name, version, target));
}
);
}
var DEFAULT_AUTH_CONTEXT3, UPLOAD_CONCURRENCY, SKIP_ENTRIES, targetOptions;
var init_site = __esm({
"libs/mesh-cli/src/commands/site.ts"() {
"use strict";
init_log();
init_agent_api_client();
DEFAULT_AUTH_CONTEXT3 = "mesh.dev";
UPLOAD_CONCURRENCY = 4;
SKIP_ENTRIES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".git"]);
targetOptions = (cmd) => cmd.option("--target <name>", "Named agent target from the agent-targets registry").option("--api-url <url>", "Agent API URL (overrides --target; ad-hoc, no registry lookup)").option("--context <ctx>", `Zitadel auth context, used with --api-url (default: ${DEFAULT_AUTH_CONTEXT3})`);
}
});
// libs/mesh-cli/src/commands/stack.ts
import { execFileSync as execFileSync29 } from "child_process";
import * as path42 from "path";
import * as fs35 from "fs";
import { parse as parseYaml5 } from "yaml";
function readTopLevelYamlKey(appRoot, stack, key) {
const configFile = path42.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs35.existsSync(configFile)) return null;
const content = fs35.readFileSync(configFile, "utf-8");
const pattern = new RegExp(`^${key}:\\s*(.+)$`, "m");
const match = content.match(pattern);
if (!match) return null;
return match[1].trim().replace(/^["']|["']$/g, "");
}
function readConfigBlockKey(appRoot, stack, key) {
const configFile = path42.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs35.existsSync(configFile)) return null;
const content = fs35.readFileSync(configFile, "utf-8");
const pattern = new RegExp(`^\\s{2}${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
const match = content.match(pattern);
if (!match) return null;
return match[1].trim().replace(/^["']|["']$/g, "");
}
async function checkParentZoneDelegation(parentZone, awsEnv) {
let publicNs = [];
try {
const { Resolver } = await import("node:dns/promises");
const resolver = new Resolver();
resolver.setServers(["1.1.1.1", "8.8.8.8"]);
publicNs = (await resolver.resolveNs(parentZone)).map((n) => n.replace(/\.$/, "").toLowerCase());
} catch {
}
let zoneNs = [];
try {
const { Route53Client, ListHostedZonesByNameCommand, GetHostedZoneCommand } = await import("@aws-sdk/client-route-53");
const client = new Route53Client({
credentials: awsEnv.AWS_ACCESS_KEY_ID ? {
accessKeyId: awsEnv.AWS_ACCESS_KEY_ID,
secretAccessKey: awsEnv.AWS_SECRET_ACCESS_KEY ?? "",
sessionToken: awsEnv.AWS_SESSION_TOKEN
} : void 0
});
const byName = await client.send(
new ListHostedZonesByNameCommand({ DNSName: parentZone, MaxItems: 1 })
);
const zone = byName.HostedZones?.[0];
if (zone?.Id && zone.Name === `${parentZone}.`) {
const detail = await client.send(new GetHostedZoneCommand({ Id: zone.Id }));
zoneNs = (detail.DelegationSet?.NameServers ?? []).map((n) => n.toLowerCase());
} else {
logWarn(
`Parent zone '${parentZone}' does not exist in Route53 yet \u2014 the platform deploy expects it (it hosts the env-zone delegations). Create it first (one-time, operator step).`
);
return;
}
} catch {
}
if (publicNs.length === 0) {
const records = zoneNs.length > 0 ? zoneNs.map((ns) => ` ${parentZone.split(".")[0]} NS ${ns}`).join("\n") : ` (run this command again with AWS credentials to print the exact nameservers)`;
logWarn(
`Parent zone '${parentZone}' has NO public NS delegation \u2014 hub/identity/temporal hostnames for this stack will NOT resolve and ACM certs stay pending until it lands.
One-time step: at the DNS host for '${parentZone.split(".").slice(1).join(".")}' (e.g. Cloudflare), add:
${records}
The deploy itself can proceed now (certs validate automatically once delegation propagates).`
);
} else if (zoneNs.length > 0 && !publicNs.some((ns) => zoneNs.includes(ns))) {
logWarn(
`Parent zone '${parentZone}' is publicly delegated to DIFFERENT nameservers than the Route53 zone in this account:
public: ${publicNs.join(", ")}
zone: ${zoneNs.join(", ")}
The env zone would be created in a zone the internet never consults \u2014 fix the delegation before deploying.`
);
} else {
logSuccess(`Parent zone delegation OK: ${parentZone} resolves publicly.`);
}
}
function readBaseConfigFromYaml(appRoot, stack) {
const file = path42.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs35.existsSync(file)) return {};
let doc;
try {
doc = parseYaml5(fs35.readFileSync(file, "utf-8"));
} catch {
return {};
}
const cfg = doc?.config ?? {};
const out = {};
for (const [key, val] of Object.entries(cfg)) {
out[key] = val !== null && typeof val === "object" ? { value: "", objectValue: val } : { value: String(val) };
}
return out;
}
function getGitHubUsername() {
try {
const result = execFileSync29("gh", ["api", "user", "--jq", ".login"], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
});
const login2 = result.trim();
if (login2) return login2;
} catch {
}
try {
const result = execFileSync29("git", ["config", "user.email"], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"]
});
const email = result.trim();
const match = email.match(/^(\d+\+)?([^@]+)@/);
if (match) return match[2];
} catch {
}
return null;
}
function registerStackCommand(program2) {
const stack = program2.command("stack").description("Manage personal Pulumi stacks");
stack.command("init").description("Create a personal dev stack (dev-{github-username})").option("--from <stack>", "Base stack to copy config from").option("--name <name>", "Override stack name (default: dev-{username})").option("--adopt", "resume an EXISTING stack with this name (skips the availability check)", false).option(
"--worktree",
"Isolate this stack per git worktree \u2014 append the worktree token to the name so concurrent worktrees deploy to distinct stacks (no SSM/namespace collisions)"
).option(
"--platform",
"Personal PLATFORM stack: a fully isolated platform env (Zitadel, Hub, Temporal, tenant envs) on the SHARED core \u2014 sets mesh:coreEnv to the base env, mesh:clusterOwner false, disables org-wide singletons (package registry, VPN), and keeps deploy ON. Run from a platform Pulumi program.",
false
).option(
"--parent-zone <zone>",
"With --platform: parent DNS zone for the env zone (mesh:dns.public.parentZone) \u2014 platform hostnames become {sub}.{env}.{parentZone} (e.g. hub.<env>.<parentZone>), so personal stacks never collide on DNS"
).action(async (opts) => {
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
logError("No Pulumi.yaml found. Run from within a Pulumi app directory.");
process.exit(1);
}
const username = getGitHubUsername();
if (!username) {
logError(
"Could not determine GitHub username.\nInstall gh CLI (https://cli.github.com) and run: gh auth login"
);
process.exit(1);
}
let baseStack = opts.from;
if (!baseStack) {
const stacks = findStackConfigs(appRoot);
if (stacks.includes("dev")) {
baseStack = "dev";
} else if (stacks.length === 1) {
baseStack = stacks[0];
} else if (stacks.length > 1) {
logError(
`Multiple stacks found: ${stacks.join(", ")}
Specify which to base on: mesh stack init --from <stack>`
);
process.exit(1);
} else {
logError("No stack configs found (Pulumi.<stack>.yaml).");
process.exit(1);
}
}
const baseConfigPath = `${appRoot}/Pulumi.${baseStack}.yaml`;
if (!fs35.existsSync(baseConfigPath)) {
logError(`Stack config not found: Pulumi.${baseStack}.yaml`);
process.exit(1);
}
const baseTenant = readConfigBlockKey(appRoot, baseStack, "mesh:tenant");
if (opts.platform && !baseTenant) {
logError(`--platform: could not read mesh:tenant from Pulumi.${baseStack}.yaml \u2014 is this a platform program?`);
process.exit(1);
}
const baseName = opts.name ?? (opts.platform ? `${baseTenant}-${username}-dev` : `dev-${username}`);
let newStack = baseName;
if (opts.worktree) {
const wt = resolveWorktreeIdentity(appRoot);
newStack = worktreeStackName(baseName, wt);
if (wt.token) {
logInfo(`--worktree: isolating as "${newStack}" (linked worktree "${wt.slug}")`);
} else {
logInfo(
`--worktree: primary checkout has no worktree token \u2014 using "${baseName}" (isolation not needed here).`
);
}
}
const newConfigPath = path42.join(appRoot, `Pulumi.${newStack}.yaml`);
const configExists = fs35.existsSync(newConfigPath);
const secretsProvider = readTopLevelYamlKey(appRoot, baseStack, "secretsprovider");
const credEnv = await resolvePulumiEnv({ appRoot, stack: baseStack });
const pulumiEnv = { ...process.env, ...credEnv };
delete pulumiEnv.AWS_PROFILE;
if (!opts.adopt) {
let existing = [];
try {
const raw = execFileSync29("pulumi", ["stack", "ls", "--json"], {
cwd: appRoot,
encoding: "utf-8",
env: pulumiEnv,
stdio: ["pipe", "pipe", "pipe"]
});
existing = JSON.parse(raw).map((s) => s.name);
} catch {
}
if (existing.includes(newStack) || configExists) {
logError(
`Personal stack name '${newStack}' is not available` + (configExists ? ` (Pulumi.${newStack}.yaml already exists here)` : " (a stack with this name exists in the backend)") + "."
);
logInfo(` Pick another name: mesh stack init --name ${newStack}-2`);
logInfo(` Resume YOUR OWN stack: mesh stack init --name ${newStack} --adopt`);
process.exit(1);
}
}
const initArgs = ["stack", "init", newStack];
if (secretsProvider) {
initArgs.push("--secrets-provider", secretsProvider);
logInfo(`Using KMS secrets provider: ${secretsProvider}`);
}
try {
execFileSync29("pulumi", initArgs, {
cwd: appRoot,
env: pulumiEnv,
stdio: "inherit"
});
logSuccess(`Stack initialized: ${newStack}${secretsProvider ? " (KMS secrets)" : ""}`);
} catch (err) {
if (err.stderr?.includes("already exists") && opts.adopt) {
logWarn(`Stack ${newStack} already exists \u2014 adopting (--adopt).`);
} else if (err.stderr?.includes("already exists")) {
logError(`Stack ${newStack} already exists. Re-run with --adopt if it is yours.`);
process.exit(1);
} else {
process.exit(err.status ?? 1);
}
}
try {
execFileSync29("pulumi", ["stack", "select", newStack], {
cwd: appRoot,
env: pulumiEnv,
stdio: ["pipe", "pipe", "pipe"]
});
} catch {
}
if (!configExists) {
let baseConfig = {};
try {
const raw = execFileSync29(
"pulumi",
["config", "--json", "--stack", baseStack],
{ cwd: appRoot, env: pulumiEnv, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
);
baseConfig = JSON.parse(raw);
} catch {
baseConfig = readBaseConfigFromYaml(appRoot, baseStack);
if (Object.keys(baseConfig).length === 0) {
logWarn(
`Could not read base config from the backend or Pulumi.${baseStack}.yaml \u2014 the new stack will need manual config.`
);
}
}
for (const [key, entry] of Object.entries(baseConfig)) {
if (key === "mesh:deploy") continue;
try {
if (entry.objectValue !== void 0) {
execFileSync29(
"pulumi",
["config", "set", key, JSON.stringify(entry.objectValue)],
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
);
} else if (entry.value === "true" || entry.value === "false") {
execFileSync29(
"pulumi",
["config", "set", "--type", "bool", key, entry.value],
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
);
} else {
execFileSync29(
"pulumi",
["config", "set", key, entry.value],
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
);
}
} catch {
}
}
if (opts.platform) {
const baseEnv = readConfigBlockKey(appRoot, baseStack, "mesh:coreEnv") ?? (baseTenant && baseStack.startsWith(`${baseTenant}-`) ? baseStack.slice(baseTenant.length + 1) : baseStack);
const setCfg = (args) => {
try {
execFileSync29("pulumi", ["config", "set", ...args], {
cwd: appRoot,
env: pulumiEnv,
stdio: ["pipe", "pipe", "pipe"]
});
} catch {
}
};
setCfg(["mesh:coreEnv", baseEnv]);
setCfg(["--type", "bool", "mesh:clusterOwner", "false"]);
setCfg(["--type", "bool", "mesh:packageRegistry", "false"]);
setCfg(["--type", "bool", "mesh:headscale", "false"]);
setCfg(["--type", "bool", "mesh:subnetRouter", "false"]);
setCfg(["--type", "bool", "mesh:devbox", "false"]);
setCfg(["--type", "bool", "mesh:stableUrl", "true"]);
try {
const tenantsRaw = readConfigBlockKey(appRoot, newStack, "mesh:tenants");
if (tenantsRaw) {
const tenants = JSON.parse(tenantsRaw);
const envSegment = baseTenant && newStack.startsWith(`${baseTenant}-`) ? newStack.slice(baseTenant.length + 1) : newStack;
for (const [tName, tCfg] of Object.entries(tenants)) {
if (!tCfg.pulumiStateBucket) {
tCfg.pulumiStateBucket = `${baseTenant}-tenant-${tName}-${envSegment}-pulumi-state`;
}
}
setCfg(["mesh:tenants", JSON.stringify(tenants)]);
}
} catch {
logWarn("Could not env-scope tenant pulumiStateBucket overrides \u2014 check mesh:tenants by hand (bucket names collide across envs otherwise).");
}
if (opts.parentZone) {
setCfg([
"mesh:dns",
JSON.stringify({
public: { parentZone: opts.parentZone, waitForValidation: false }
})
]);
} else {
logWarn(
`No --parent-zone given: platform hostnames will derive FLAT from the core zone and can collide with the primary platform. Set it now or later with: mesh deploy config set mesh:dns '{"public":{"parentZone":"<zone>"}}'`
);
}
if (opts.parentZone) {
await checkParentZoneDelegation(opts.parentZone, pulumiEnv);
}
logSuccess(
`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}` : ""})`
);
} else {
execFileSync29("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
cwd: appRoot,
env: pulumiEnv,
stdio: ["pipe", "pipe", "pipe"]
});
logSuccess(`Configured Pulumi.${newStack}.yaml (based on ${baseStack}, deploy: false)`);
}
}
console.log("");
logInfo(opts.platform ? `Your personal platform stack is ready.` : `Your personal stack is ready.`);
logInfo(`Run: mesh deploy up --stack ${newStack} --yes`);
logInfo(
opts.platform ? `Then point app stacks at it: mesh:platformEnv ${newStack.startsWith(`${baseTenant}-`) ? newStack.slice(baseTenant.length + 1) : newStack}` : `Then: mesh dev --stage ${newStack}`
);
});
stack.command("rm <name>").description("Remove a personal Pulumi stack (backend state + local config)").option("--yes", "Skip the confirmation prompt").action(async (name, opts) => {
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
logError("No Pulumi.yaml found. Run from within a Pulumi app directory.");
process.exit(1);
}
const credStack = readStackConfig(appRoot, name, "mesh:deployerRole") ? name : [getCurrentStack(appRoot), "dev", ...findStackConfigs(appRoot)].find(
(s) => !!s && s !== name && !!readStackConfig(appRoot, s, "mesh:deployerRole")
) ?? name;
const credEnv = await resolvePulumiEnv({ appRoot, stack: credStack });
const pulumiEnv = { ...process.env, ...credEnv };
delete pulumiEnv.AWS_PROFILE;
const args = ["stack", "rm", name];
if (opts.yes) args.push("--yes");
try {
execFileSync29("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
logSuccess(`Removed stack ${name}`);
} catch (err) {
process.exit(err.status ?? 1);
}
});
}
var init_stack2 = __esm({
"libs/mesh-cli/src/commands/stack.ts"() {
"use strict";
init_log();
init_pulumi();
init_worktree_identity();
init_pulumi_run();
}
});
// libs/mesh-cli/src/utils/recover-conversation.ts
async function classifyHistoryEvents(rawEvents, decodePayload) {
const activityTypeByScheduledId = /* @__PURE__ */ new Map();
const events = [];
for (const event of rawEvents) {
const eventId = String(event.eventId);
const scheduled = event.activityTaskScheduledEventAttributes;
if (scheduled) {
const name = scheduled.activityType?.name;
if (name) activityTypeByScheduledId.set(eventId, name);
continue;
}
const signal = event.workflowExecutionSignaledEventAttributes;
if (signal?.signalName === "submit_user_message") {
events.push({
eventId,
kind: "userSignal",
payload: await decodePayload(signal.input?.payloads)
});
continue;
}
const completed = event.activityTaskCompletedEventAttributes;
if (completed) {
const scheduledId = String(completed.scheduledEventId);
if (activityTypeByScheduledId.get(scheduledId) === "callLLM") {
events.push({
eventId,
kind: "callLLMResult",
payload: await decodePayload(completed.result?.payloads)
});
}
}
}
return events;
}
async function extractSnapshotMessages(rawEvents, decodePayload) {
let lastCallLLMScheduledId;
let lastCallLLMInputPayloads;
const activityTypeByScheduledId = /* @__PURE__ */ new Map();
let lastCallLLMResultPayloads;
for (const event of rawEvents) {
const eventId = String(event.eventId);
const scheduled = event.activityTaskScheduledEventAttributes;
if (scheduled) {
const name = scheduled.activityType?.name;
if (name) activityTypeByScheduledId.set(eventId, name);
if (name === "callLLM") {
lastCallLLMScheduledId = eventId;
lastCallLLMInputPayloads = scheduled.input?.payloads;
lastCallLLMResultPayloads = void 0;
}
continue;
}
const completed = event.activityTaskCompletedEventAttributes;
if (completed) {
const scheduledId = String(completed.scheduledEventId);
if (activityTypeByScheduledId.get(scheduledId) === "callLLM") {
lastCallLLMResultPayloads = completed.result?.payloads;
}
}
}
if (lastCallLLMScheduledId === void 0) return [];
const inputArgs = await decodePayload(lastCallLLMInputPayloads);
const inputMessages = messagesFrom(inputArgs);
const result = await decodePayload(lastCallLLMResultPayloads);
const resultMessages = messagesFrom(result);
return [...inputMessages, ...resultMessages];
}
function messagesFrom(payload) {
if (isRecord(payload) && Array.isArray(payload.messages)) {
return payload.messages;
}
return [];
}
function extractText(content) {
if (typeof content === "string") return content;
if (Array.isArray(content)) {
return content.filter(
(p) => !!p && typeof p === "object" && p.type === "text"
).map((p) => typeof p.text === "string" ? p.text : "").join("");
}
return "";
}
function summarizeArgs(args) {
try {
const str = JSON.stringify(args ?? {});
return str.length > 100 ? str.substring(0, 97) + "..." : str;
} catch {
return "...";
}
}
function formatToolCall(part) {
const name = typeof part.toolName === "string" ? part.toolName : "?";
const args = "input" in part ? part.input : part.args;
return `${name}(${summarizeArgs(args)})`;
}
function isRecord(v) {
return !!v && typeof v === "object";
}
function extractUserText(payload) {
if (!isRecord(payload)) return null;
const messages = payload.messages;
if (Array.isArray(messages)) {
const parts = [];
for (const m of messages) {
if (isRecord(m) && m.role === "user") {
const t = extractText(m.content);
if (t) parts.push(t);
}
}
if (parts.length > 0) return parts.join("\n");
}
for (const field of ["content", "text", "message"]) {
const v = payload[field];
if (typeof v === "string" && v) return v;
const t = extractText(v);
if (t) return t;
}
return null;
}
function extractAssistantTurn(payload) {
if (!isRecord(payload)) return null;
const messages = payload.messages;
if (!Array.isArray(messages)) {
const t = extractText(payload.content) || extractText(payload.text);
return t ? { text: t, toolCalls: [] } : null;
}
let text = "";
const toolCalls = [];
for (const m of messages) {
if (!isRecord(m) || m.role !== "assistant") continue;
text += extractText(m.content);
if (Array.isArray(m.content)) {
for (const p of m.content) {
if (isRecord(p) && p.type === "tool-call") {
toolCalls.push(formatToolCall(p));
}
}
}
}
if (!text && toolCalls.length === 0) return null;
return { text, toolCalls };
}
function reconstructTranscript(events) {
const turns = [];
for (const event of events) {
if (event.kind === "userSignal") {
const text = extractUserText(event.payload);
turns.push(
text !== null ? { role: "user", text, eventId: event.eventId } : { role: "user", text: placeholder(event.eventId), eventId: event.eventId }
);
} else if (event.kind === "callLLMResult") {
const turn = extractAssistantTurn(event.payload);
if (turn) {
turns.push({
role: "assistant",
text: turn.text,
eventId: event.eventId,
...turn.toolCalls.length > 0 ? { toolCalls: turn.toolCalls } : {}
});
} else {
turns.push({
role: "assistant",
text: placeholder(event.eventId),
eventId: event.eventId
});
}
}
}
return turns;
}
function placeholder(eventId) {
return `_[unparseable turn: ${eventId}]_`;
}
function renderTranscriptMarkdown(turns) {
const blocks = [];
for (const turn of turns) {
const heading = turn.role === "user" ? "## User" : "## Assistant";
const lines = [heading, ""];
if (turn.text) lines.push(turn.text);
if (turn.toolCalls && turn.toolCalls.length > 0) {
if (turn.text) lines.push("");
for (const tc of turn.toolCalls) lines.push(`\u2192 tool: ${tc}`);
}
blocks.push(lines.join("\n"));
}
return blocks.join("\n\n") + "\n";
}
var init_recover_conversation = __esm({
"libs/mesh-cli/src/utils/recover-conversation.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/utils/temporal-codec.ts
import { execFileSync as execFileSync30 } from "child_process";
import { webcrypto as crypto4 } from "node:crypto";
function resolveTemporalEncodingKeyFromK8s(namespace) {
const secretName = `${namespace}-temporal-encoding-key`;
try {
const b64 = execFileSync30(
"kubectl",
[
"get",
"secret",
secretName,
"-n",
namespace,
"-o",
"jsonpath={.data.TEMPORAL_ENCODING_KEY}"
],
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
).trim();
if (!b64) return void 0;
return Buffer.from(b64, "base64").toString("utf-8");
} catch {
return void 0;
}
}
async function deriveKey(encodingKey) {
const keyData = await crypto4.subtle.digest(
"SHA-256",
new TextEncoder().encode(encodingKey)
);
return crypto4.subtle.importKey(
"raw",
keyData,
{ name: "AES-GCM" },
false,
["decrypt"]
);
}
async function aesGcmDecrypt(data, key) {
const iv = data.slice(0, IV_LENGTH);
const ciphertextWithTag = data.slice(IV_LENGTH);
const decrypted = await crypto4.subtle.decrypt(
{ name: "AES-GCM", iv },
key,
ciphertextWithTag
);
return new Uint8Array(decrypted);
}
async function createPayloadDecrypter(encodingKey) {
const key = await deriveKey(encodingKey);
return async (payload) => {
if (!payload?.metadata) return null;
const encodingBytes = payload.metadata["encoding"];
if (!encodingBytes) return null;
const encoding = new TextDecoder().decode(encodingBytes);
if (encoding !== ENCRYPTED_ENCODING) return null;
if (!payload.data) return "[encrypted payload missing data]";
let inner;
try {
const decrypted = await aesGcmDecrypt(payload.data, key);
inner = decodePayloadProtobuf(decrypted);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
return `[undecryptable: ${msg}]`;
}
return decodeInnerPayload(inner);
};
}
async function createRawPayloadDecrypter(encodingKey) {
const key = await deriveKey(encodingKey);
return async (payload) => {
if (!payload?.metadata) return null;
const encodingBytes = payload.metadata["encoding"];
if (!encodingBytes) return null;
const encoding = new TextDecoder().decode(encodingBytes);
if (encoding !== ENCRYPTED_ENCODING) return null;
if (!payload.data) {
throw new Error("encrypted payload has no data to decrypt");
}
const decrypted = await aesGcmDecrypt(payload.data, key);
return decodePayloadProtobuf(decrypted);
};
}
function readVarint(buf, pos) {
let value = 0;
let shift = 0;
let next = pos;
while (next < buf.length) {
const byte = buf[next];
next += 1;
value |= (byte & 127) << shift;
if ((byte & 128) === 0) return { value, next };
shift += 7;
if (shift >= 32) throw new Error("varint too large");
}
throw new Error("truncated varint");
}
function readLengthDelimited(buf, pos) {
const { value: length, next: afterLen } = readVarint(buf, pos);
const end = afterLen + length;
if (end > buf.length) throw new Error("truncated length-delimited field");
return { bytes: buf.slice(afterLen, end), next: end };
}
function decodePayloadProtobuf(buf) {
const metadata = {};
let data;
let pos = 0;
while (pos < buf.length) {
const { value: tag, next: afterTag } = readVarint(buf, pos);
const fieldNumber = tag >>> 3;
const wireType = tag & 7;
pos = afterTag;
if (wireType === 2) {
const { bytes, next } = readLengthDelimited(buf, pos);
pos = next;
if (fieldNumber === 1) {
const entry = decodeMetadataEntry(bytes);
if (entry) metadata[entry.key] = entry.value;
} else if (fieldNumber === 2) {
data = bytes;
}
} else if (wireType === 0) {
pos = readVarint(buf, pos).next;
} else if (wireType === 1) {
pos += 8;
} else if (wireType === 5) {
pos += 4;
} else {
throw new Error(`unsupported wire type ${wireType} at pos ${pos}`);
}
}
return { metadata, data: data ?? new Uint8Array(0) };
}
function decodeMetadataEntry(buf) {
let key;
let value;
let pos = 0;
while (pos < buf.length) {
const { value: tag, next: afterTag } = readVarint(buf, pos);
const fieldNumber = tag >>> 3;
const wireType = tag & 7;
pos = afterTag;
if (wireType !== 2) {
if (wireType === 0) pos = readVarint(buf, pos).next;
else if (wireType === 1) pos += 8;
else if (wireType === 5) pos += 4;
else throw new Error(`unsupported metadata wire type ${wireType}`);
continue;
}
const { bytes, next } = readLengthDelimited(buf, pos);
pos = next;
if (fieldNumber === 1) key = new TextDecoder().decode(bytes);
else if (fieldNumber === 2) value = bytes;
}
if (key === void 0 || value === void 0) return null;
return { key, value };
}
function decodeInnerPayload(payload) {
const meta = payload.metadata ?? {};
const encodingBytes = meta["encoding"];
const encoding = encodingBytes ? new TextDecoder().decode(encodingBytes) : "unknown";
const data = payload.data ?? new Uint8Array(0);
if (encoding === "binary/null") return "null";
if (encoding === "json/plain") {
const text = new TextDecoder().decode(data);
try {
return JSON.stringify(JSON.parse(text), null, 2);
} catch {
return text;
}
}
if (encoding === "binary/plain") return `[binary, ${data.length} bytes]`;
return `[encoding=${encoding}, ${data.length} bytes]`;
}
function resolveEncodingKey(namespace) {
const fromK8s = resolveTemporalEncodingKeyFromK8s(namespace);
const key = fromK8s ?? process.env.TEMPORAL_ENCODING_KEY;
if (!key) {
logWarn(
`Could not read TEMPORAL_ENCODING_KEY from secret ${namespace}/${namespace}-temporal-encoding-key, and TEMPORAL_ENCODING_KEY is not set. Configure kubectl (namespace read access), or export TEMPORAL_ENCODING_KEY (e.g. from a \`mesh dev\` worker env).`
);
return void 0;
}
logSuccess(
fromK8s ? `Resolved encoding key from ${namespace}-temporal-encoding-key` : "Resolved encoding key from TEMPORAL_ENCODING_KEY env"
);
return key;
}
async function buildDecrypterForNamespace(namespace) {
const key = resolveEncodingKey(namespace);
if (!key) return void 0;
return createPayloadDecrypter(key);
}
async function buildRawDecrypterForNamespace(namespace) {
const key = resolveEncodingKey(namespace);
if (!key) return void 0;
return createRawPayloadDecrypter(key);
}
var ENCRYPTED_ENCODING, IV_LENGTH;
var init_temporal_codec = __esm({
"libs/mesh-cli/src/utils/temporal-codec.ts"() {
"use strict";
init_log();
ENCRYPTED_ENCODING = "binary/encrypted";
IV_LENGTH = 12;
}
});
// libs/mesh-cli/src/utils/capture-history.ts
var capture_history_exports = {};
__export(capture_history_exports, {
decryptHistoryPayloads: () => decryptHistoryPayloads,
serializeHistoryToFixture: () => serializeHistoryToFixture
});
import { historyToJSON } from "@temporalio/common/lib/proto-utils.js";
function isPayload(value) {
return value != null && typeof value === "object" && "metadata" in value && "data" in value;
}
async function decryptHistoryPayloads(node, decrypt) {
if (node == null || typeof node !== "object") return 0;
if (isPayload(node)) {
const inner = await decrypt(node);
if (inner === null) return 0;
node.metadata = inner.metadata ?? {};
node.data = inner.data ?? new Uint8Array(0);
return 1;
}
let count = 0;
if (Array.isArray(node)) {
for (const item of node) count += await decryptHistoryPayloads(item, decrypt);
} else {
for (const value of Object.values(node)) {
count += await decryptHistoryPayloads(value, decrypt);
}
}
return count;
}
async function serializeHistoryToFixture(events, decrypter) {
let decryptedPayloads = 0;
if (decrypter) {
for (const event of events) {
decryptedPayloads += await decryptHistoryPayloads(event, decrypter);
}
}
const fixture = JSON.parse(historyToJSON({ events }));
return { fixture, eventCount: events.length, decryptedPayloads };
}
var init_capture_history = __esm({
"libs/mesh-cli/src/utils/capture-history.ts"() {
"use strict";
}
});
// libs/mesh-cli/src/commands/temporal.ts
import { spawnSync as spawnSync4 } from "node:child_process";
import { writeFileSync as writeFileSync24, mkdirSync as mkdirSync19 } from "node:fs";
import { homedir as homedir6 } from "node:os";
import { dirname as dirname28, join as join39, resolve as resolve17 } from "node:path";
async function resolveConnection(options) {
if (options.address && options.namespace) {
return { address: options.address, namespace: options.namespace };
}
if (!options.address && process.env.TEMPORAL_ADDRESS) {
const address = process.env.TEMPORAL_ADDRESS;
const namespace2 = options.namespace ?? process.env.TEMPORAL_NAMESPACE;
if (namespace2) return { address, namespace: namespace2 };
}
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
throw new Error(
"Not in a Mesh app directory (no Pulumi.yaml found).\nRun from an app directory, or provide --address and --namespace."
);
}
const stack = options.stack ?? getCurrentStack(appRoot);
if (!stack) {
const stacks = findStackConfigs(appRoot);
throw new Error(
`No Pulumi stack selected.
` + (stacks.length > 0 ? `Available: ${stacks.join(", ")}
Use: mesh temporal --stack <name> ...` : "No stack configs found in this directory.")
);
}
const sa = stack ? ["--stack", stack] : [];
const deployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
const awsCreds = deployerRole ? (await resolveAwsCredentials(deployerRole, appRoot, stack))?.env : void 0;
let devOutput;
let appOutput;
try {
const result = pulumiStackOutput(appRoot, "app", sa, awsCreds);
appOutput = JSON.parse(result);
devOutput = appOutput.dev ?? appOutput;
} catch {
try {
const result = pulumiStackOutput(appRoot, "dev", sa, awsCreds);
devOutput = JSON.parse(result);
appOutput = devOutput;
} catch {
throw new Error(
`Could not read stack output for '${stack}'.
Ensure you've run: mesh deploy up
Or provide --address and --namespace explicitly.`
);
}
}
const tunnel = devOutput?.tunnels?.temporal;
const namespace = options.namespace ?? appOutput?.namespace ?? devOutput?.namespace;
if (!tunnel) {
throw new Error("No temporal tunnel found in stack output.");
}
if (!namespace) {
throw new Error("No temporal namespace found in stack output.");
}
const platform = devOutput?.platform ?? appOutput?.platform;
const auth = platform ? {
tenant: platform.tenant ?? "mesh",
env: platform.env ?? "dev",
platformName: platform.name ?? platform.tenant ?? "mesh"
} : void 0;
return {
address: options.address ?? `${tunnel.host}:${tunnel.port}`,
namespace,
auth
};
}
async function resolveBearerToken(auth) {
try {
const haveEnvCreds = !!process.env.TEMPORAL_AUTH_CLIENT_ID && !!process.env.ZITADEL_ISSUER;
const vars = !haveEnvCreds && auth ? await resolveTemporalAuth(auth.tenant, auth.env, auth.platformName) : {};
return await acquireTemporalBearer(vars);
} catch (err) {
logWarn(
`Temporal auth unavailable (${err instanceof Error ? err.message : String(err)}); connecting unauthenticated`
);
return void 0;
}
}
async function describeWorkflow(workflowId, runId, options) {
const conn = await connect5(options);
try {
const desc = await conn.connection.workflowService.describeWorkflowExecution({
namespace: conn.namespace,
execution: { workflowId, runId }
});
const info = desc.workflowExecutionInfo;
if (!info) {
logError("No workflow execution info returned.");
return;
}
console.log(`Workflow: ${workflowId}`);
console.log(`Run ID: ${info.execution?.runId ?? runId ?? "?"}`);
console.log(`Type: ${info.type?.name ?? "?"}`);
console.log(`Status: ${STATUS_NAMES[info.status ?? 0] ?? info.status}`);
console.log(`Task Queue: ${info.taskQueue ?? "?"}`);
console.log(`Namespace: ${conn.namespace}`);
const pending = desc.pendingActivities ?? [];
if (pending.length > 0) {
console.log(`
Pending Activities (${pending.length}):`);
for (const pa of pending) {
console.log(` - ${pa.activityType?.name ?? "?"} (attempt ${pa.attempt}, state: ${pa.state})`);
}
}
const pendingNexus = desc.pendingNexusOperations ?? [];
if (pendingNexus.length > 0) {
console.log(`
Pending Nexus Operations (${pendingNexus.length}):`);
for (const pn of pendingNexus) {
console.log(` - ${pn.operation ?? "?"} \u2192 ${pn.endpoint ?? "?"} (state: ${pn.state})`);
}
}
} finally {
conn.connection.close();
}
}
async function* iterHistoryEvents(workflowService, namespace, workflowId, runId, maximumPageSize = 100) {
let nextPageToken;
do {
const resp = await workflowService.getWorkflowExecutionHistory({
namespace,
execution: { workflowId, runId },
maximumPageSize,
nextPageToken
});
for (const event of resp.history?.events ?? []) {
yield event;
}
nextPageToken = resp.nextPageToken?.length ? resp.nextPageToken : void 0;
} while (nextPageToken);
}
async function showHistory(workflowId, runId, options) {
const conn = await connect5(options);
const maxEvents = options.follow ? Infinity : parseInt(options.limit ?? "200", 10);
const skipTypes = /* @__PURE__ */ new Set([5, 6, 7, 8, 9]);
const decrypter = options.showPayloads ? await buildDecrypterForNamespace(conn.namespace) : void 0;
try {
let totalShown = 0;
let lastEventId = 0;
console.log(`History for ${workflowId} (namespace: ${conn.namespace})${options.follow ? " [following]" : ""}
`);
for await (const event of iterHistoryEvents(
conn.connection.workflowService,
conn.namespace,
workflowId,
runId
)) {
lastEventId = Number(event.eventId);
const eventType = event.eventType ?? 0;
if (options.compact !== false && skipTypes.has(eventType)) continue;
await printEvent(event, decrypter);
totalShown++;
if (totalShown >= maxEvents) break;
}
if (!options.follow) {
console.log(`
${totalShown} events shown.`);
return;
}
const terminalTypes = /* @__PURE__ */ new Set([2, 3, 4, 21, 27, 28]);
let done = false;
while (!done) {
try {
const resp = await conn.connection.workflowService.getWorkflowExecutionHistory({
namespace: conn.namespace,
execution: { workflowId, runId },
maximumPageSize: 100,
waitNewEvent: true,
// Start after the last event we've seen
...lastEventId > 0 ? { skipArchival: true } : {}
});
for (const event of resp.history?.events ?? []) {
const eid = Number(event.eventId);
if (eid <= lastEventId) continue;
lastEventId = eid;
const eventType = event.eventType ?? 0;
if (options.compact !== false && skipTypes.has(eventType)) continue;
await printEvent(event, decrypter);
totalShown++;
if (terminalTypes.has(eventType)) {
done = true;
}
}
} catch (err) {
if (err?.code === 5 || err?.message?.includes("not found")) {
console.log("\nWorkflow completed or not found.");
done = true;
} else {
throw err;
}
}
}
console.log(`
${totalShown} events shown (workflow ended).`);
} finally {
conn.connection.close();
}
}
async function printEvent(event, decrypter) {
const eventType = event.eventType ?? 0;
const typeName = EVENT_TYPE_NAMES[eventType] ?? `Unknown(${eventType})`;
const id = String(event.eventId).padStart(4);
const detail = extractEventDetail(event);
const detailStr = detail ? ` \u2014 ${detail}` : "";
console.log(`${id} ${typeName}${detailStr}`);
if (!decrypter) return;
for (const group of extractEventPayloads(event)) {
const rendered = await renderPayloads(group.payloads, decrypter);
if (rendered === null) continue;
console.log(` ${group.label}:`);
for (const line of rendered.split("\n")) {
console.log(` ${line}`);
}
}
}
function extractEventPayloads(event) {
const groups = [];
const pushIfAny = (label, payloads) => {
if (payloads && payloads.length > 0) groups.push({ label, payloads });
};
if (event.workflowExecutionStartedEventAttributes) {
pushIfAny("input", event.workflowExecutionStartedEventAttributes.input?.payloads);
}
if (event.workflowExecutionCompletedEventAttributes) {
pushIfAny("result", event.workflowExecutionCompletedEventAttributes.result?.payloads);
}
if (event.workflowExecutionFailedEventAttributes) {
const failure = event.workflowExecutionFailedEventAttributes.failure;
if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
}
if (event.workflowExecutionSignaledEventAttributes) {
pushIfAny("signal input", event.workflowExecutionSignaledEventAttributes.input?.payloads);
}
if (event.activityTaskScheduledEventAttributes) {
pushIfAny("input", event.activityTaskScheduledEventAttributes.input?.payloads);
}
if (event.activityTaskCompletedEventAttributes) {
pushIfAny("result", event.activityTaskCompletedEventAttributes.result?.payloads);
}
if (event.activityTaskFailedEventAttributes) {
const failure = event.activityTaskFailedEventAttributes.failure;
if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
}
if (event.nexusOperationScheduledEventAttributes) {
const input2 = event.nexusOperationScheduledEventAttributes.input;
if (input2) pushIfAny("input", [input2]);
}
if (event.nexusOperationCompletedEventAttributes) {
const result = event.nexusOperationCompletedEventAttributes.result;
if (result) pushIfAny("result", [result]);
}
if (event.nexusOperationFailedEventAttributes) {
const failure = event.nexusOperationFailedEventAttributes.failure;
if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
}
return groups;
}
async function renderPayloads(payloads, decrypter) {
const parts = [];
for (const payload of payloads) {
if (payload && typeof payload === "object" && "__failure" in payload) {
const failure = payload.__failure;
parts.push(renderFailure(failure));
continue;
}
const decoded = await decrypter(payload);
if (decoded === null) {
parts.push("[non-encrypted payload]");
} else {
parts.push(decoded);
}
}
return parts.length > 0 ? parts.join("\n---\n") : null;
}
function renderFailure(failure) {
const lines = [failure.message ?? "(no message)"];
if (failure.stackTrace) lines.push(failure.stackTrace);
return lines.join("\n");
}
function extractEventDetail(event) {
if (event.activityTaskScheduledEventAttributes) {
const a = event.activityTaskScheduledEventAttributes;
return `activity: ${a.activityType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
}
if (event.activityTaskCompletedEventAttributes) {
return `scheduledId: ${event.activityTaskCompletedEventAttributes.scheduledEventId}`;
}
if (event.activityTaskFailedEventAttributes) {
const a = event.activityTaskFailedEventAttributes;
const msg = a.failure?.message ?? "";
return `scheduledId: ${a.scheduledEventId}${msg ? `, error: ${msg.slice(0, 120)}` : ""}`;
}
if (event.activityTaskTimedOutEventAttributes) {
return `scheduledId: ${event.activityTaskTimedOutEventAttributes.scheduledEventId}`;
}
if (event.nexusOperationScheduledEventAttributes) {
const a = event.nexusOperationScheduledEventAttributes;
return `endpoint: ${a.endpoint ?? "?"}, operation: ${a.operation ?? "?"}, service: ${a.service ?? "?"}`;
}
if (event.nexusOperationCompletedEventAttributes) {
return `scheduledId: ${event.nexusOperationCompletedEventAttributes.scheduledEventId}`;
}
if (event.nexusOperationFailedEventAttributes) {
const a = event.nexusOperationFailedEventAttributes;
return `scheduledId: ${a.scheduledEventId}, error: ${a.failure?.message?.slice(0, 120) ?? "?"}`;
}
if (event.nexusOperationTimedOutEventAttributes) {
return `scheduledId: ${event.nexusOperationTimedOutEventAttributes.scheduledEventId}`;
}
if (event.workflowExecutionSignaledEventAttributes) {
return `signal: ${event.workflowExecutionSignaledEventAttributes.signalName ?? "?"}`;
}
if (event.workflowExecutionStartedEventAttributes) {
const a = event.workflowExecutionStartedEventAttributes;
return `type: ${a.workflowType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
}
if (event.workflowExecutionCompletedEventAttributes) return null;
if (event.workflowExecutionFailedEventAttributes) {
return `error: ${event.workflowExecutionFailedEventAttributes.failure?.message?.slice(0, 120) ?? "?"}`;
}
if (event.timerStartedEventAttributes) {
const dur = event.timerStartedEventAttributes.startToFireTimeout;
return `timerId: ${event.timerStartedEventAttributes.timerId}, duration: ${dur?.seconds ?? "?"}s`;
}
if (event.timerFiredEventAttributes) {
return `timerId: ${event.timerFiredEventAttributes.timerId}`;
}
return null;
}
async function recoverConversation(workflowId, runId, options) {
const conn = await connect5(options);
const decrypter = await buildDecrypterForNamespace(conn.namespace);
if (!decrypter) {
conn.connection.close();
throw new Error(
"Cannot decrypt conversation payloads: no encoding key available.\nSet TEMPORAL_ENCODING_KEY (e.g. from a `mesh dev` worker env) or configure kubectl namespace read access, then retry."
);
}
try {
const decodePayload = async (payloads) => {
const first = payloads?.[0];
if (first === void 0) return void 0;
const decoded = await decrypter(first);
if (decoded === null) return void 0;
try {
return JSON.parse(decoded);
} catch {
return void 0;
}
};
const rawEvents = [];
for await (const event of iterHistoryEvents(
conn.connection.workflowService,
conn.namespace,
workflowId,
runId
)) {
rawEvents.push(event);
}
if (options.snapshot) {
const messages = await extractSnapshotMessages(rawEvents, decodePayload);
const blob = {
conversationId: workflowId,
messages,
artifacts: [],
focus: null
};
const output2 = JSON.stringify(blob, null, 2) + "\n";
if (options.out) {
writeFileSync24(options.out, output2);
logSuccess(
`Wrote snapshot (${messages.length} messages) to ${options.out}`
);
} else {
process.stdout.write(output2);
}
return;
}
const events = await classifyHistoryEvents(rawEvents, decodePayload);
const turns = reconstructTranscript(events);
const output = options.json ? JSON.stringify(
{
turns: turns.map((t) => ({
role: t.role,
text: t.text,
...t.toolCalls ? { toolCalls: t.toolCalls } : {}
}))
},
null,
2
) + "\n" : renderTranscriptMarkdown(turns);
if (options.out) {
writeFileSync24(options.out, output);
logSuccess(`Wrote ${turns.length} turns to ${options.out}`);
} else {
process.stdout.write(output);
}
} finally {
conn.connection.close();
}
}
async function captureHistory(workflowId, runId, options) {
const conn = await connect5(options);
const decrypter = await buildRawDecrypterForNamespace(conn.namespace);
if (!decrypter) {
conn.connection.close();
throw new Error(
"Cannot decrypt history payloads: no encoding key available.\nA replay fixture needs decrypted payloads. Set TEMPORAL_ENCODING_KEY (e.g. from a `mesh dev` worker env) or configure kubectl namespace read access, then retry."
);
}
try {
const events = [];
for await (const event of iterHistoryEvents(
conn.connection.workflowService,
conn.namespace,
workflowId,
runId
)) {
events.push(event);
}
if (events.length === 0) {
throw new Error(
`No history events found for workflow '${workflowId}'` + (runId ? ` (run ${runId})` : "") + `. Check the workflow id and namespace (${conn.namespace}).`
);
}
const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
const outPath = options.out ?? join39(homedir6(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
mkdirSync19(dirname28(outPath), { recursive: true });
if (options.out) warnIfNotGitIgnored(options.out);
writeFileSync24(outPath, JSON.stringify(fixture, null, 2) + "\n");
logSuccess(
`Wrote replay history (${eventCount} events, ${decryptedPayloads} payloads decrypted) to ${outPath}`
);
logWarn(
"\u26A0 This file contains DECRYPTED payloads \u2014 real customer/conversation data.\n For LOCAL replay debugging ONLY. Do NOT commit it. (CI golden fixtures are synthetic; this is the debugging counterpart.)"
);
} finally {
conn.connection.close();
}
}
function sanitizeFileId(id) {
return id.replace(/[^A-Za-z0-9._-]/g, "_");
}
function warnIfNotGitIgnored(outPath) {
const abs = resolve17(outPath);
try {
const res = spawnSync4("git", ["-C", dirname28(abs), "check-ignore", "-q", abs], {
stdio: "ignore"
});
if (res.status !== 1) return;
} catch {
return;
}
logWarn(
`\u26A0\u26A0\u26A0 OUTPUT PATH IS INSIDE A GIT REPO AND IS *NOT* GITIGNORED \u26A0\u26A0\u26A0
${abs}
This fixture contains DECRYPTED production payloads. Add it to .gitignore or move it outside the repo \u2014 do NOT commit it.`
);
}
async function terminateWorkflow(workflowId, runId, options) {
const conn = await connect5(options);
try {
await conn.connection.workflowService.terminateWorkflowExecution({
namespace: conn.namespace,
workflowExecution: { workflowId, runId },
reason: options.reason ?? "terminated via mesh temporal",
identity: "mesh-cli"
});
logSuccess(
`Terminated ${workflowId}${runId ? ` (run ${runId})` : ""} in namespace ${conn.namespace}`
);
} finally {
conn.connection.close();
}
}
async function connect5(options) {
const info = await resolveConnection(options);
logInfo(`Connecting to ${info.address} (namespace: ${info.namespace})`);
const { Connection } = await import("@temporalio/client");
const token = await resolveBearerToken(info.auth);
const metadata = token ? { authorization: `Bearer ${token}` } : void 0;
const connection = await Connection.connect({
address: info.address,
metadata,
channelArgs: { "grpc.max_receive_message_length": 256 * 1024 * 1024 }
});
return { connection, namespace: info.namespace };
}
function registerTemporalCommands(program2) {
const temporal = program2.command("temporal").description("Inspect Temporal workflows for the current app/stack");
const sharedOpts = (cmd) => cmd.option("--stack <stack>", "Pulumi stack name (auto-detected from current directory)").option("--address <addr>", "Temporal server address (default: from stack output)").option("--namespace <ns>", "Temporal namespace (default: from stack output)");
sharedOpts(
temporal.command("describe <workflowId> [runId]").description("Show workflow status and metadata")
).action(async (workflowId, runId, opts) => {
try {
await describeWorkflow(workflowId, runId, opts);
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
}
});
sharedOpts(
temporal.command("terminate <workflowId> [runId]").description("Terminate a running workflow (e.g. a wedged/looping conversation)")
).option("--reason <reason>", "Termination reason (recorded in workflow history)").action(async (workflowId, runId, opts) => {
try {
await terminateWorkflow(workflowId, runId, opts);
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
}
});
sharedOpts(
temporal.command("history <workflowId> [runId]").description("Show workflow event history").option("-n, --limit <n>", "Maximum events to show (default: 200)").option("-f, --follow", "Tail the history, waiting for new events until the workflow completes").option("--no-compact", "Show all events including WorkflowTask scheduling noise").option("-p, --show-payloads", "Decrypt and print activity inputs/outputs (reads TEMPORAL_ENCODING_KEY from the app's K8s secret via kubectl)")
).action(async (workflowId, runId, opts) => {
try {
await showHistory(workflowId, runId, opts);
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
}
});
sharedOpts(
temporal.command("recover-conversation <workflowId> [runId]").description(
"Reconstruct an agent conversation transcript from durable Temporal history (for when the worker can no longer replay it). Requires the encoding key \u2014 reads TEMPORAL_ENCODING_KEY (or the app's K8s secret)."
).option("--out <path>", "Write the transcript to a file instead of stdout").option("--json", "Emit a structured { turns: [...] } JSON array instead of markdown").option(
"--snapshot",
"Emit a store-ready conversation blob { conversationId, messages, artifacts, focus } with STRUCTURED ModelMessage[] (tool-call parts intact) for a full-fidelity backfill"
)
).action(async (workflowId, runId, opts) => {
try {
await recoverConversation(workflowId, runId, opts);
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
}
});
sharedOpts(
temporal.command("capture-history <workflowId> [runId]").description(
"Capture a workflow's full history to a replay fixture JSON (proto3-JSON, DECRYPTED payloads) for LOCAL replay debugging. Requires the encoding key. Defaults to ~/.mesh/replay-histories/<workflowId>.json (outside any repo) \u2014 do NOT commit the output."
).option(
"--out <path>",
"Output path (default: ~/.mesh/replay-histories/<workflowId>.json; a non-gitignored path inside a git repo triggers a loud warning)"
)
).action(async (workflowId, runId, opts) => {
try {
await captureHistory(workflowId, runId, opts);
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
}
});
}
var EVENT_TYPE_NAMES, STATUS_NAMES;
var init_temporal = __esm({
"libs/mesh-cli/src/commands/temporal.ts"() {
"use strict";
init_log();
init_recover_conversation();
init_pulumi();
init_aws_auth();
init_temporal_auth();
init_temporal_codec();
EVENT_TYPE_NAMES = {
1: "WorkflowExecutionStarted",
2: "WorkflowExecutionCompleted",
3: "WorkflowExecutionFailed",
4: "WorkflowExecutionTimedOut",
5: "WorkflowTaskScheduled",
6: "WorkflowTaskStarted",
7: "WorkflowTaskCompleted",
8: "WorkflowTaskTimedOut",
9: "WorkflowTaskFailed",
10: "ActivityTaskScheduled",
11: "ActivityTaskStarted",
12: "ActivityTaskCompleted",
13: "ActivityTaskFailed",
14: "ActivityTaskTimedOut",
15: "ActivityTaskCancelRequested",
16: "ActivityTaskCanceled",
17: "TimerStarted",
18: "TimerFired",
19: "TimerCanceled",
20: "WorkflowExecutionCancelRequested",
21: "WorkflowExecutionCanceled",
24: "WorkflowExecutionSignaled",
// not 22/23 which were removed
25: "WorkflowExecutionTerminated",
26: "WorkflowExecutionSignaled",
29: "WorkflowPropertiesModified",
40: "ChildWorkflowExecutionStarted",
41: "ChildWorkflowExecutionCompleted",
42: "ChildWorkflowExecutionFailed",
43: "ChildWorkflowExecutionCanceled",
44: "ChildWorkflowExecutionTimedOut",
45: "ChildWorkflowExecutionTerminated",
46: "WorkflowPropertiesModified",
48: "NexusOperationScheduled",
49: "NexusOperationStarted",
50: "NexusOperationCompleted",
51: "NexusOperationFailed",
52: "NexusOperationCanceled",
53: "NexusOperationTimedOut"
};
STATUS_NAMES = {
0: "UNSPECIFIED",
1: "RUNNING",
2: "COMPLETED",
3: "FAILED",
4: "CANCELED",
5: "TERMINATED",
6: "CONTINUED_AS_NEW",
7: "TIMED_OUT"
};
}
});
// libs/mesh-cli/src/commands/tenant.ts
import chalk7 from "chalk";
import * as fs36 from "fs";
import * as path43 from "path";
import { parseDocument, YAMLMap, isMap } from "yaml";
function validateTenantName(name) {
return /^[a-z][a-z0-9-]*$/.test(name) && !name.endsWith("-");
}
function deriveEnvFromStack(stack) {
const m = stack.match(/-([a-z0-9]+)$/);
return m ? m[1] ?? null : null;
}
function addTenantToStackConfig(yamlText, spec) {
const doc = parseDocument(yamlText);
const config = doc.getIn(["config"]);
if (!isMap(config)) {
return { ok: false, reason: "no-config", detail: "no top-level `config:` map in the stack config" };
}
const tenantsPath = ["config", "mesh:tenants"];
let createdTenantsBlock = false;
if (!isMap(doc.getIn(tenantsPath))) {
if (doc.hasIn(tenantsPath)) {
return { ok: false, reason: "no-config", detail: "`mesh:tenants` exists but is not a map" };
}
doc.setIn(tenantsPath, new YAMLMap());
createdTenantsBlock = true;
}
if (doc.hasIn([...tenantsPath, spec.name])) {
return { ok: false, reason: "exists", detail: `tenant '${spec.name}' is already declared in mesh:tenants` };
}
const entry = {};
if (spec.displayName) entry.displayName = spec.displayName;
entry.subdomain = spec.subdomain;
if (spec.zitadelOrgId) entry.zitadelOrgId = spec.zitadelOrgId;
doc.setIn([...tenantsPath, spec.name], doc.createNode(entry));
return {
ok: true,
yaml: doc.toString({ flowCollectionPadding: false, lineWidth: 0 }),
createdTenantsBlock
};
}
function listTenantsInStackConfig(yamlText) {
const doc = parseDocument(yamlText);
const tenants = doc.getIn(["config", "mesh:tenants"]);
if (!isMap(tenants)) return [];
return tenants.items.map((item) => {
const name = String(item.key?.value ?? item.key);
const value = item.value;
const get = (key) => {
if (!isMap(value)) return void 0;
const v = value.get(key, false);
return typeof v === "string" ? v : void 0;
};
return { name, displayName: get("displayName"), subdomain: get("subdomain") };
});
}
function resolveStackConfig(explicitStack) {
const appRoot = findAppRoot(process.cwd());
if (!appRoot) {
throw new MeshCliError(
"No Pulumi.yaml found. Run from the PLATFORM layer of a tenant platform repo (the directory whose stack config carries mesh:tenants)."
);
}
const stacks = findStackConfigs(appRoot);
const stack = explicitStack ?? getCurrentStack(appRoot) ?? (stacks.length === 1 ? stacks[0] : null);
if (!stack) {
throw new MeshCliError(
`Could not determine the stack (found: ${stacks.join(", ") || "none"}).`,
{ remediation: { command: "mesh tenant add <name> --stack <stack>" } }
);
}
const file = path43.join(appRoot, `Pulumi.${stack}.yaml`);
if (!fs36.existsSync(file)) {
throw new MeshCliError(`Stack config not found: ${file}`, {
remediation: { command: "mesh tenant add <name> --stack <stack>" }
});
}
return { appRoot, stack, file, content: fs36.readFileSync(file, "utf-8") };
}
function readProjectName(appRoot) {
const projectDoc = parseDocument(fs36.readFileSync(path43.join(appRoot, "Pulumi.yaml"), "utf-8"));
return String(projectDoc.get("name") ?? "");
}
function looksLikePlatformLayer(projectName) {
return projectName.endsWith("-platform");
}
function assertPlatformLayer(appRoot, content) {
if (parseDocument(content).hasIn(["config", "mesh:tenants"])) return;
const projectName = readProjectName(appRoot);
if (!looksLikePlatformLayer(projectName)) {
throw new MeshCliError(
`This looks like the wrong layer: project '${projectName}' has no mesh:tenants block and does not look like a platform layer. App tenants are registered on the tenant platform repo's PLATFORM stack (e.g. mesh-sandbox/platform).`
);
}
}
function registerTenantCommands(program2) {
const tenant = program2.command("tenant").description("Register and inspect app tenants on a platform stack");
tenant.command("add <name>").description(
"Register an app tenant in the platform stack config (mesh:tenants) \u2014 then `mesh deploy up` makes it live."
).option("--display-name <name>", "display name shown in the Hub UI (default: the tenant slug)").option("--subdomain <subdomain>", "tenant subdomain (default: {name}-{env} derived from the stack name)").option("--zitadel-org-id <id>", "existing Zitadel organization ID to bind (optional)").option("--stack <stack>", "platform stack to register on (default: current stack, else the only one)").option("--json", "machine-readable output", false).action(
async (name, opts) => {
if (!validateTenantName(name)) {
throw new MeshCliError(`Invalid tenant name '${name}' (lowercase alphanumeric + dashes).`);
}
const resolved = resolveStackConfig(opts.stack);
assertPlatformLayer(resolved.appRoot, resolved.content);
const env = deriveEnvFromStack(resolved.stack);
const subdomain = opts.subdomain ?? (env ? `${name}-${env}` : null);
if (!subdomain) {
throw new MeshCliError(
`Cannot derive a default subdomain from stack '${resolved.stack}'.`,
{ remediation: { command: `mesh tenant add ${name} --subdomain <subdomain>` } }
);
}
const result = addTenantToStackConfig(resolved.content, {
name,
displayName: opts.displayName,
subdomain,
zitadelOrgId: opts.zitadelOrgId
});
if (!result.ok) {
if (result.reason === "exists") {
throw new MeshCliError(
`${result.detail} (${path43.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
);
}
throw new MeshCliError(`${resolved.file}: ${result.detail}`);
}
atomicWriteFileSync(resolved.file, result.yaml, fs36.statSync(resolved.file).mode & 511);
if (opts.json) {
emitJsonPayload({
ok: true,
tenant: name,
stack: resolved.stack,
file: resolved.file,
entry: { displayName: opts.displayName, subdomain, zitadelOrgId: opts.zitadelOrgId },
deploy: `mesh deploy up -s ${resolved.stack}`
});
return;
}
logInfo(
`Tenant '${name}' registered in ${path43.basename(resolved.file)} (mesh:tenants, subdomain '${subdomain}')${result.createdTenantsBlock ? " \u2014 created the mesh:tenants block" : ""}`
);
console.log("");
console.log("Next steps:");
console.log(` mesh deploy preview -s ${resolved.stack} # expect only '${name}'-scoped additions`);
console.log(` mesh deploy up -s ${resolved.stack} # provision tenant infra + Hub registry entry`);
console.log("");
console.log("Then, from the tenant's apps repo:");
console.log(` mesh init app-tenant --tenant ${name} # the doctor should now pass`);
console.log(` mesh create-app --tenant ${name} --name <app>`);
}
);
tenant.command("list").description("List app tenants declared in the platform stack config").option("--stack <stack>", "platform stack to read (default: current stack, else the only one)").option("--json", "machine-readable output", false).action(async (opts) => {
const resolved = resolveStackConfig(opts.stack);
const tenants = listTenantsInStackConfig(resolved.content);
if (opts.json) {
emitJsonPayload({ ok: true, stack: resolved.stack, tenants });
return;
}
if (tenants.length === 0) {
const projectName = readProjectName(resolved.appRoot);
if (!looksLikePlatformLayer(projectName)) {
logWarn(
`Project '${projectName}' does not look like a platform layer \u2014 tenants are declared on the tenant platform repo's PLATFORM stack (e.g. mesh-sandbox/platform).`
);
}
logInfo(`No tenants declared in ${path43.basename(resolved.file)}.`);
return;
}
logInfo(`Tenants on stack '${resolved.stack}':`);
for (const t of tenants) {
const display = t.displayName ? ` (${t.displayName})` : "";
console.log(` ${chalk7.cyan(t.name.padEnd(20))}${t.subdomain ?? ""}${chalk7.dim(display)}`);
}
});
}
var init_tenant = __esm({
"libs/mesh-cli/src/commands/tenant.ts"() {
"use strict";
init_log();
init_errors();
init_pulumi();
init_login();
}
});
// libs/mesh-cli/src/commands/tunnel/index.ts
import { spawn as spawn9 } from "child_process";
import * as fs37 from "fs";
import * as os12 from "os";
import * as path44 from "path";
import {
SecretsManagerClient as SecretsManagerClient9,
GetSecretValueCommand as GetSecretValueCommand9
} from "@aws-sdk/client-secrets-manager";
function resolveTenantEnv(options) {
if (options.tenant && options.env) {
return { tenant: options.tenant, env: options.env };
}
const ctx = detectContext(options.env);
return {
tenant: options.tenant ?? ctx.tenant,
env: options.env ?? ctx.platformEnv,
stage: ctx.stage
};
}
function readPulumiAwsRegion(stage) {
const preferred = stage ? `Pulumi.${stage}.yaml` : void 0;
const rank = (f) => f === preferred ? 0 : f === "Pulumi.yaml" ? 2 : 1;
try {
const files = fs37.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
for (const f of files) {
const m = fs37.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
if (m?.[1]) return m[1].trim();
}
} catch {
}
return void 0;
}
function awsProfileSections(name) {
return [
[path44.join(os12.homedir(), ".aws", "config"), `[profile ${name}]`],
[path44.join(os12.homedir(), ".aws", "credentials"), `[${name}]`]
];
}
function awsProfileExists(name) {
for (const [file, header] of awsProfileSections(name)) {
try {
const lines = fs37.readFileSync(file, "utf-8").split("\n");
if (lines.some((l) => l.trim() === header)) return true;
} catch {
}
}
return false;
}
function parseProfileRegion(text, header) {
let inSection = false;
for (const raw of text.split("\n")) {
const line = raw.trim();
if (line.startsWith("[")) {
inSection = line === header;
continue;
}
if (!inSection) continue;
const m = line.match(/^region\s*=\s*(.+)$/);
const value = m?.[1]?.split(/\s+[#;]/)[0]?.trim();
if (value) return value;
}
return void 0;
}
function awsProfileRegion(profile) {
if (!profile) return void 0;
for (const [file, header] of awsProfileSections(profile)) {
try {
const region = parseProfileRegion(fs37.readFileSync(file, "utf-8"), header);
if (region) return region;
} catch {
}
}
return void 0;
}
function awsDefaultProfileSections() {
return [
[path44.join(os12.homedir(), ".aws", "config"), "[default]"],
[path44.join(os12.homedir(), ".aws", "credentials"), "[default]"]
];
}
function awsDefaultProfileRegion() {
for (const [file, header] of awsDefaultProfileSections()) {
try {
const region = parseProfileRegion(fs37.readFileSync(file, "utf-8"), header);
if (region) return region;
} catch {
}
}
return void 0;
}
function applyAwsDefaults(tenant, env, stage) {
if (!process.env.AWS_PROFILE && !process.env.AWS_ACCESS_KEY_ID) {
const profile = [`${tenant}-${env}`, tenant].find(awsProfileExists);
if (profile) {
process.env.AWS_PROFILE = profile;
logInfo(`AWS profile: ${profile} (from ~/.aws, tenant convention)`);
}
}
if (!process.env.AWS_REGION && !process.env.AWS_DEFAULT_REGION) {
const region = readPulumiAwsRegion(stage) ?? (process.env.AWS_PROFILE ? awsProfileRegion(process.env.AWS_PROFILE) : awsDefaultProfileRegion()) ?? "us-east-2";
process.env.AWS_REGION = region;
logInfo(`AWS region: ${region} (auto)`);
}
}
function getEffectivePort(serviceName, options) {
const config = SERVICE_CONFIG[serviceName];
if (!config) return 0;
switch (serviceName) {
case "db":
return options.dbPort ? parseInt(options.dbPort, 10) : config.localPort;
case "temporal-ui":
return options.temporalUiPort ? parseInt(options.temporalUiPort, 10) : config.localPort;
case "temporal-frontend":
return options.temporalFrontendPort ? parseInt(options.temporalFrontendPort, 10) : config.localPort;
default:
return config.localPort;
}
}
function parseServices(input2) {
if (SERVICE_GROUPS[input2]) {
return SERVICE_GROUPS[input2];
}
return input2.split(",").map((s) => s.trim());
}
function spawnSsmTunnel(instanceId, service, localPort) {
return spawn9(
"aws",
[
"ssm",
"start-session",
"--target",
instanceId,
"--document-name",
"AWS-StartPortForwardingSessionToRemoteHost",
"--parameters",
JSON.stringify({
host: [service.host],
portNumber: [String(service.port)],
localPortNumber: [String(localPort)]
})
],
{ stdio: "inherit" }
);
}
async function listServices(options) {
const { tenant, env } = options;
const bastion = await getPlatformBastionInfo(tenant, env);
console.log("");
logInfo(`Tenant: ${tenant}, Platform Env: ${env}`);
console.log("");
console.log("Available services:");
console.log("");
for (const [name, config] of Object.entries(SERVICE_CONFIG)) {
const endpoint = bastion.services[config.bastionKey];
const status = endpoint ? "\u2713" : "\u2717";
const localPort = getEffectivePort(name, options);
console.log(
` ${status} ${name.padEnd(20)} localhost:${localPort} \u2192 ${endpoint?.host ?? "not available"}:${endpoint?.port ?? ""}`
);
}
console.log("");
console.log("Service groups:");
for (const [group, services] of Object.entries(SERVICE_GROUPS)) {
console.log(` ${group.padEnd(20)} ${services.join(", ")}`);
}
console.log("");
logInfo("Usage:");
console.log(" mesh tunnel dev # All dev services");
console.log(" mesh tunnel --services temporal # Just temporal services");
console.log(" mesh tunnel --services db # Just database");
console.log(" mesh tunnel external <name> # Registered external service");
console.log("");
}
async function tunnelServices(serviceNames, options) {
const { tenant, env } = options;
const bastion = await getPlatformBastionInfo(tenant, env);
const servicesToStart = [];
for (const name of serviceNames) {
const config = SERVICE_CONFIG[name];
if (!config) {
logError(`Unknown service: ${name}`);
logInfo(`Available services: ${Object.keys(SERVICE_CONFIG).join(", ")}`);
process.exit(1);
}
const endpoint = bastion.services[config.bastionKey];
if (!endpoint) {
logError(`Service '${name}' not available in platform bastion.`);
logInfo("Make sure the service is enabled and deployed.");
process.exit(1);
}
servicesToStart.push({ name, config, endpoint });
}
if (servicesToStart.length === 0) {
logError("No services specified.");
process.exit(1);
}
console.log("");
logInfo(`Tenant: ${tenant}, Platform Env: ${env}`);
console.log("");
logInfo("Starting tunnels...");
for (const { name, config, endpoint } of servicesToStart) {
const localPort = getEffectivePort(name, options);
console.log(
` ${config.displayName.padEnd(25)} localhost:${localPort} \u2192 ${endpoint.host}:${endpoint.port}`
);
}
console.log("");
logInfo("Press Ctrl+C to stop the tunnels");
console.log("");
const processes = [];
for (const { name, endpoint } of servicesToStart) {
const localPort = getEffectivePort(name, options);
processes.push(spawnSsmTunnel(bastion.instanceId, endpoint, localPort));
}
const cleanup = () => {
for (const proc of processes) {
proc.kill();
}
process.exit(0);
};
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
await Promise.race(
processes.map(
(proc) => new Promise((resolve19) => {
proc.on("exit", () => resolve19());
proc.on("error", () => resolve19());
})
)
);
cleanup();
}
function resolveCredentialAxis(options) {
return {
appTenant: options.appTenant ?? options.tenant,
appStage: options.appStage ?? options.stage ?? options.env
};
}
function isValidPort(port) {
return Number.isInteger(port) && port >= 1 && port <= 65535;
}
function externalSecretId(appTenant, appStage, name, key) {
const base = `mesh/${appTenant}/${appStage}/external/${name}`;
return key ? `${base}/${key}` : base;
}
function resolveExternalPorts(creds, portOverride) {
const host = typeof creds.host === "string" ? creds.host.trim() : "";
const remotePort = Number(creds.port);
if (!host || !isValidPort(remotePort)) {
throw new Error(
"Secret has no usable host/port fields \u2014 only host:port externals are tunnelable."
);
}
if (portOverride === void 0) {
return { host, remotePort, localPort: remotePort };
}
const localPort = Number(portOverride);
if (!isValidPort(localPort)) {
throw new Error(
`--port must be an integer between 1 and 65535 (got "${portOverride}").`
);
}
return { host, remotePort, localPort };
}
async function tunnelExternal(name, options) {
const { tenant, env, key } = options;
const { appTenant, appStage } = resolveCredentialAxis(options);
const secretId = externalSecretId(appTenant, appStage, name, key);
const setHint = `mesh secrets set external/${name}${key ? ` --key=${key}` : ""}`;
const sm = new SecretsManagerClient9({});
let creds;
try {
const out = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
creds = JSON.parse(out.SecretString ?? "{}");
} catch (err) {
logError(
`Could not read ${secretId}: ${err instanceof Error ? err.message : String(err)}`
);
logInfo(`Is the external service registered and seeded? (${setHint})`);
logInfo(
`Credential axis used: app tenant '${appTenant}', app stage '${appStage}' (override with --app-tenant/--app-stage) \u2014 zero-flag runs key on the stage detected from the app dir, while explicit -t/-e keys on the platform env.`
);
if (!key) {
logInfo(
"Multi-instance externals (credentials.keyedBy) store one secret per instance \u2014 pass --key <key>."
);
}
process.exit(1);
}
let host;
let remotePort;
let localPort;
try {
({ host, remotePort, localPort } = resolveExternalPorts(creds, options.port));
} catch (err) {
logError(err instanceof Error ? err.message : String(err));
logInfo(`Secret: ${secretId}`);
process.exit(1);
}
const bastion = await getPlatformBastionInfo(tenant, env);
console.log("");
logInfo(`Tenant: ${tenant}, Platform Env: ${env}`);
if (appTenant !== tenant || appStage !== env) {
logInfo(`App credentials: tenant=${appTenant}, stage=${appStage}`);
}
console.log("");
console.log(
` ${name.padEnd(25)} localhost:${localPort} \u2192 ${host}:${remotePort}`
);
console.log("");
logInfo("Press Ctrl+C to stop the tunnel");
console.log("");
const proc = spawnSsmTunnel(
bastion.instanceId,
{ host, port: remotePort },
localPort
);
const cleanup = () => {
proc.kill();
process.exit(0);
};
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
await new Promise((resolve19) => {
proc.on("exit", () => resolve19());
proc.on("error", () => resolve19());
});
cleanup();
}
function registerTunnelCommands(program2) {
const tunnel = program2.command("tunnel").description("Start SSM tunnel to platform services").option(
"-t, --tenant <tenant>",
"Platform tenant (default: detected from Pulumi/SST config in cwd)"
).option(
"-e, --env <env>",
"Platform environment (default: detected from cwd, else dev)"
).option("-l, --list", "List available services").option("-s, --services <services>", "Services to tunnel (comma-separated or group name)").option("--db-port <port>", "Custom local port for database (default: 5432)").option("--temporal-ui-port <port>", "Custom local port for Temporal UI (default: 8080)").option("--temporal-frontend-port <port>", "Custom local port for Temporal Frontend (default: 7233)").argument("[group]", "Service group to tunnel (e.g., dev, temporal)").action(
async (group, rawOptions) => {
const { tenant, env, stage } = resolveTenantEnv(rawOptions);
applyAwsDefaults(tenant, env, stage);
const options = { ...rawOptions, tenant, env };
if (options.list) {
await listServices(options);
return;
}
let services;
if (options.services) {
services = parseServices(options.services);
} else if (group) {
services = parseServices(group);
} else {
await listServices(options);
return;
}
await tunnelServices(services, options);
}
);
tunnel.command("external").description(
"Tunnel to a registered external service via the platform bastion (host/port from mesh/{app-tenant}/{app-stage}/external/{name})"
).argument("<name>", "External service name (e.g., plaid-db)").option(
"-t, --tenant <tenant>",
"Platform tenant (default: detected from Pulumi/SST config in cwd)"
).option(
"-e, --env <env>",
"Platform environment (default: detected from cwd, else dev)"
).option(
"--app-tenant <tenant>",
"Tenant for the external service's credentials (default: --tenant)"
).option(
"--app-stage <stage>",
"Stage for the external service's credentials (default: --env)"
).option(
"-k, --key <key>",
"Instance key for a multi-instance external (credentials.keyedBy)"
).option(
"-p, --port <port>",
"Local port (default: the service's remote port)"
).action(
async (name, _options, command) => {
const merged = command.optsWithGlobals();
const { tenant, env, stage } = resolveTenantEnv(merged);
applyAwsDefaults(tenant, env, stage);
await tunnelExternal(name, { ...merged, tenant, env, stage });
}
);
}
var SERVICE_CONFIG, SERVICE_GROUPS;
var init_tunnel2 = __esm({
"libs/mesh-cli/src/commands/tunnel/index.ts"() {
"use strict";
init_utils();
SERVICE_CONFIG = {
"temporal-frontend": {
bastionKey: "temporal-frontend",
localPort: 7233,
displayName: "Temporal Frontend (gRPC)"
},
"temporal-ui": {
bastionKey: "temporal-ui",
localPort: 8080,
displayName: "Temporal Web UI"
},
db: {
bastionKey: "rds",
localPort: 5432,
displayName: "Database (PostgreSQL)"
}
};
SERVICE_GROUPS = {
dev: ["temporal-frontend", "temporal-ui", "db"],
temporal: ["temporal-frontend", "temporal-ui"]
};
}
});
// libs/mesh-cli/src/commands/vcs/common.ts
import { execFile as execFile4 } from "node:child_process";
import { promisify as promisify3 } from "node:util";
function redactToken(err, token) {
const base = err instanceof Error ? err.message : String(err);
const stderr = typeof err?.stderr === "string" ? err.stderr.trim() : "";
const combined = stderr && !base.includes(stderr) ? `${base}
${stderr}` : base;
const message = token ? combined.split(token).join("<redacted-token>") : combined;
const out = new Error(message);
const code = err?.code;
if (code !== void 0) out.code = code;
return out;
}
async function gitWithAuth(args, token, cwd) {
try {
return await execFileAsync3("git", args, cwd ? { cwd } : {});
} catch (err) {
throw redactToken(err, token);
}
}
function parseRemote(remoteUrl) {
const m = remoteUrl.trim().match(/^(https?:\/\/[^/]+)\/git\/([^/]+?)(?:\.git)?$/);
return m ? { baseUrl: m[1], repo: m[2] } : null;
}
async function resolveTarget2(opts) {
const explicitBase = opts.url ?? process.env.VCS_URL;
let originTarget = null;
try {
const { stdout } = await execFileAsync3("git", ["remote", "get-url", "origin"], {
cwd: opts.cwd ?? process.cwd()
});
originTarget = parseRemote(stdout);
} catch {
}
const baseUrl = (explicitBase ?? originTarget?.baseUrl)?.replace(/\/$/, "");
if (!baseUrl) {
throw new Error(
"vcs service URL not found: pass --url, set VCS_URL, or run inside a vcs clone"
);
}
return { baseUrl, repo: opts.repo ?? originTarget?.repo };
}
async function resolveToken(opts) {
if (opts.token) return opts.token;
if (process.env.VCS_TOKEN) return process.env.VCS_TOKEN;
if (opts.context) {
const { getValidToken: getValidToken2 } = await Promise.resolve().then(() => (init_login(), login_exports));
const token = await getValidToken2(opts.context);
if (token) return token;
}
throw new Error(
"no token: pass --token, set VCS_TOKEN, or pass --context <platform-context> (after mesh login)"
);
}
async function vcsApi(baseUrl, token, path46, method = "GET", body) {
const res = await fetch(`${baseUrl}${path46}`, {
method,
headers: {
authorization: `Bearer ${token}`,
...body !== void 0 ? { "content-type": "application/json" } : {}
},
...body !== void 0 ? { body: JSON.stringify(body) } : {}
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(
`${method} ${path46} failed (${res.status}): ${String(data.error ?? "unknown error")}`
);
}
return data;
}
var execFileAsync3;
var init_common = __esm({
"libs/mesh-cli/src/commands/vcs/common.ts"() {
"use strict";
execFileAsync3 = promisify3(execFile4);
}
});
// libs/mesh-cli/src/commands/vcs/clone.ts
async function cloneCommand(repo, dest, opts) {
const baseUrl = (opts.url ?? process.env.VCS_URL)?.replace(/\/$/, "");
if (!baseUrl) throw new Error("clone requires --url or VCS_URL");
const token = await resolveToken(opts);
await gitWithAuth(
[
"clone",
"-c",
`http.extraHeader=Authorization: Bearer ${token}`,
`${baseUrl}/git/${repo}`,
dest ?? repo
],
token
);
await gitWithAuth(
["config", "http.extraHeader", `Authorization: Bearer ${token}`],
token,
dest ?? repo
);
console.log(
`cloned ${repo} \u2192 ${dest ?? repo} (read-only remote; use 'mesh vcs propose' to submit changes)`
);
}
var init_clone = __esm({
"libs/mesh-cli/src/commands/vcs/clone.ts"() {
"use strict";
init_common();
}
});
// libs/mesh-cli/src/commands/vcs/get.ts
import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
import { join as join42, dirname as dirname29 } from "node:path";
async function getCommand(repo, path46, opts) {
let vcsBaseUrl;
let token;
if (opts.target) {
const t = resolveTarget({ target: opts.target });
vcsBaseUrl = deriveVcsBaseUrl(t.apiBaseUrl);
token = t.token ?? await getValidToken(t.loginContext ?? "mesh.dev") ?? void 0;
} else {
vcsBaseUrl = (opts.url ?? process.env.VCS_URL)?.replace(/\/$/, "") ?? null;
token = await resolveToken(opts);
}
if (!vcsBaseUrl) throw new Error("vcs get requires --target <name> or --url <vcs-url>");
if (!token) throw new Error("no token: pass --target (after mesh login), or --token / --context");
const reader = createVcsFolderReader({ vcsBaseUrl, token });
try {
const { ref, files } = await reader.readPath(repo, path46);
if (files.length === 0) {
logInfo(`no files at ${repo}:${path46}`);
return;
}
if (opts.output) {
for (const f of files) {
const abs = join42(opts.output, f.path);
await mkdir2(dirname29(abs), { recursive: true });
await writeFile2(abs, f.contents);
}
logInfo(`wrote ${files.length} file(s) from ${repo}:${path46} (${ref}) \u2192 ${opts.output}`);
} else {
for (const f of files) {
if (files.length > 1) process.stdout.write(`
=== ${f.path} ===
`);
process.stdout.write(f.contents);
}
}
} finally {
await reader.cleanup();
}
}
var init_get = __esm({
"libs/mesh-cli/src/commands/vcs/get.ts"() {
"use strict";
init_src2();
init_agent_api_client();
init_login();
init_common();
init_log();
}
});
// libs/mesh-cli/src/commands/vcs/drafts.ts
async function call(opts, repo, path46, method = "GET", body) {
const target = await resolveTarget2({ ...opts, repo });
const token = await resolveToken(opts);
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path46}`, method, body);
}
async function draftsListCommand(repo, opts) {
const data = await call(opts, repo, `/drafts${opts.all ? "?all=true" : ""}`);
const drafts = data.drafts;
if (drafts.length === 0) {
console.log("no drafts");
return;
}
for (const d of drafts) {
console.log(`${d.draftId} [${d.state}] ${d.title} (updated ${d.updatedAt})`);
}
}
async function draftsShowCommand(repo, draftId, opts) {
const d = await call(opts, repo, `/drafts/${draftId}`);
console.log(`${d.draftId} [${d.state}] ${d.title}`);
if (d.headSha !== void 0) {
console.log(`head: ${d.headSha}`);
}
if (d.mergeable === false) {
console.log("\u26A0 the page changed while this draft was being edited");
}
if (d.changedFiles) {
for (const f of d.changedFiles) {
console.log(` ${f.status} ${f.path}`);
}
}
}
async function draftsCreateCommand(repo, opts) {
const d = await call(opts, repo, "/drafts", "POST", opts.title ? { title: opts.title } : {});
console.log(`draft ${d.draftId} created \u2014 submit with: mesh vcs drafts submit ${repo} ${d.draftId}`);
}
async function draftsSubmitCommand(repo, draftId, opts) {
const d = await call(
opts,
repo,
`/drafts/${draftId}/submit`,
"POST",
opts.note ? { note: opts.note } : {}
);
console.log(`draft ${d.draftId} submitted for review [${d.state}]`);
}
async function draftsDiscardCommand(repo, draftId, opts) {
const d = await call(opts, repo, `/drafts/${draftId}/discard`, "POST", {});
console.log(`draft ${d.draftId} discarded`);
}
var init_drafts = __esm({
"libs/mesh-cli/src/commands/vcs/drafts.ts"() {
"use strict";
init_common();
}
});
// libs/mesh-cli/src/commands/vcs/propose.ts
import { readFile as readFile3 } from "node:fs/promises";
import { join as join43 } from "node:path";
function parseStatus(out) {
const tokens = out.split("\0");
const changes = [];
let i = 0;
while (i < tokens.length) {
const entry = tokens[i];
if (!entry) {
i++;
continue;
}
const status = entry.slice(0, 2);
const newPath = entry.slice(3);
const isRename = status[0] === "R" || status[1] === "R";
const isCopy = !isRename && (status[0] === "C" || status[1] === "C");
if (isRename || isCopy) {
const origPath = tokens[i + 1] ?? "";
i += 2;
if (isRename && origPath && !origPath.endsWith("/")) {
changes.push({ status: "D", path: origPath });
}
if (newPath && !newPath.endsWith("/")) {
changes.push({ status: "A", path: newPath });
}
continue;
}
i++;
if (newPath.length > 0 && !newPath.endsWith("/")) {
changes.push({ status: status.trim(), path: newPath });
}
}
return changes;
}
async function writeOp(cwd, path46) {
const buf = await readFile3(join43(cwd, path46));
const asUtf8 = buf.toString("utf8");
if (Buffer.from(asUtf8, "utf8").equals(buf)) {
return { op: "write", path: path46, content: asUtf8 };
}
return { op: "write", path: path46, content: buf.toString("base64"), encoding: "base64" };
}
async function proposeCommand(opts) {
const cwd = process.cwd();
const target = await resolveTarget2(opts);
if (!target.repo) {
throw new Error("could not determine repo from the origin remote (pass --url from a clone)");
}
const token = await resolveToken(opts);
const { stdout: statusOut } = await execFileAsync3(
"git",
["status", "--porcelain", "-z", "--untracked-files=all"],
{ cwd }
);
const changes = parseStatus(statusOut);
if (changes.length === 0) {
console.log("working tree clean \u2014 nothing to propose");
return;
}
const operations = await Promise.all(
changes.map(
async (c) => c.status.startsWith("D") ? { op: "delete", path: c.path } : await writeOp(cwd, c.path)
)
);
let baseCommit;
if (opts.revise) {
const state = await vcsApi(
target.baseUrl,
token,
`/v1/repos/${target.repo}/proposals/${opts.revise}`
);
if (!state.headSha) throw new Error(`cannot resolve head of proposal ${opts.revise}`);
baseCommit = state.headSha;
} else {
const { stdout: head } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd });
baseCommit = head.trim();
}
const body = {
...opts.proposalId && !opts.revise ? { proposalId: opts.proposalId } : {},
baseCommit,
message: opts.message ?? `Update ${operations.length} file(s)`,
operations,
...opts.mergeParent ? { mergeParent: opts.mergeParent } : {}
};
const path46 = opts.revise ? `/v1/repos/${target.repo}/proposals/${opts.revise}/revisions` : `/v1/repos/${target.repo}/proposals`;
const data = await vcsApi(target.baseUrl, token, path46, "POST", body);
console.log(
`proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 review: mesh vcs show ${target.repo} ${data.proposalId} --url ${target.baseUrl}`
);
}
var init_propose = __esm({
"libs/mesh-cli/src/commands/vcs/propose.ts"() {
"use strict";
init_common();
}
});
// libs/mesh-cli/src/commands/vcs/rm.ts
import { mkdtemp as mkdtemp2, rm as rmrf } from "node:fs/promises";
import { tmpdir as tmpdir10 } from "node:os";
import { join as join44 } from "node:path";
function normalize2(p) {
return p.trim().replace(/^\.\//, "").replace(/\/+$/, "");
}
function expandPathsToDeletions(repoFiles, requested) {
const out = /* @__PURE__ */ new Set();
for (const raw of requested) {
const path46 = normalize2(raw);
if (path46.startsWith("/") || path46.split("/").includes("..")) {
throw new Error(
`"${raw}" is not a repo-relative path \u2014 vcs paths never start with "/" or traverse with "..".`
);
}
const exact = repoFiles.filter((f) => f === path46);
if (exact.length > 0) {
for (const f of exact) out.add(f);
continue;
}
const under = repoFiles.filter((f) => f.startsWith(`${path46}/`));
if (under.length === 0) {
throw new Error(
`"${raw}" matched no files in the repo \u2014 nothing was proposed. Check the path with: mesh vcs get <repo> ${path46}`
);
}
for (const f of under) out.add(f);
}
return [...out].sort();
}
async function rmCommand(repo, paths, opts) {
const baseUrl = (opts.url ?? process.env.VCS_URL)?.replace(/\/$/, "");
if (!baseUrl) throw new Error("rm requires --url or VCS_URL");
const token = await resolveToken(opts);
const dir = await mkdtemp2(join44(tmpdir10(), "mesh-vcs-rm-"));
try {
const git = (args, cwd) => gitWithAuth(args, token, cwd);
await git([
"clone",
"--filter=blob:none",
"--no-checkout",
"-c",
`http.extraHeader=Authorization: Bearer ${token}`,
`${baseUrl}/git/${repo}`,
dir
]);
const { stdout: treeOut } = await git(["ls-tree", "-r", "--name-only", "HEAD"], dir);
const repoFiles = treeOut.split("\n").filter(Boolean);
const doomed = expandPathsToDeletions(repoFiles, paths);
if (opts.dryRun) {
console.log(`would delete ${doomed.length} file(s) from ${repo}:`);
for (const p of doomed) console.log(` ${p}`);
console.log("(--dry-run: nothing proposed)");
return;
}
const { stdout: head } = await git(["rev-parse", "HEAD"], dir);
const body = {
baseCommit: head.trim(),
message: opts.message ?? `Delete ${doomed.length} file(s)`,
operations: doomed.map((path46) => ({ op: "delete", path: path46 }))
};
const data = await vcsApi(
baseUrl,
token,
`/v1/repos/${repo}/proposals`,
"POST",
body
);
console.log(`deleting ${doomed.length} file(s) from ${repo}:`);
for (const p of doomed) console.log(` ${p}`);
console.log(
`proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 a human must approve: mesh vcs approve ${repo} ${data.proposalId} --url ${baseUrl}`
);
} finally {
await rmrf(dir, { recursive: true, force: true });
}
}
var init_rm = __esm({
"libs/mesh-cli/src/commands/vcs/rm.ts"() {
"use strict";
init_common();
}
});
// libs/mesh-cli/src/commands/vcs/review.ts
async function call2(opts, repo, path46, method = "GET", body) {
const target = await resolveTarget2({ ...opts, repo });
const token = await resolveToken(opts);
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path46}`, method, body);
}
async function proposalsCommand(repo, opts) {
console.log(JSON.stringify(await call2(opts, repo, "/proposals"), null, 2));
}
async function showCommand(repo, id, opts) {
console.log(JSON.stringify(await call2(opts, repo, `/proposals/${id}`), null, 2));
}
async function diffCommand(repo, id, opts) {
const d = await call2(opts, repo, `/proposals/${id}/diff`);
console.log(d.patch);
}
async function approveCommand(repo, id, opts) {
const sha = opts.sha ?? (await call2(opts, repo, `/proposals/${id}`)).headSha;
await call2(opts, repo, `/proposals/${id}/approve`, "POST", { sha });
console.log(`approved ${id} @ ${sha.slice(0, 8)}`);
}
async function rejectCommand(repo, id, opts) {
await call2(opts, repo, `/proposals/${id}/reject`, "POST", { reason: opts.reason });
console.log(`rejected ${id}`);
}
async function commentCommand(repo, id, text, opts) {
await call2(opts, repo, `/proposals/${id}/comment`, "POST", { text });
console.log("comment added");
}
async function requestChangesCommand(repo, id, opts) {
await call2(opts, repo, `/proposals/${id}/request-changes`, "POST", { note: opts.note });
console.log(`changes requested on ${id}`);
}
var init_review = __esm({
"libs/mesh-cli/src/commands/vcs/review.ts"() {
"use strict";
init_common();
}
});
// libs/mesh-cli/src/commands/vcs/index.ts
function withCommon(cmd) {
return cmd.option("--url <url>", "vcs service URL (or VCS_URL, or derived from origin remote)").option("--token <token>", "bearer token (or VCS_TOKEN)").option("--context <context>", "mesh login context to mint a token from");
}
function registerVcsCommands(program2) {
const vcs = program2.command("vcs").description("Versioned content repos (mesh.vcs): clone, get, propose, rm, review");
withCommon(
vcs.command("clone <repo> [dest]").description("Clone a repo (read-only remote)")
).action(cloneCommand);
withCommon(
vcs.command("get <repo> <path>").description("Read a specific path (file or subtree) from a repo \u2014 one doc, not the whole repo").option("--target <name>", "agent target (agent-targets registry) to derive the vcs URL + auth").option("-o, --output <dir>", "write file(s) here instead of stdout")
).action(getCommand);
withCommon(
vcs.command("propose").description("Propose the working tree's changes (run inside a clone)").option("-m, --message <message>", "proposal message").option("--proposal-id <id>", "explicit proposal id").option("--revise <id>", "add a revision to an existing proposal").option("--merge-parent <sha>", "conflict resolution: main sha incorporated")
).action(proposeCommand);
withCommon(
vcs.command("rm <repo> <paths...>").description("Propose deleting files or folders (no clone needed)").option("-m, --message <message>", "proposal message").option("--dry-run", "list what would be deleted, propose nothing")
).action(rmCommand);
withCommon(vcs.command("proposals <repo>").description("List open proposals")).action(
proposalsCommand
);
withCommon(vcs.command("show <repo> <id>").description("Show proposal state")).action(
showCommand
);
withCommon(vcs.command("diff <repo> <id>").description("Show proposal diff")).action(
diffCommand
);
withCommon(
vcs.command("approve <repo> <id>").description("Approve at the current head (payload-bound)").option("--sha <sha>", "approve a specific head sha")
).action(approveCommand);
withCommon(
vcs.command("reject <repo> <id>").description("Reject a proposal").option("--reason <reason>", "rejection reason")
).action(rejectCommand);
withCommon(
vcs.command("comment <repo> <id> <text>").description("Comment on a proposal")
).action(commentCommand);
withCommon(
vcs.command("request-changes <repo> <id>").description("Ask the author for changes (review stays open)").option("--note <note>", "what needs to change")
).action(requestChangesCommand);
const drafts = vcs.command("drafts").description("Work-in-progress drafts");
withCommon(
drafts.command("list <repo>").description("List open drafts").option("--all", "include closed drafts")
).action(draftsListCommand);
withCommon(drafts.command("show <repo> <id>").description("Show draft status")).action(
draftsShowCommand
);
withCommon(
drafts.command("create <repo>").description("Create a draft").option("--title <title>", "draft title")
).action(draftsCreateCommand);
withCommon(
drafts.command("submit <repo> <id>").description("Submit a draft for review").option("--note <note>", "note to reviewers")
).action(draftsSubmitCommand);
withCommon(drafts.command("discard <repo> <id>").description("Discard a draft")).action(
draftsDiscardCommand
);
}
var init_vcs = __esm({
"libs/mesh-cli/src/commands/vcs/index.ts"() {
"use strict";
init_clone();
init_get();
init_drafts();
init_propose();
init_rm();
init_review();
}
});
// libs/workflow-model/src/workflow-ir.ts
function computeHappyPath(nodes, edges) {
const startNode = nodes.find((n) => n.type === "start");
const endNodes = nodes.filter((n) => n.type === "end");
if (!startNode || endNodes.length === 0) return [];
const adjacency = /* @__PURE__ */ new Map();
for (const edge of edges) {
const list = adjacency.get(edge.from) ?? [];
list.push(edge);
adjacency.set(edge.from, list);
}
const waitNodeIds = new Set(nodes.filter((n) => n.type === "wait").map((n) => n.id));
function pathHasWait(startId) {
const visited = /* @__PURE__ */ new Set();
const queue = [startId];
for (let depth = 0; depth < 6 && queue.length > 0; depth++) {
const next = [];
for (const id of queue) {
if (waitNodeIds.has(id)) return true;
if (visited.has(id)) continue;
visited.add(id);
for (const e of adjacency.get(id) ?? []) {
if (!e.isExceptional && !e.isTimeout) next.push(e.to);
}
}
queue.length = 0;
queue.push(...next);
}
return false;
}
const successEndIds = new Set(
endNodes.filter((n) => n.outcome !== "failure").map((n) => n.id)
);
const failureEndIds = new Set(
endNodes.filter((n) => n.outcome === "failure").map((n) => n.id)
);
const allEndIds = new Set(endNodes.map((n) => n.id));
function bfs(targetIds, allowExceptional) {
const queue = [
{ id: startNode.id, path: [startNode.id] }
];
const visited = /* @__PURE__ */ new Set([startNode.id]);
while (queue.length > 0) {
const { id, path: path46 } = queue.shift();
if (targetIds.has(id)) return path46;
const neighbors = adjacency.get(id) ?? [];
const hasMainEdge = neighbors.some((e) => e.isMainPath);
const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
const sorted = [...candidates].sort((a, b) => {
const aErr = a.isExceptional || a.isTimeout;
const bErr = b.isExceptional || b.isTimeout;
if (aErr && !bErr) return 1;
if (!aErr && bErr) return -1;
const aWait = waitNodeIds.has(a.to) || pathHasWait(a.to);
const bWait = waitNodeIds.has(b.to) || pathHasWait(b.to);
if (aWait && !bWait) return 1;
if (!aWait && bWait) return -1;
return 0;
});
for (const neighbor of sorted) {
if (visited.has(neighbor.to)) continue;
if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
continue;
visited.add(neighbor.to);
queue.push({ id: neighbor.to, path: [...path46, neighbor.to] });
}
}
return null;
}
return bfs(successEndIds, false) ?? bfs(allEndIds, false) ?? bfs(allEndIds, true) ?? [];
}
function isProcessArtifactProvenance(value) {
return typeof value === "object" && value !== null && value.source === "process-artifact";
}
function childGraphsOf(node) {
const graphs = [];
const withChild = node;
if (withChild.childGraph) graphs.push(withChild.childGraph);
const withBacking = node;
if (withBacking.backing?.backingGraph) graphs.push(withBacking.backing.backingGraph);
return graphs;
}
function walkProcessNodes(ir, scope = []) {
const out = [];
for (const node of ir.nodes) {
if (isProcessArtifactProvenance(node.provenance)) {
out.push({ nodeId: node.id, scope, node, provenance: node.provenance });
}
for (const child of childGraphsOf(node)) {
out.push(...walkProcessNodes(child, [...scope, node.id]));
}
}
return out;
}
function findProcessNodesByCommand(ir, command) {
return walkProcessNodes(ir).filter((m) => m.provenance.bind?.commands?.includes(command));
}
function findProcessNodesByElementId(ir, elementId, element) {
return walkProcessNodes(ir).filter(
(m) => m.provenance.elementId === elementId && (element === void 0 || m.provenance.element === element)
);
}
function cloneWorkflow(workflow) {
return JSON.parse(JSON.stringify(workflow));
}
function cloneMaybeWorkflow(workflow) {
return workflow ? cloneWorkflow(workflow) : null;
}
function upsertAtIndex(items, item, index) {
const next = [...items];
if (index === void 0 || index < 0 || index >= next.length) {
next.push(item);
return next;
}
next.splice(index, 0, item);
return next;
}
function edgeMatches(edge, from, to) {
return edge.from === from && edge.to === to;
}
function ensureNodeExists(workflow, nodeId) {
if (!workflow.nodes.some((node) => node.id === nodeId)) {
throw new WorkflowPatchError(`Node not found: ${nodeId}`);
}
}
function ensureNoDuplicateNode(workflow, nodeId) {
if (workflow.nodes.some((node) => node.id === nodeId)) {
throw new WorkflowPatchError(`Duplicate node id: ${nodeId}`);
}
}
function isDuplicateEdge(workflow, edge) {
return workflow.edges.some(
(existing) => existing.from === edge.from && existing.to === edge.to && existing.fromExit === edge.fromExit
);
}
function sanitizeMainPath(workflow, mainPath) {
const nodeIds = new Set(workflow.nodes.map((node) => node.id));
const seen = /* @__PURE__ */ new Set();
const normalized = [];
for (const nodeId of mainPath) {
if (!nodeIds.has(nodeId)) {
throw new WorkflowPatchError(`Main path references unknown node: ${nodeId}`);
}
if (seen.has(nodeId)) {
continue;
}
seen.add(nodeId);
normalized.push(nodeId);
}
return normalized;
}
function getChildGraph(node) {
if ("childGraph" in node && node.childGraph) return node.childGraph;
if ("backing" in node && node.backing?.backingGraph)
return node.backing.backingGraph;
return null;
}
function setChildGraph(node, childGraph) {
if ("childGraph" in node) return { ...node, childGraph };
if ("backing" in node && node.backing?.backingGraph) {
return {
...node,
backing: { ...node.backing, backingGraph: childGraph }
};
}
return { ...node, childGraph };
}
function applyScopedPatch(workflow, scope, patch) {
const [headId, ...rest] = scope;
const next = cloneWorkflow(workflow);
const nodeIndex = next.nodes.findIndex((n) => n.id === headId);
if (nodeIndex === -1) {
throw new WorkflowPatchError(`Scoped patch: node '${headId}' not found in current graph`);
}
const node = next.nodes[nodeIndex];
let childGraph = getChildGraph(node);
if (!childGraph) {
const canHoldChildGraph = node.type === "group" || node.type === "child_workflow";
if (!canHoldChildGraph) {
throw new WorkflowPatchError(
`Scoped patch: node '${headId}' (type '${node.type}') cannot hold a child graph`
);
}
childGraph = { name: "", description: "", nodes: [], edges: [], mainPath: [] };
}
const unscopedPatch = { ...patch, scope: rest.length > 0 ? rest : void 0 };
const updatedChild = applyWorkflowPatch(childGraph, unscopedPatch);
next.nodes[nodeIndex] = setChildGraph(node, updatedChild);
return next;
}
function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
if (nodeIds.length === 0) throw new WorkflowPatchError("group: nodeIds is empty");
const inSet = new Set(nodeIds);
if (workflow.nodes.some((n) => n.id === groupId)) {
throw new WorkflowPatchError(`group: id '${groupId}' already exists`);
}
for (const id of nodeIds) {
const node = workflow.nodes.find((n) => n.id === id);
if (!node) throw new WorkflowPatchError(`group: node '${id}' not found`);
if (node.type === "start" || node.type === "end") {
throw new WorkflowPatchError(`group: cannot wrap a '${node.type}' node ('${id}')`);
}
}
const internal = [];
const incoming = [];
const outgoing = [];
const external = [];
for (const e of workflow.edges) {
const f = inSet.has(e.from);
const t = inSet.has(e.to);
if (f && t) internal.push(e);
else if (!f && t) incoming.push(e);
else if (f && !t) outgoing.push(e);
else external.push(e);
}
const entryNodes = new Set(incoming.map((e) => e.to));
if (entryNodes.size === 0) {
const internalIn = new Set(internal.map((e) => e.to));
for (const id of nodeIds) if (!internalIn.has(id)) entryNodes.add(id);
}
const exitNodes = new Set(outgoing.map((e) => e.from));
if (exitNodes.size === 0) {
const internalOut = new Set(internal.map((e) => e.from));
for (const id of nodeIds) if (!internalOut.has(id)) exitNodes.add(id);
}
if (entryNodes.size !== 1) {
throw new WorkflowPatchError(
`group: the selection has ${entryNodes.size} entry points; wrap a single-entry region`
);
}
const entry = [...entryNodes][0];
const startId = `${groupId}__start`;
const groupedNodes = workflow.nodes.filter((n) => inSet.has(n.id));
const inParentMain = workflow.mainPath.filter((id) => inSet.has(id));
let childGraph;
let parentExitEdges;
const pulledIds = /* @__PURE__ */ new Set();
if (outgoing.length === 0) {
const endId = `${groupId}__end`;
const childNodes = [
{ id: startId, type: "start", label: "Start" },
...groupedNodes,
{ id: endId, type: "end", label: "Done", outcome: "success" }
];
const childEdges = [
{ from: startId, to: entry, isMainPath: true },
...internal,
...[...exitNodes].map((from) => ({ from, to: endId, isMainPath: true }))
];
childGraph = {
name: label,
description: "",
nodes: childNodes,
edges: childEdges,
mainPath: [startId, ...inParentMain.length ? inParentMain : nodeIds, endId]
};
parentExitEdges = [];
} else {
const byTarget = /* @__PURE__ */ new Map();
for (const e of outgoing) {
const arr = byTarget.get(e.to);
if (arr) arr.push(e);
else byTarget.set(e.to, [e]);
}
const isSoleRefTerminal = (target) => {
const node = workflow.nodes.find((n) => n.id === target);
if (!node || node.type !== "end") return false;
return workflow.edges.every((e) => e.to !== target || inSet.has(e.from));
};
const toChildEdge = (e, to) => ({
from: e.from,
to,
...e.label ? { label: e.label } : {},
...e.isMainPath ? { isMainPath: true } : {},
...e.isExceptional ? { isExceptional: true } : {},
...e.isTimeout ? { isTimeout: true } : {}
});
const extraChildNodes = [];
const exitChildEdges = [];
parentExitEdges = [];
let exitN = 0;
for (const [target, edges] of byTarget) {
if (isSoleRefTerminal(target)) {
const endNode = workflow.nodes.find((n) => n.id === target);
extraChildNodes.push(endNode);
pulledIds.add(target);
for (const e of edges) exitChildEdges.push(toChildEdge(e, target));
} else {
exitN += 1;
const exitId = `${groupId}__exit_${exitN}`;
const first = edges[0];
const anyMain = edges.some((e) => e.isMainPath);
const allExceptional = edges.every((e) => e.isExceptional || e.isTimeout);
const exitLabel = first.label ? first.label.charAt(0).toUpperCase() + first.label.slice(1) : workflow.nodes.find((n) => n.id === target)?.label ?? `Exit ${exitN}`;
extraChildNodes.push({
id: exitId,
type: "end",
label: exitLabel,
outcome: allExceptional ? "failure" : "success"
});
for (const e of edges) exitChildEdges.push(toChildEdge(e, exitId));
parentExitEdges.push({
from: groupId,
to: target,
fromExit: exitId,
...first.label ? { label: first.label } : {},
...anyMain ? { isMainPath: true } : {},
...!anyMain && edges.every((e) => e.isExceptional) ? { isExceptional: true } : {},
...!anyMain && edges.every((e) => e.isTimeout) ? { isTimeout: true } : {}
});
}
}
const successExit = extraChildNodes.find(
(n) => n.type === "end" && !pulledIds.has(n.id) && n.outcome === "success"
);
const mainEdgeKeys = /* @__PURE__ */ new Set();
const edgeKey = (a, b) => `${a}\0${b}`;
let mainBody = inParentMain.length ? inParentMain : nodeIds;
let mainTail = [];
if (successExit) {
const exitSources = new Set(
exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from)
);
const adj = /* @__PURE__ */ new Map();
for (const e of internal) {
if (e.isExceptional || e.isTimeout) continue;
const arr = adj.get(e.from);
if (arr) arr.push(e.to);
else adj.set(e.from, [e.to]);
}
const prev = /* @__PURE__ */ new Map();
const visited = /* @__PURE__ */ new Set([entry]);
const queue = [entry];
let found;
while (queue.length > 0) {
const cur = queue.shift();
if (exitSources.has(cur)) {
found = cur;
break;
}
for (const nx of adj.get(cur) ?? []) {
if (!visited.has(nx)) {
visited.add(nx);
prev.set(nx, cur);
queue.push(nx);
}
}
}
if (found) {
const path46 = [];
for (let c = found; c !== void 0; c = prev.get(c)) path46.unshift(c);
for (let i = 0; i < path46.length - 1; i++) mainEdgeKeys.add(edgeKey(path46[i], path46[i + 1]));
mainEdgeKeys.add(edgeKey(found, successExit.id));
mainBody = path46;
mainTail = [successExit.id];
}
}
const markMain = (e) => mainEdgeKeys.has(edgeKey(e.from, e.to)) ? { ...e, isMainPath: true } : { ...e };
childGraph = {
name: label,
description: "",
nodes: [
{ id: startId, type: "start", label: "Start" },
...groupedNodes,
...extraChildNodes
],
edges: [
{ from: startId, to: entry, isMainPath: true },
...internal.map(markMain),
...exitChildEdges.map(markMain)
],
mainPath: [startId, ...mainBody, ...mainTail]
};
}
const groupNode = {
id: groupId,
type: "group",
groupType,
label,
childGraph
};
const newNodes = [];
let placed = false;
for (const n of workflow.nodes) {
if (inSet.has(n.id)) {
if (!placed) {
newNodes.push(groupNode);
placed = true;
}
} else if (!pulledIds.has(n.id)) {
newNodes.push(n);
}
}
const seen = /* @__PURE__ */ new Set();
const newEdges = [];
const pushEdge = (e) => {
const key = `${e.from} ${e.to} ${e.fromExit ?? ""} ${e.label ?? ""}`;
if (seen.has(key)) return;
seen.add(key);
newEdges.push(e);
};
for (const e of external) pushEdge(e);
for (const e of incoming) pushEdge({ ...e, to: groupId });
for (const e of parentExitEdges) pushEdge(e);
const newMain = [];
let mainInserted = false;
for (const id of workflow.mainPath) {
if (inSet.has(id)) {
if (!mainInserted) {
newMain.push(groupId);
mainInserted = true;
}
} else {
newMain.push(id);
}
}
return { ...workflow, nodes: newNodes, edges: newEdges, mainPath: newMain };
}
function emptyWorkflowIR(name = "untitled") {
return { name, description: "", nodes: [], edges: [], mainPath: [] };
}
function applyWorkflowPatch(workflow, patch) {
if (patch.scope && patch.scope.length > 0) {
return applyScopedPatch(workflow, patch.scope, patch);
}
if (patch.op === "batch") {
return patch.patches.reduce(
(current, operation) => applyWorkflowPatch(current, operation),
workflow
);
}
if (patch.op === "replace") {
return cloneWorkflow(patch.workflow);
}
const next = cloneWorkflow(workflow);
switch (patch.op) {
case "addNode": {
ensureNoDuplicateNode(next, patch.node.id);
next.nodes = upsertAtIndex(next.nodes, patch.node, patch.index);
return next;
}
case "updateNode": {
const index = next.nodes.findIndex((node) => node.id === patch.nodeId);
if (index === -1) {
throw new WorkflowPatchError(`Node not found: ${patch.nodeId}`);
}
const existing = next.nodes.at(index);
if (!existing) {
throw new WorkflowPatchError(`Node not found at index: ${index}`);
}
next.nodes[index] = {
...existing,
...patch.patch,
id: existing.id
};
return next;
}
case "removeNode": {
ensureNodeExists(next, patch.nodeId);
next.nodes = next.nodes.filter((node) => node.id !== patch.nodeId);
next.mainPath = next.mainPath.filter((nodeId) => nodeId !== patch.nodeId);
const shouldRemoveEdges = patch.removeAttachedEdges ?? true;
if (shouldRemoveEdges) {
next.edges = next.edges.filter(
(edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId
);
}
return next;
}
case "addEdge": {
ensureNodeExists(next, patch.from);
ensureNodeExists(next, patch.to);
const edge = {
from: patch.from,
to: patch.to,
...patch.fromExit !== void 0 ? { fromExit: patch.fromExit } : {},
...patch.label !== void 0 ? { label: patch.label } : {},
...patch.isMainPath !== void 0 ? { isMainPath: patch.isMainPath } : {},
...patch.isExceptional !== void 0 ? { isExceptional: patch.isExceptional } : {},
...patch.isTimeout !== void 0 ? { isTimeout: patch.isTimeout } : {}
};
if (isDuplicateEdge(next, edge)) {
return next;
}
next.edges = upsertAtIndex(next.edges, edge, patch.index);
return next;
}
case "updateEdge": {
const index = next.edges.findIndex((edge) => edgeMatches(edge, patch.from, patch.to));
if (index === -1) {
throw new WorkflowPatchError(`Edge not found: ${patch.from} -> ${patch.to}`);
}
const existing = next.edges.at(index);
if (!existing) {
throw new WorkflowPatchError(`Edge not found at index: ${index}`);
}
next.edges[index] = {
...existing,
...patch.patch,
from: patch.from,
to: patch.to
};
return next;
}
case "removeEdge": {
const initialLength = next.edges.length;
next.edges = next.edges.filter((edge) => !edgeMatches(edge, patch.from, patch.to));
if (next.edges.length === initialLength) {
throw new WorkflowPatchError(`Edge not found: ${patch.from} -> ${patch.to}`);
}
return next;
}
case "group": {
return wrapIntoGroup(
next,
patch.nodeIds,
patch.groupId,
patch.label,
patch.groupType ?? "function"
);
}
case "setMainPath": {
next.mainPath = sanitizeMainPath(next, patch.mainPath);
return next;
}
case "setUiSpecs": {
next.uiSpecs = { ...next.uiSpecs, ...patch.uiSpecs };
return next;
}
case "setMeta": {
if (patch.name !== void 0) next.name = patch.name;
if (patch.description !== void 0) next.description = patch.description;
if (patch.signals !== void 0) next.signals = patch.signals;
if (patch.changes !== void 0) next.changes = patch.changes;
return next;
}
default:
return next;
}
}
function applyWorkflowPatches(workflow, patches) {
return patches.reduce((current, patch) => applyWorkflowPatch(current, patch), workflow);
}
function validateWorkflowPatches(ir, patches) {
const errors = [];
let clone = cloneWorkflow(ir);
patches.forEach((patch, index) => {
try {
clone = applyWorkflowPatch(clone, patch);
} catch (err) {
errors.push({
index,
op: patch.op ?? "?",
message: err instanceof Error ? err.message : String(err)
});
}
});
return errors;
}
var WorkflowPatchError, WorkflowPatchStream;
var init_workflow_ir = __esm({
"libs/workflow-model/src/workflow-ir.ts"() {
"use strict";
WorkflowPatchError = class extends Error {
constructor(message) {
super(message);
this.name = "WorkflowPatchError";
}
};
WorkflowPatchStream = class {
currentWorkflow;
undoStack = [];
redoStack = [];
constructor(initialWorkflow = null) {
this.currentWorkflow = cloneMaybeWorkflow(initialWorkflow);
}
get current() {
return cloneMaybeWorkflow(this.currentWorkflow);
}
get canUndo() {
return this.undoStack.length > 0;
}
get canRedo() {
return this.redoStack.length > 0;
}
reset(workflow = null) {
this.currentWorkflow = cloneMaybeWorkflow(workflow);
this.undoStack = [];
this.redoStack = [];
return this.current;
}
apply(patchOrPatches) {
const patches = Array.isArray(patchOrPatches) ? patchOrPatches : [patchOrPatches];
if (patches.length === 0) {
if (!this.currentWorkflow) {
throw new WorkflowPatchError("Cannot apply empty patch list without an initial workflow");
}
return cloneWorkflow(this.currentWorkflow);
}
const previous = cloneMaybeWorkflow(this.currentWorkflow);
let next = cloneMaybeWorkflow(this.currentWorkflow);
for (const patch of patches) {
if (!next) {
if (patch.op !== "replace") {
throw new WorkflowPatchError(
"Patch stream is empty. First patch must be a replace operation."
);
}
next = cloneWorkflow(patch.workflow);
continue;
}
next = applyWorkflowPatch(next, patch);
}
if (!next) {
throw new WorkflowPatchError("Patch application produced an empty workflow");
}
this.undoStack.push(previous);
this.redoStack = [];
this.currentWorkflow = next;
return cloneWorkflow(next);
}
undo() {
if (!this.canUndo) {
return this.current;
}
const previous = this.undoStack.pop() ?? null;
this.redoStack.push(cloneMaybeWorkflow(this.currentWorkflow));
this.currentWorkflow = cloneMaybeWorkflow(previous);
return this.current;
}
redo() {
if (!this.canRedo) {
return this.current;
}
const next = this.redoStack.pop() ?? null;
this.undoStack.push(cloneMaybeWorkflow(this.currentWorkflow));
this.currentWorkflow = cloneMaybeWorkflow(next);
return this.current;
}
};
}
});
// libs/workflow-model/src/workflow-ir-validate.ts
function outgoingEdges(edges, nodeId) {
return edges.filter((e) => e.from === nodeId);
}
function incomingEdges(edges, nodeId) {
return edges.filter((e) => e.to === nodeId);
}
function hasFailureEnd(workflow) {
return workflow.nodes.some(
(n) => n.type === "end" && n.outcome === "failure"
);
}
function hasExceptionalEdge(edges, nodeId) {
return edges.some((e) => e.from === nodeId && e.isExceptional);
}
function validateWorkflowIR(workflow, options) {
const maxDepth = options?.maxDepth ?? 10;
const diagnostics = [];
collectDiagnostics(workflow, diagnostics, 0, maxDepth);
const byNodeId = /* @__PURE__ */ new Map();
for (const d of diagnostics) {
const existing = byNodeId.get(d.nodeId);
if (existing) {
existing.push(d);
} else {
byNodeId.set(d.nodeId, [d]);
}
}
return { diagnostics, byNodeId };
}
function collectDiagnostics(workflow, out, depth, maxDepth) {
if (depth > maxDepth) return;
const { nodes, edges } = workflow;
const nodeMap = /* @__PURE__ */ new Map();
for (const node of nodes) {
nodeMap.set(node.id, node);
}
for (const node of nodes) {
const outEdges = outgoingEdges(edges, node.id);
const inEdges = incomingEdges(edges, node.id);
if (node.type === "wait" && node.timeout) {
if (!outEdges.some((e) => e.isTimeout)) {
out.push({
code: "WAIT_NO_TIMEOUT_EDGE",
severity: "warning",
nodeId: node.id,
message: `Wait node "${node.label}" has timeout (${node.timeout}) but no timeout edge.`
});
}
}
if (node.type === "decision" && outEdges.length < 2) {
out.push({
code: "DECISION_DEAD_END",
severity: "warning",
nodeId: node.id,
message: `Decision node "${node.label}" has ${outEdges.length} outgoing edge(s); expected at least 2.`
});
}
if (node.type === "child_workflow" && node.childGraph) {
if (hasFailureEnd(node.childGraph) && !hasExceptionalEdge(edges, node.id)) {
out.push({
code: "CHILD_FAILURE_UNHANDLED",
severity: "warning",
nodeId: node.id,
message: `Child workflow "${node.label}" can fail but parent has no error edge.`
});
}
collectDiagnostics(node.childGraph, out, depth + 1, maxDepth);
}
if (node.type === "nexus_operation" && node.backing?.backingGraph) {
if (hasFailureEnd(node.backing.backingGraph) && !hasExceptionalEdge(edges, node.id)) {
out.push({
code: "NEXUS_FAILURE_UNHANDLED",
severity: "warning",
nodeId: node.id,
message: `Nexus operation "${node.label}" backing graph can fail but parent has no error edge.`
});
}
collectDiagnostics(node.backing.backingGraph, out, depth + 1, maxDepth);
}
if (node.type !== "start" && inEdges.length === 0 && edges.length > 0) {
out.push({
code: "UNREACHABLE_NODE",
severity: "error",
nodeId: node.id,
message: `Node "${node.label}" has no incoming edges and is unreachable.`
});
}
if (node.type !== "end" && outEdges.length === 0 && edges.length > 0) {
out.push({
code: "TERMINAL_NON_END",
severity: "error",
nodeId: node.id,
message: `Non-terminal node "${node.label}" has no outgoing edges (dead end).`
});
}
}
for (const edge of edges) {
if (edge.isTimeout) {
const sourceNode = nodeMap.get(edge.from);
if (sourceNode && sourceNode.type !== "wait") {
out.push({
code: "TIMEOUT_ON_NON_WAIT",
severity: "warning",
nodeId: edge.from,
message: `Edge from "${sourceNode.label}" to "${nodeMap.get(edge.to)?.label ?? edge.to}" has isTimeout but source is a ${sourceNode.type} node, not a wait node.`
});
}
}
}
}
var init_workflow_ir_validate = __esm({
"libs/workflow-model/src/workflow-ir-validate.ts"() {
"use strict";
}
});
// libs/workflow-model/src/apply-preview-patch.ts
function applyPreviewPatch(ir, patch) {
try {
return applyWorkflowPatch(ir, patch);
} catch {
return ir;
}
}
var init_apply_preview_patch = __esm({
"libs/workflow-model/src/apply-preview-patch.ts"() {
"use strict";
init_workflow_ir();
}
});
// libs/workflow-model/src/change-counts.ts
function countChanges(changes) {
if (!changes) return null;
const counts = {
asBuilt: zero(),
asPractised: zero(),
total: changes.length,
newSites: 0
};
const sites = /* @__PURE__ */ new Set();
for (const change of changes) {
const face = change.face === "as-practised" ? counts.asPractised : counts.asBuilt;
face[change.kind] += 1;
face.total += 1;
if (change.kind === "add") sites.add(change.site);
}
counts.newSites = sites.size;
return counts;
}
function describeChanges(counts) {
if (!counts) return "no change list yet \u2014 nothing has counted what this proposal changes";
if (counts.total === 0) return "no changes \u2014 this proposal is the captures, unaltered";
const surface = counts.newSites === 0 ? "no new places for work to land" : `${counts.newSites} new place${counts.newSites === 1 ? "" : "s"} for work to land`;
const faces = [];
if (counts.asBuilt.total > 0) {
faces.push(`${counts.asBuilt.total} to what the system does`);
}
if (counts.asPractised.total > 0) {
faces.push(`${counts.asPractised.total} to what people do`);
}
return `${surface} \xB7 ${faces.join(", ")}`;
}
var zero;
var init_change_counts = __esm({
"libs/workflow-model/src/change-counts.ts"() {
"use strict";
zero = () => ({ extend: 0, add: 0, remove: 0, rewire: 0, total: 0 });
}
});
// libs/workflow-model/src/process-artifact.ts
import { z as z4 } from "zod";
function outcomeTriggers(outcome) {
const raw = Array.isArray(outcome.when) ? outcome.when : [outcome.when];
return raw.map((trigger) => {
if ("predicate" in trigger) return { kind: "predicate", name: trigger.predicate };
if ("timeoutOf" in trigger) return { kind: "timeout", name: trigger.timeoutOf };
return { kind: "command", name: trigger.command };
});
}
function resolveSubstepActor(artifact, stage, substep) {
const actorId = substep.actor ?? stage.actor;
if (!actorId) return void 0;
const actor = artifact.process.actors[actorId];
if (!actor) return void 0;
return {
id: actorId,
label: actor.label,
...actor.category ? { category: actor.category } : {},
...substep.selector ?? actor.selector ? { selector: substep.selector ?? actor.selector } : {},
...actor.cardinality ? { cardinality: actor.cardinality } : {}
};
}
function resolveActorRef(artifact, actorId) {
const actor = artifact.process.actors[actorId];
if (!actor) return void 0;
return {
id: actorId,
label: actor.label,
...actor.category ? { category: actor.category } : {},
...actor.selector ? { selector: actor.selector } : {},
...actor.cardinality ? { cardinality: actor.cardinality } : {}
};
}
function buildProcessManifest(artifact, outcomeNodeIds) {
const { process: process2 } = artifact;
const actors = {};
for (const actorId of Object.keys(process2.actors)) {
actors[actorId] = resolveActorRef(artifact, actorId);
}
let selectors;
if (process2.selectors) {
selectors = {};
for (const [name, selector] of Object.entries(process2.selectors)) {
selectors[name] = {
name,
label: selector.label,
...selector.description ? { description: selector.description } : {}
};
}
}
const outcomes = process2.outcomes.map((outcome) => {
const nodeId = outcomeNodeIds?.get(outcome.id);
return {
id: outcome.id,
label: outcome.label,
kind: outcome.kind,
triggers: outcomeTriggers(outcome),
...outcome.description ? { description: outcome.description } : {},
...nodeId ? { nodeId } : {}
};
});
const pointOfNoReturnStage = process2.stages.find((stage) => stage.pointOfNoReturn);
return {
source: "process-artifact",
artifactVersion: process2.version,
revision: process2.revision,
workflowType: process2.workflowType,
actors,
...selectors ? { selectors } : {},
outcomes,
...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
};
}
function formatPath(path46) {
return path46.length > 0 ? z4.core.toDotPath(path46) : "(root)";
}
function issueToDiagnostic(issue) {
const path46 = formatPath(issue.path);
let code = "SCHEMA_INVALID";
if (issue.code === "custom") {
const paramCode = issue.params?.code;
if (typeof paramCode === "string") {
code = paramCode;
}
}
return {
severity: "error",
code,
message: `${path46}: ${issue.message}`,
path: path46
};
}
function upgradeV1(artifact) {
const diagnostics = [
{
severity: "warning",
code: "DEPRECATED_ARTIFACT_VERSION",
message: `process.version: artifact is version 1; upgraded in memory to version ${CURRENT_PROCESS_VERSION}. Migrate the file (add process.revision, declare actor categories/selectors) \u2014 version 1 is a deprecated input, not a second authoring format.`,
path: "process.version"
},
{
severity: "warning",
code: "MISSING_PROCESS_REVISION",
message: `process.revision: version 1 has no revision; recorded as "${UNVERSIONED_REVISION}". Anything pinned to this artifact cannot name which version of the process it ran.`,
path: "process.revision"
}
];
const process2 = artifact.process;
const actors = {};
for (const [actorId, actor] of Object.entries(process2.actors)) {
const legacyCategory = LEGACY_V1_ACTOR_CATEGORY[actorId];
actors[actorId] = {
...actor,
...actor.category === void 0 && legacyCategory ? { category: legacyCategory } : {}
};
}
return {
artifact: {
...artifact,
process: {
...process2,
version: CURRENT_PROCESS_VERSION,
revision: UNVERSIONED_REVISION,
actors
}
},
diagnostics
};
}
function parseProcessArtifact(json) {
const result = processArtifactSchema.safeParse(json);
if (!result.success) {
return { diagnostics: result.error.issues.map(issueToDiagnostic) };
}
if (result.data.process.version < CURRENT_PROCESS_VERSION) {
return upgradeV1(result.data);
}
return { artifact: result.data, diagnostics: [] };
}
var CURRENT_PROCESS_VERSION, SUPPORTED_PROCESS_VERSIONS, UNVERSIONED_REVISION, LEGACY_V1_ACTOR_CATEGORY, actorSchema, selectorSchema, substepBindSchema, substepSchema, outcomeTriggerSchema, outcomeWhenSchema, outcomeSchema, stageCompleteSchema, stageSchema, processBlockSchema, processArtifactSchema;
var init_process_artifact = __esm({
"libs/workflow-model/src/process-artifact.ts"() {
"use strict";
CURRENT_PROCESS_VERSION = 2;
SUPPORTED_PROCESS_VERSIONS = [1, 2];
UNVERSIONED_REVISION = "unversioned";
LEGACY_V1_ACTOR_CATEGORY = {
customer: "human",
banker: "approval",
system: "data"
};
actorSchema = z4.object({
label: z4.string(),
category: z4.string().min(1).optional(),
selector: z4.string().min(1).optional(),
cardinality: z4.enum(["one", "many"]).optional()
}).strict();
selectorSchema = z4.object({
label: z4.string(),
description: z4.string().optional()
}).strict();
substepBindSchema = z4.object({
commands: z4.array(z4.string()).optional(),
activities: z4.array(z4.string()).optional()
}).strict().superRefine((bind, ctx) => {
if (!bind.commands?.length && !bind.activities?.length) {
ctx.addIssue({
code: "custom",
message: "bind must include at least one of commands or activities",
params: { code: "EMPTY_SUBSTEP_BIND" }
});
}
});
substepSchema = z4.object({
id: z4.string(),
label: z4.string(),
actor: z4.string().optional(),
/**
* Overrides the actor's own `selector` for this substep only — the case
* where one cast entry is resolved differently at one step (a handoff
* target, an escalation pool). Must be declared in `process.selectors`.
*/
selector: z4.string().min(1).optional(),
bind: substepBindSchema
}).strict();
outcomeTriggerSchema = z4.union([
z4.object({ predicate: z4.string().min(1) }).strict(),
z4.object({ timeoutOf: z4.string().min(1) }).strict(),
z4.object({ command: z4.string().min(1) }).strict()
]);
outcomeWhenSchema = z4.union([
outcomeTriggerSchema,
z4.array(outcomeTriggerSchema).superRefine((triggers, ctx) => {
if (triggers.length === 0) {
ctx.addIssue({
code: "custom",
message: "when must declare at least one trigger",
params: { code: "EMPTY_OUTCOME_TRIGGER" }
});
}
})
]);
outcomeSchema = z4.object({
id: z4.string(),
label: z4.string(),
kind: z4.enum(["success", "failure"]),
when: outcomeWhenSchema,
description: z4.string().optional()
}).strict();
stageCompleteSchema = z4.object({
predicate: z4.string()
}).strict();
stageSchema = z4.object({
id: z4.string(),
label: z4.string(),
description: z4.string().optional(),
actor: z4.string().optional(),
substeps: z4.array(substepSchema).optional(),
complete: stageCompleteSchema,
pointOfNoReturn: z4.literal(true).optional()
}).strict();
processBlockSchema = z4.object({
version: z4.number(),
/**
* Pinned identity of this authored content. Opaque to the platform —
* a date, a semver, a content hash, a monotonic counter all work. What
* matters is that changing the process changes it, so an execution can
* be pinned to the revision it started under. Required from V2; a V1
* artifact is upgraded with {@link UNVERSIONED_REVISION} and a warning.
*/
revision: z4.string().min(1).optional(),
workflowType: z4.string(),
actors: z4.record(z4.string(), actorSchema),
/** Named selector registry — every `selector` reference resolves here. */
selectors: z4.record(z4.string(), selectorSchema).optional(),
stages: z4.array(stageSchema),
outcomes: z4.array(outcomeSchema),
internalCommands: z4.array(z4.string()).optional()
}).strict().superRefine((process2, ctx) => {
if (!SUPPORTED_PROCESS_VERSIONS.includes(process2.version)) {
ctx.addIssue({
code: "custom",
path: ["version"],
message: `Unsupported process.version ${JSON.stringify(process2.version)}; this parser understands ${SUPPORTED_PROCESS_VERSIONS.join(" and ")}. Upgrade the parser or downgrade the artifact.`,
params: { code: "UNSUPPORTED_VERSION" }
});
}
if (process2.version >= CURRENT_PROCESS_VERSION && process2.revision === void 0) {
ctx.addIssue({
code: "custom",
path: ["revision"],
message: `process.revision is required from version ${CURRENT_PROCESS_VERSION}. Give this authored content a pinned identity (a date, a semver, or a content hash) so an execution can name the revision it is running.`,
params: { code: "MISSING_PROCESS_REVISION" }
});
}
const declaredSelectors = new Set(Object.keys(process2.selectors ?? {}));
const checkActor = (actorId, path46) => {
if (actorId === void 0) return;
if (!Object.prototype.hasOwnProperty.call(process2.actors, actorId)) {
ctx.addIssue({
code: "custom",
path: path46,
message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path46)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
params: { code: "UNDECLARED_ACTOR" }
});
}
};
const checkSelector = (selector, path46) => {
if (selector === void 0) return;
if (!declaredSelectors.has(selector)) {
ctx.addIssue({
code: "custom",
path: path46,
message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path46)} but not declared in process.selectors. Declare the selector or fix the reference.`,
params: { code: "UNDECLARED_SELECTOR" }
});
}
};
for (const [actorId, actor] of Object.entries(process2.actors)) {
checkSelector(actor.selector, ["actors", actorId, "selector"]);
}
const SAME_KIND_CODE = {
stage: "DUPLICATE_STAGE_ID",
substep: "DUPLICATE_SUBSTEP_ID",
outcome: "DUPLICATE_OUTCOME_ID"
};
const idFirstSeenAt = /* @__PURE__ */ new Map();
const checkId = (id, kind, path46) => {
const first = idFirstSeenAt.get(id);
if (first) {
const kindLabel = kind === first.kind ? kind : "process";
ctx.addIssue({
code: "custom",
path: path46,
message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(path46)} collides with ${first.kind} at ${z4.core.toDotPath(first.path)}.`,
params: {
code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
}
});
} else {
idFirstSeenAt.set(id, { kind, path: path46 });
}
};
process2.stages.forEach((stage, stageIndex) => {
checkId(stage.id, "stage", ["stages", stageIndex, "id"]);
checkActor(stage.actor, ["stages", stageIndex, "actor"]);
stage.substeps?.forEach((substep, substepIndex) => {
checkId(substep.id, "substep", ["stages", stageIndex, "substeps", substepIndex, "id"]);
checkActor(substep.actor, ["stages", stageIndex, "substeps", substepIndex, "actor"]);
checkSelector(substep.selector, [
"stages",
stageIndex,
"substeps",
substepIndex,
"selector"
]);
});
});
process2.outcomes.forEach((outcome, outcomeIndex) => {
checkId(outcome.id, "outcome", ["outcomes", outcomeIndex, "id"]);
});
});
processArtifactSchema = z4.object({
name: z4.string(),
description: z4.string(),
phase: z4.enum(["draft", "workflow_review"]).optional(),
process: processBlockSchema
}).passthrough();
}
});
// libs/workflow-model/src/process-lint.ts
function stagePath(stageIndex) {
return `process.stages[${stageIndex}]`;
}
function substepPath(stageIndex, substepIndex) {
return `${stagePath(stageIndex)}.substeps[${substepIndex}]`;
}
function lintProcess(artifact, inventory, predicateNames, options = {}) {
const diagnostics = [];
const process2 = artifact.process;
const commandNames = new Set(inventory.commands.map((c) => c.name));
const activityNames = new Set(inventory.activities);
const predicateSet = new Set(predicateNames);
const internalCommands = process2.internalCommands ?? [];
const internalSet = new Set(internalCommands);
const boundCommandPaths = /* @__PURE__ */ new Map();
const checkPredicate = (predicate, path46) => {
if (!predicateSet.has(predicate)) {
diagnostics.push({
severity: "error",
code: "UNKNOWN_PREDICATE",
message: `Predicate "${predicate}" referenced at ${path46} is not in the predicate registry.`,
path: path46
});
}
};
if (options.selectorNames) {
const selectorSet = new Set(options.selectorNames);
for (const [name] of Object.entries(process2.selectors ?? {})) {
if (!selectorSet.has(name)) {
diagnostics.push({
severity: "error",
code: "UNKNOWN_SELECTOR",
message: `Selector "${name}" declared at process.selectors.${name} is not in the selector registry; the application has no rule resolving it to subjects.`,
path: `process.selectors.${name}`
});
}
}
}
process2.stages.forEach((stage, stageIndex) => {
const predicatePath = `${stagePath(stageIndex)}.complete.predicate`;
const predicate = stage.complete?.predicate;
if (typeof predicate !== "string" || predicate.length === 0) {
diagnostics.push({
severity: "error",
code: "MISSING_STAGE_PREDICATE",
message: `Stage "${stage.id}" (${stagePath(stageIndex)}) has no complete.predicate.`,
path: predicatePath
});
} else {
checkPredicate(predicate, predicatePath);
}
stage.substeps?.forEach((substep, substepIndex) => {
const bindPath = substepPath(stageIndex, substepIndex);
substep.bind.commands?.forEach((commandName, commandIndex) => {
if (!boundCommandPaths.has(commandName)) {
boundCommandPaths.set(commandName, `${bindPath}.bind.commands[${commandIndex}]`);
}
if (!commandNames.has(commandName)) {
diagnostics.push({
severity: "error",
code: "BOUND_COMMAND_NOT_IN_INVENTORY",
message: `Command "${commandName}" bound by substep "${substep.id}" (${bindPath}.bind.commands[${commandIndex}]) does not exist in the inventory.`,
path: `${bindPath}.bind.commands[${commandIndex}]`
});
}
});
substep.bind.activities?.forEach((activityName, activityIndex) => {
if (!activityNames.has(activityName)) {
diagnostics.push({
severity: "error",
code: "BOUND_ACTIVITY_NOT_IN_INVENTORY",
message: `Activity "${activityName}" bound by substep "${substep.id}" (${bindPath}.bind.activities[${activityIndex}]) does not exist in the inventory.`,
path: `${bindPath}.bind.activities[${activityIndex}]`
});
}
});
});
});
process2.outcomes.forEach((outcome, outcomeIndex) => {
const whenPath = `process.outcomes[${outcomeIndex}].when`;
outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
const path46 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
if (trigger.kind === "predicate") {
checkPredicate(trigger.name, `${path46}.predicate`);
} else if (trigger.kind === "command") {
if (!boundCommandPaths.has(trigger.name)) {
boundCommandPaths.set(trigger.name, `${path46}.command`);
}
if (!commandNames.has(trigger.name)) {
diagnostics.push({
severity: "error",
code: "BOUND_COMMAND_NOT_IN_INVENTORY",
message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path46}.command) does not exist in the inventory.`,
path: `${path46}.command`
});
}
}
});
});
const hasSuccessOutcome = process2.outcomes.some((o) => o.kind === "success");
const hasFailureOutcome = process2.outcomes.some((o) => o.kind === "failure");
if (!hasSuccessOutcome) {
diagnostics.push({
severity: "error",
code: "MISSING_SUCCESS_OUTCOME",
message: 'process.outcomes has no outcome with kind "success"; every process needs at least one.',
path: "process.outcomes"
});
}
if (!hasFailureOutcome) {
diagnostics.push({
severity: "error",
code: "MISSING_FAILURE_OUTCOME",
message: 'process.outcomes has no outcome with kind "failure"; every process needs at least one.',
path: "process.outcomes"
});
}
internalCommands.forEach((commandName, index) => {
if (!commandNames.has(commandName)) {
diagnostics.push({
severity: "warning",
code: "STALE_INTERNAL_COMMAND",
message: `internalCommands entry "${commandName}" (process.internalCommands[${index}]) does not exist in the inventory; this looks like a stale marker.`,
path: `process.internalCommands[${index}]`
});
}
});
inventory.commands.forEach((command) => {
const boundPath = boundCommandPaths.get(command.name);
const isBound = boundPath !== void 0;
const isInternal = internalSet.has(command.name);
if (isBound && isInternal) {
diagnostics.push({
severity: "warning",
code: "CONTRADICTORY_COMMAND_DECLARATION",
message: `Inventory command "${command.name}" is both bound (at ${boundPath}) and listed in internalCommands \u2014 contradictory declaration.`,
path: boundPath
});
} else if (!isBound && !isInternal) {
diagnostics.push({
severity: "error",
code: "UNBOUND_INVENTORY_COMMAND",
message: `Inventory command "${command.name}" is neither bound by any substep, nor named as an outcome trigger, nor listed in process.internalCommands.`,
path: "process.internalCommands"
});
}
});
if (process2.workflowType !== inventory.workflowType) {
diagnostics.push({
severity: "error",
code: "WORKFLOW_TYPE_MISMATCH",
message: `process.workflowType "${process2.workflowType}" does not match inventory.workflowType "${inventory.workflowType}"; this artifact is bound to the wrong workflow.`,
path: "process.workflowType"
});
}
return diagnostics;
}
var init_process_lint = __esm({
"libs/workflow-model/src/process-lint.ts"() {
"use strict";
init_process_artifact();
}
});
// libs/workflow-model/src/index.ts
var src_exports2 = {};
__export(src_exports2, {
CURRENT_PROCESS_VERSION: () => CURRENT_PROCESS_VERSION,
SUPPORTED_PROCESS_VERSIONS: () => SUPPORTED_PROCESS_VERSIONS,
UNVERSIONED_REVISION: () => UNVERSIONED_REVISION,
WorkflowPatchError: () => WorkflowPatchError,
WorkflowPatchStream: () => WorkflowPatchStream,
applyPreviewPatch: () => applyPreviewPatch,
applyWorkflowPatch: () => applyWorkflowPatch,
applyWorkflowPatches: () => applyWorkflowPatches,
buildProcessManifest: () => buildProcessManifest,
computeHappyPath: () => computeHappyPath,
countChanges: () => countChanges,
describeChanges: () => describeChanges,
emptyWorkflowIR: () => emptyWorkflowIR,
findProcessNodesByCommand: () => findProcessNodesByCommand,
findProcessNodesByElementId: () => findProcessNodesByElementId,
lintProcess: () => lintProcess,
outcomeTriggers: () => outcomeTriggers,
parseProcessArtifact: () => parseProcessArtifact,
processArtifactSchema: () => processArtifactSchema,
resolveActorRef: () => resolveActorRef,
resolveSubstepActor: () => resolveSubstepActor,
validateWorkflowIR: () => validateWorkflowIR,
validateWorkflowPatches: () => validateWorkflowPatches,
walkProcessNodes: () => walkProcessNodes
});
var init_src4 = __esm({
"libs/workflow-model/src/index.ts"() {
"use strict";
init_workflow_ir();
init_workflow_ir_validate();
init_apply_preview_patch();
init_change_counts();
init_process_artifact();
init_process_lint();
}
});
// libs/mesh-cli/src/commands/workflow.ts
import * as fs38 from "fs";
import * as path45 from "path";
import { createRequire as createRequire2 } from "module";
import { execFileSync as execFileSync31 } from "child_process";
function resolveExtractorPath() {
try {
const require2 = createRequire2(import.meta.url);
const resolved = require2.resolve("@mesh-tech/workflow-viz/ast-extractor");
return resolved;
} catch {
return null;
}
}
function runExtraction(targetPath, extractorPath, explicitProcessPath) {
let explicitProcessArtifact;
if (explicitProcessPath) {
try {
explicitProcessArtifact = JSON.parse(fs38.readFileSync(explicitProcessPath, "utf-8"));
} catch (err) {
logError(
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
const script = `
import { extractWorkflowIR } from "${extractorPath.replace(/\\/g, "/")}";
import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
import { basename, dirname, join } from "node:path";
${DISCOVERY_HELPERS_SRC}
const target = ${JSON.stringify(targetPath)};
const explicitProcessArtifact = ${JSON.stringify(explicitProcessArtifact ?? null)};
const stat = statSync(target);
const files = stat.isDirectory()
? readdirSync(target).filter(f => f.endsWith(".ts") && f !== "index.ts").map(f => join(target, f))
: [target];
const results = [];
for (const file of files) {
const source = readFileSync(file, "utf-8");
if (!source.includes("proxyActivities") && !source.includes("@temporalio/workflow")) continue;
try {
const aux = discoverAux(file, source);
const acts = discoverActivities(file, source);
const processArtifact = explicitProcessArtifact ?? discoverProcessArtifact(file, source);
const extractOptions = { auxiliarySources: aux, activitySources: acts };
if (processArtifact) extractOptions.processArtifact = processArtifact;
const ir = extractWorkflowIR(source, extractOptions);
const name = ir.name || basename(file, ".ts");
results.push({ workflowType: name, ir, sourceFile: file });
process.stderr.write(" " + name + " (" + ir.nodes.length + " nodes, " + ir.edges.length + " edges)\\n");
} catch (err) {
process.stderr.write(" skip " + file + ": " + err.message + "\\n");
}
}
process.stdout.write(JSON.stringify(results));
`;
try {
const result = execFileSync31("npx", ["tsx", "--eval", script], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "inherit"],
maxBuffer: 10 * 1024 * 1024
});
return JSON.parse(result);
} catch (err) {
logError(
`Extraction subprocess failed: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
function runInventoryExtraction(targetPath, extractorPath) {
const script = `
import { extractInventory } from "${extractorPath.replace(/\\/g, "/")}";
import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
import { basename, dirname, join } from "node:path";
${DISCOVERY_HELPERS_SRC}
const target = ${JSON.stringify(targetPath)};
const stat = statSync(target);
const files = stat.isDirectory()
? readdirSync(target).filter(f => f.endsWith(".ts") && f !== "index.ts").map(f => join(target, f))
: [target];
const results = [];
for (const file of files) {
const source = readFileSync(file, "utf-8");
if (!source.includes("proxyActivities") && !source.includes("@temporalio/workflow")) continue;
try {
const aux = discoverAux(file, source);
const acts = discoverActivities(file, source);
const inventory = extractInventory(source, { auxiliarySources: aux, activitySources: acts });
results.push(inventory);
process.stderr.write(" " + inventory.workflowType + " (" + inventory.commands.length + " commands, " + inventory.queries.length + " queries, " + inventory.activities.length + " activities)\\n");
} catch (err) {
process.stderr.write(" skip " + file + ": " + err.message + "\\n");
}
}
process.stdout.write(JSON.stringify(results));
`;
try {
const result = execFileSync31("npx", ["tsx", "--eval", script], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "inherit"],
maxBuffer: 10 * 1024 * 1024
});
return JSON.parse(result);
} catch (err) {
logError(
`Inventory extraction subprocess failed: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
function runLintExtraction(targetPath, extractorPath, explicitProcessPath) {
let explicitProcessArtifact;
if (explicitProcessPath) {
try {
explicitProcessArtifact = JSON.parse(fs38.readFileSync(explicitProcessPath, "utf-8"));
} catch (err) {
logError(
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
const script = `
import { extractInventory } from "${extractorPath.replace(/\\/g, "/")}";
import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
import { basename, dirname, join } from "node:path";
${DISCOVERY_HELPERS_SRC}
const target = ${JSON.stringify(targetPath)};
const explicitProcessArtifact = ${JSON.stringify(explicitProcessArtifact ?? null)};
const stat = statSync(target);
const files = stat.isDirectory()
? readdirSync(target).filter(f => f.endsWith(".ts") && f !== "index.ts").map(f => join(target, f))
: [target];
const results = [];
for (const file of files) {
const source = readFileSync(file, "utf-8");
if (!source.includes("proxyActivities") && !source.includes("@temporalio/workflow")) continue;
try {
const aux = discoverAux(file, source);
const acts = discoverActivities(file, source);
const inventory = extractInventory(source, { auxiliarySources: aux, activitySources: acts });
const processArtifact = explicitProcessArtifact ?? discoverProcessArtifact(file, source) ?? null;
results.push({ inventory, processArtifact, sourceFile: file });
process.stderr.write(" " + inventory.workflowType + " (" + inventory.commands.length + " commands, " + inventory.queries.length + " queries, " + inventory.activities.length + " activities)\\n");
} catch (err) {
process.stderr.write(" skip " + file + ": " + err.message + "\\n");
}
}
process.stdout.write(JSON.stringify(results));
`;
try {
const result = execFileSync31("npx", ["tsx", "--eval", script], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "inherit"],
maxBuffer: 10 * 1024 * 1024
});
return JSON.parse(result);
} catch (err) {
logError(
`Lint extraction subprocess failed: ${err instanceof Error ? err.message : String(err)}`
);
return null;
}
}
async function uploadToS3(bucket, appName, workflows) {
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
const s3 = new S3Client({ region: process.env.AWS_REGION ?? "us-east-2" });
for (const wf of workflows) {
const key = `workflow-ir/${appName}/${wf.workflowType}.json`;
const body = JSON.stringify(wf.ir, null, 2);
logInfo(` Uploading s3://${bucket}/${key} (${body.length} bytes)`);
await s3.send(
new PutObjectCommand({
Bucket: bucket,
Key: key,
Body: body,
ContentType: "application/json"
})
);
}
const manifest = {
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
app: appName,
workflows: Object.fromEntries(
workflows.map((wf) => [
wf.workflowType,
{
sourceFile: wf.sourceFile,
nodeCount: wf.ir.nodes.length,
edgeCount: wf.ir.edges.length
}
])
)
};
const manifestKey = `workflow-ir/${appName}/_manifest.json`;
logInfo(` Uploading s3://${bucket}/${manifestKey}`);
await s3.send(
new PutObjectCommand({
Bucket: bucket,
Key: manifestKey,
Body: JSON.stringify(manifest, null, 2),
ContentType: "application/json"
})
);
}
function parseNameList(value) {
return value ? value.split(",").map((name) => name.trim()).filter((name) => name.length > 0) : void 0;
}
function registerWorkflowCommands(program2) {
const workflow = program2.command("workflow").description("Workflow tooling \u2014 IR extraction, visualization");
workflow.command("extract-ir <path>").description(
"Extract WorkflowIR from Temporal workflow source files\n\nParses TypeScript workflow code into a graph (nodes + edges) for visualization.\nSupports single files or directories."
).option("--app <name>", "Application name (used as S3 key prefix)").option(
"--upload <bucket>",
"S3 bucket to upload extracted IR (requires --app)"
).option(
"--process <path>",
"Path to a process artifact JSON file (docs/plans/2026-07-05-process-artifact-design.md \xA71); renders substeps/actors/outcomes in place of a BUSINESS_STAGES map. Absent this flag, a sibling process/*.process.json is auto-discovered per workflow file."
).action(
async (targetPath, opts) => {
try {
const resolvedPath = path45.resolve(targetPath);
if (!fs38.existsSync(resolvedPath)) {
logError(`Path does not exist: ${resolvedPath}`);
process.exitCode = 1;
return;
}
let resolvedProcessPath;
if (opts.process) {
resolvedProcessPath = path45.resolve(opts.process);
if (!fs38.existsSync(resolvedProcessPath)) {
logError(`--process path does not exist: ${resolvedProcessPath}`);
process.exitCode = 1;
return;
}
}
const extractorPath = resolveExtractorPath();
if (!extractorPath) {
logError(
"Cannot resolve @mesh-tech/workflow-viz/ast-extractor.\nWorkflow viz is an OPTIONAL peer of the CLI. In the monorepo it is already linked;\nfrom a registry install, add it: npm i -g @mesh-tech/workflow-viz"
);
process.exitCode = 1;
return;
}
logInfo(`Extracting WorkflowIR from ${resolvedPath}`);
const results = runExtraction(resolvedPath, extractorPath, resolvedProcessPath);
if (!results || results.length === 0) {
if (results) logWarn("No workflows found");
process.exitCode = results ? 0 : 1;
return;
}
for (const wf of results) {
logSuccess(
` ${wf.workflowType} (${wf.ir.nodes.length} nodes, ${wf.ir.edges.length} edges)`
);
}
if (opts.upload && opts.app) {
logInfo(
`Uploading ${results.length} workflow IR(s) to s3://${opts.upload}/workflow-ir/${opts.app}/`
);
await uploadToS3(opts.upload, opts.app, results);
logSuccess("Upload complete");
} else if (opts.upload || opts.app) {
logWarn("Both --app and --upload required for S3 upload");
} else {
console.log(JSON.stringify(results.map((r) => r.ir), null, 2));
}
} catch (error) {
logError(
`Extraction failed: ${error instanceof Error ? error.message : String(error)}`
);
process.exitCode = 1;
}
}
);
workflow.command("inventory <path>").description(
"Extract the as-built command/query/activity inventory from Temporal workflow source\n\nThe inventory ({ workflowType, commands, queries, activities }) is the process-\nconformance lint's code-side input \u2014 it is never hand-edited. Supports single\nfiles or directories."
).action(async (targetPath) => {
try {
const resolvedPath = path45.resolve(targetPath);
if (!fs38.existsSync(resolvedPath)) {
logError(`Path does not exist: ${resolvedPath}`);
process.exitCode = 1;
return;
}
const extractorPath = resolveExtractorPath();
if (!extractorPath) {
logError(
"Cannot resolve @mesh-tech/workflow-viz/ast-extractor.\nWorkflow viz is an OPTIONAL peer of the CLI. In the monorepo it is already linked;\nfrom a registry install, add it: npm i -g @mesh-tech/workflow-viz"
);
process.exitCode = 1;
return;
}
logInfo(`Extracting inventory from ${resolvedPath}`);
const results = runInventoryExtraction(resolvedPath, extractorPath);
if (!results || results.length === 0) {
if (results) logWarn("No workflows found");
process.exitCode = results ? 0 : 1;
return;
}
for (const inv of results) {
logSuccess(
` ${inv.workflowType} (${inv.commands.length} commands, ${inv.queries.length} queries, ${inv.activities.length} activities)`
);
}
console.log(JSON.stringify(results.length === 1 ? results[0] : results, null, 2));
} catch (error) {
logError(
`Inventory extraction failed: ${error instanceof Error ? error.message : String(error)}`
);
process.exitCode = 1;
}
});
workflow.command("lint-process <path>").description(
"Lint a process artifact's bindings against the as-built code inventory\n\nReuses the inventory extraction's aux/activity discovery, resolves the process\nartifact (--process, or the same sibling process/*.process.json discovery\nextract-ir uses), then runs parseProcessArtifact + lintProcess against it.\n\nWithout --predicates / --selectors, the CLI cannot verify those names against the\nworker's registries (it has no way to execute them) \u2014 the corresponding findings\nare skipped and a note is printed; every other rule (bindings, coverage, ids,\nactors, outcomes, workflowType) still runs. Pass both for full conformance, or rely\non the worker's own process-conformance test which has the registries in-process."
).option(
"--process <path>",
"Path to a process artifact JSON file. Absent this flag, a sibling process/*.process.json is auto-discovered per workflow file."
).option(
"--predicates <names>",
"Comma-separated named-predicate registry (enables UNKNOWN_PREDICATE checks)"
).option(
"--selectors <names>",
"Comma-separated named-selector registry (enables UNKNOWN_SELECTOR checks)"
).action(async (targetPath, opts) => {
try {
const resolvedPath = path45.resolve(targetPath);
if (!fs38.existsSync(resolvedPath)) {
logError(`Path does not exist: ${resolvedPath}`);
process.exitCode = 1;
return;
}
let resolvedProcessPath;
if (opts.process) {
resolvedProcessPath = path45.resolve(opts.process);
if (!fs38.existsSync(resolvedProcessPath)) {
logError(`--process path does not exist: ${resolvedProcessPath}`);
process.exitCode = 1;
return;
}
}
const extractorPath = resolveExtractorPath();
if (!extractorPath) {
logError(
"Cannot resolve @mesh-tech/workflow-viz/ast-extractor.\nWorkflow viz is an OPTIONAL peer of the CLI. In the monorepo it is already linked;\nfrom a registry install, add it: npm i -g @mesh-tech/workflow-viz"
);
process.exitCode = 1;
return;
}
let parseProcessArtifact2;
let lintProcess2;
try {
({ parseProcessArtifact: parseProcessArtifact2 } = await Promise.resolve().then(() => (init_src4(), src_exports2)));
({ lintProcess: lintProcess2 } = await Promise.resolve().then(() => (init_src4(), src_exports2)));
} catch {
logError(
"Cannot resolve @mesh-tech/workflow-model (parseProcessArtifact / lintProcess).\nIt is bundled into the published CLI \u2014 on a registry install this means a broken install; reinstall the CLI. In the monorepo, run the scoped install (pnpm bootstrap:worktree @mesh-tech/mesh-cli)."
);
process.exitCode = 1;
return;
}
const predicateNames = parseNameList(opts.predicates);
const selectorNames = parseNameList(opts.selectors);
if (!predicateNames) {
logWarn(
"predicate checks skipped \u2014 pass --predicates or run the worker conformance test"
);
}
if (!selectorNames) {
logWarn("selector checks skipped \u2014 pass --selectors or run the worker conformance test");
}
logInfo(`Linting process artifact(s) against ${resolvedPath}`);
const results = runLintExtraction(resolvedPath, extractorPath, resolvedProcessPath);
if (!results || results.length === 0) {
if (results) logWarn("No workflows found");
process.exitCode = results ? 0 : 1;
return;
}
let hasError = false;
for (const { inventory, processArtifact, sourceFile } of results) {
console.log(`
${inventory.workflowType} (${sourceFile})`);
if (!processArtifact) {
logError(
" No process artifact found (checked --process and sibling process/*.process.json)."
);
hasError = true;
continue;
}
const { artifact, diagnostics: parseDiagnostics } = parseProcessArtifact2(processArtifact);
let diagnostics = parseDiagnostics;
if (artifact) {
const lintDiagnostics = lintProcess2(
artifact,
inventory,
predicateNames ?? [],
selectorNames ? { selectorNames } : {}
);
const reportable = predicateNames ? lintDiagnostics : lintDiagnostics.filter((d) => d.code !== "UNKNOWN_PREDICATE");
diagnostics = diagnostics.concat(reportable);
}
if (diagnostics.length === 0) {
logSuccess(" clean");
} else {
for (const d of diagnostics) {
const line = ` [${d.severity}] ${d.code} ${d.path}: ${d.message}`;
if (d.severity === "error") {
logError(line);
hasError = true;
} else {
logWarn(line);
}
}
}
}
process.exitCode = hasError ? 1 : 0;
} catch (error) {
logError(`Lint failed: ${error instanceof Error ? error.message : String(error)}`);
process.exitCode = 1;
}
});
}
var DISCOVERY_HELPERS_SRC;
var init_workflow = __esm({
"libs/mesh-cli/src/commands/workflow.ts"() {
"use strict";
init_log();
DISCOVERY_HELPERS_SRC = `
function discoverAux(filePath, source) {
const dir = dirname(filePath);
const aux = {};
const re = /import\\s+(?:type\\s+)?(?:(?:\\{[^}]*\\}|\\*\\s+as\\s+\\w+)(?:\\s*,\\s*)?)+\\s+from\\s+["'](\\.[^"']+)["']/g;
let m;
while ((m = re.exec(source)) !== null) {
const spec = m[1];
if (!spec.startsWith("./") && !spec.startsWith("../")) continue;
for (const c of [join(dir, spec.replace(/\\.js$/, ".ts")), join(dir, spec + ".ts")]) {
if (existsSync(c) && statSync(c).isFile()) {
const s = readFileSync(c, "utf-8");
if (s.includes("proxyActivities") || s.includes("@temporalio/workflow") || s.includes("wf.")) {
aux[spec] = s;
}
break;
}
}
}
return aux;
}
function discoverActivities(filePath, source) {
const dir = dirname(filePath);
const acts = {};
const re = /import\\s+type\\s+\\{[^}]*createActivities[^}]*\\}\\s+from\\s+["'](\\.[^"']+)["']/g;
let m;
while ((m = re.exec(source)) !== null) {
const spec = m[1];
for (const c of [join(dir, spec.replace(/\\.js$/, ".ts")), join(dir, spec + ".ts")]) {
if (existsSync(c) && statSync(c).isFile()) {
acts[spec] = readFileSync(c, "utf-8");
const actDir = dirname(c);
try {
for (const sib of readdirSync(actDir)) {
if (!sib.endsWith(".ts") || sib === basename(c)) continue;
const sp = join(actDir, sib);
if (statSync(sp).isFile()) {
acts[spec.replace(/\\/[^/]+$/, "/" + sib.replace(".ts", ".js"))] = readFileSync(sp, "utf-8");
}
}
} catch {}
break;
}
}
}
return acts;
}
/**
* Discover a sibling process artifact for a workflow file: a "process/"
* directory containing "*.process.json" files, checked both next to the
* workflow file itself AND next to its parent directory (worker packages
* typically nest the workflow file under "src/", with "process/" a sibling
* of "src/" at the package root, not of the file directly \u2014 e.g.
* "worker/src/workflows.ts" + "worker/process/*.process.json"). Since
* extractWorkflowIR itself validates the artifact (schema errors and a
* workflowType mismatch both fall back with a warning, never throw \u2014 P6),
* this only needs a best-effort guess at the workflow's name to avoid
* bothering with an OBVIOUSLY unrelated file in a multi-workflow directory \u2014
* a quick regex for the first exported async function, same derivation
* extractWorkflowIR falls back to absent an explicit \`name\` override.
* Returns the parsed JSON (unknown to this script) or undefined.
*/
function discoverProcessArtifact(filePath, source) {
const fileDir = dirname(filePath);
const candidateDirs = [join(fileDir, "process"), join(fileDir, "..", "process")];
const nameMatch = source.match(/export\\s+async\\s+function\\s+(\\w+)/);
const guessedName = nameMatch ? nameMatch[1] : undefined;
for (const procDir of candidateDirs) {
if (!existsSync(procDir) || !statSync(procDir).isDirectory()) continue;
for (const entry of readdirSync(procDir)) {
if (!entry.endsWith(".process.json")) continue;
const entryPath = join(procDir, entry);
if (!statSync(entryPath).isFile()) continue;
try {
const parsed = JSON.parse(readFileSync(entryPath, "utf-8"));
if (!guessedName || parsed?.process?.workflowType === guessedName) {
return parsed;
}
} catch {
// Not valid JSON \u2014 skip; extractWorkflowIR would reject it anyway.
}
}
}
return undefined;
}
`;
}
});
// libs/mesh-cli/src/program.ts
var program_exports = {};
__export(program_exports, {
CLI_DESCRIPTION: () => CLI_DESCRIPTION,
CLI_NAME: () => CLI_NAME,
createProgram: () => createProgram
});
import { Command as Command3 } from "commander";
function createProgram(options = {}) {
const program2 = new Command3();
program2.name(CLI_NAME).description(CLI_DESCRIPTION).enablePositionalOptions();
if (options.version !== void 0) program2.version(options.version);
registerLoginCommand(program2);
registerRegistryCommands(program2);
registerDbCommands(program2);
registerDeployCommand(program2);
registerDevCommand(program2);
registerCreateAppCommand(program2);
registerAppCommands(program2);
registerDocsCommand(program2);
registerStackCommand(program2);
registerTunnelCommands(program2);
registerSecretsCommands(program2);
registerArtifactsCommands(program2);
registerConversationsCommands(program2);
registerVpnCommands(program2);
registerWorkflowCommands(program2);
registerSiteCommands(program2);
registerTemporalCommands(program2);
registerClusterCommands(program2);
registerVcsCommands(program2);
registerLocalCommands(program2);
registerHubCommands(program2);
registerSkillsCommands(program2);
registerInitCommands(program2);
registerTenantCommands(program2);
registerInstallShimCommand(program2);
return program2;
}
var CLI_NAME, CLI_DESCRIPTION;
var init_program = __esm({
"libs/mesh-cli/src/program.ts"() {
"use strict";
init_app_check();
init_artifacts();
init_cluster();
init_conversations();
init_create_app();
init_db();
init_deploy();
init_dev();
init_docs();
init_hub();
init_init();
init_install_shim();
init_local();
init_login();
init_registry();
init_secrets();
init_site();
init_skills();
init_stack2();
init_temporal();
init_tenant();
init_tunnel2();
init_vcs();
init_vpn2();
init_workflow();
CLI_NAME = "mesh";
CLI_DESCRIPTION = "CLI for Mesh Lab development utilities";
}
});
// libs/mesh-cli/bin/mesh.ts
init_program();
init_errors();
init_build_info();
var wantsVersion = process.argv.includes("--version") || process.argv.includes("-V");
var program = createProgram({
version: wantsVersion ? formatVersionLine(resolveCliRuntime()) : resolveCliRuntime({ vcs: false }).version
});
program.parseAsync().catch(handleCliError);