@mesh-tech/mesh-cli 0.34.4 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/mesh.js CHANGED
@@ -1712,8 +1712,10 @@ var init_stack = __esm({
1712
1712
  var skills_exports = {};
1713
1713
  __export(skills_exports, {
1714
1714
  INTENT_RANGE: () => INTENT_RANGE,
1715
+ installedSkillName: () => installedSkillName,
1715
1716
  listPackageSkills: () => listPackageSkills,
1716
1717
  registerSkillsCommands: () => registerSkillsCommands,
1718
+ renderManagedSkill: () => renderManagedSkill,
1717
1719
  resolveTargetRoot: () => resolveTargetRoot,
1718
1720
  seedAppSkill: () => seedAppSkill,
1719
1721
  syncSkills: () => syncSkills
@@ -1738,15 +1740,36 @@ function cliAsset(...segments) {
1738
1740
  function listBaseSkills() {
1739
1741
  const dir = cliAsset("skills");
1740
1742
  if (!fs6.existsSync(dir)) return [];
1741
- return fs6.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs6.existsSync(path6.join(dir, entry.name, "SKILL.md"))).map((entry) => ({ name: entry.name, source: path6.join(dir, entry.name, "SKILL.md") }));
1743
+ return fs6.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs6.existsSync(path6.join(dir, entry.name, "SKILL.md"))).map((entry) => ({
1744
+ name: installedSkillName([entry.name]),
1745
+ // The CLI's own skill shipped as `skills/core/` and installed as `mesh-core/`.
1746
+ legacyName: entry.name === "mesh" ? "mesh-core" : `mesh-${entry.name}`,
1747
+ source: path6.join(dir, entry.name, "SKILL.md")
1748
+ }));
1749
+ }
1750
+ function installedSkillName(segments) {
1751
+ const parts = ["mesh"];
1752
+ for (const segment of segments) {
1753
+ if (segment === "core") continue;
1754
+ if (segment === parts[parts.length - 1]) continue;
1755
+ parts.push(segment);
1756
+ }
1757
+ return parts.join("-");
1742
1758
  }
1743
- function renderManagedSkill(source) {
1759
+ function renderManagedSkill(source, name) {
1744
1760
  const raw = fs6.readFileSync(source, "utf-8");
1745
1761
  const frontmatter = raw.match(/^(---\n[\s\S]*?\n---\n)/)?.[1];
1746
- if (!frontmatter) return `${MANAGED_MARKER}
1762
+ if (!frontmatter) return `---
1763
+ name: ${name}
1764
+ ---
1765
+
1766
+ ${MANAGED_MARKER}
1747
1767
 
1748
1768
  ${raw}`;
1749
- return `${frontmatter}
1769
+ const named = /^name:.*$/m.test(frontmatter) ? frontmatter.replace(/^name:.*$/m, `name: ${name}`) : frontmatter.replace(/^---\n/, `---
1770
+ name: ${name}
1771
+ `);
1772
+ return `${named}
1750
1773
  ${MANAGED_MARKER}
1751
1774
  ${raw.slice(frontmatter.length)}`;
1752
1775
  }
@@ -1785,13 +1808,31 @@ function listPackageSkills(root) {
1785
1808
  for (const domain of fs6.readdirSync(skillsDir, { withFileTypes: true })) {
1786
1809
  const source = path6.join(skillsDir, domain.name, "SKILL.md");
1787
1810
  if (!fs6.existsSync(source)) continue;
1788
- const name = `mesh-${pkg.name}-${domain.name}`;
1789
- if (!byName.has(name)) byName.set(name, { name, dir: path6.join(skillsDir, domain.name), source });
1811
+ const name = installedSkillName([pkg.name, domain.name]);
1812
+ if (!byName.has(name)) {
1813
+ byName.set(name, {
1814
+ name,
1815
+ legacyName: `mesh-${pkg.name}-${domain.name}`,
1816
+ dir: path6.join(skillsDir, domain.name),
1817
+ source
1818
+ });
1819
+ }
1790
1820
  }
1791
1821
  }
1792
1822
  }
1793
1823
  return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
1794
1824
  }
1825
+ function extrasInSync(sourceDir, targetDir) {
1826
+ for (const entry of fs6.readdirSync(sourceDir, { withFileTypes: true, recursive: true })) {
1827
+ if (!entry.isFile()) continue;
1828
+ const rel2 = path6.relative(sourceDir, path6.join(entry.parentPath, entry.name));
1829
+ if (rel2 === "SKILL.md") continue;
1830
+ const installed = path6.join(targetDir, rel2);
1831
+ if (!fs6.existsSync(installed)) return false;
1832
+ if (!fs6.readFileSync(installed).equals(fs6.readFileSync(path6.join(sourceDir, rel2)))) return false;
1833
+ }
1834
+ return true;
1835
+ }
1795
1836
  function copySkillExtras(sourceDir, targetDir) {
1796
1837
  for (const entry of fs6.readdirSync(sourceDir, { withFileTypes: true })) {
1797
1838
  if (entry.name === "SKILL.md") continue;
@@ -1809,25 +1850,36 @@ function seedAppSkill(root, appName, appRelPath) {
1809
1850
  }
1810
1851
  function planSync(root) {
1811
1852
  const items = [];
1812
- for (const skill of listBaseSkills()) {
1813
- const target = path6.join(root, ".claude", "skills", `mesh-${skill.name}`, "SKILL.md");
1814
- const desired = renderManagedSkill(skill.source);
1815
- const label = `.claude/skills/mesh-${skill.name}/SKILL.md (base skill)`;
1853
+ const baseSkills = listBaseSkills();
1854
+ for (const skill of baseSkills) {
1855
+ const targetDir = path6.join(root, ".claude", "skills", skill.name);
1856
+ const target = path6.join(targetDir, "SKILL.md");
1857
+ const sourceDir = path6.dirname(skill.source);
1858
+ const desired = renderManagedSkill(skill.source, skill.name);
1859
+ const label = `.claude/skills/${skill.name}/SKILL.md (base skill)`;
1816
1860
  if (!fs6.existsSync(target)) {
1817
1861
  items.push({
1818
1862
  label,
1819
1863
  state: "write",
1820
1864
  apply: () => {
1821
- fs6.mkdirSync(path6.dirname(target), { recursive: true });
1865
+ fs6.mkdirSync(targetDir, { recursive: true });
1822
1866
  fs6.writeFileSync(target, desired);
1867
+ copySkillExtras(sourceDir, targetDir);
1823
1868
  }
1824
1869
  });
1825
1870
  } else {
1826
1871
  const current = fs6.readFileSync(target, "utf-8");
1827
- if (current === desired) {
1872
+ if (current === desired && extrasInSync(sourceDir, targetDir)) {
1828
1873
  items.push({ label, state: "ok" });
1829
1874
  } else if (current.includes(MANAGED_MARKER)) {
1830
- items.push({ label, state: "write", apply: () => fs6.writeFileSync(target, desired) });
1875
+ items.push({
1876
+ label,
1877
+ state: "write",
1878
+ apply: () => {
1879
+ fs6.writeFileSync(target, desired);
1880
+ copySkillExtras(sourceDir, targetDir);
1881
+ }
1882
+ });
1831
1883
  } else {
1832
1884
  items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
1833
1885
  }
@@ -1837,7 +1889,7 @@ function planSync(root) {
1837
1889
  for (const skill of packageSkills) {
1838
1890
  const targetDir = path6.join(root, ".claude", "skills", skill.name);
1839
1891
  const target = path6.join(targetDir, "SKILL.md");
1840
- const desired = renderManagedSkill(skill.source);
1892
+ const desired = renderManagedSkill(skill.source, skill.name);
1841
1893
  const label = `.claude/skills/${skill.name}/SKILL.md (platform skill)`;
1842
1894
  if (!fs6.existsSync(target)) {
1843
1895
  items.push({
@@ -1870,6 +1922,22 @@ function planSync(root) {
1870
1922
  if (packageSkills.length === 0 && meshTechScopeDirs(root).length === 0) {
1871
1923
  logInfo("No @mesh-tech packages installed yet \u2014 run `pnpm install`, then `mesh skills sync` again for the platform skills.");
1872
1924
  }
1925
+ for (const skill of [...baseSkills, ...packageSkills]) {
1926
+ if (skill.legacyName === skill.name) continue;
1927
+ const legacyDir = path6.join(root, ".claude", "skills", skill.legacyName);
1928
+ const legacyFile = path6.join(legacyDir, "SKILL.md");
1929
+ if (!fs6.existsSync(legacyFile)) continue;
1930
+ const label = `.claude/skills/${skill.legacyName}/ (renamed to ${skill.name})`;
1931
+ if (fs6.readFileSync(legacyFile, "utf-8").includes(MANAGED_MARKER)) {
1932
+ items.push({
1933
+ label: `${label} \u2014 removing the old copy`,
1934
+ state: "write",
1935
+ apply: () => fs6.rmSync(legacyDir, { recursive: true, force: true })
1936
+ });
1937
+ } else {
1938
+ items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
1939
+ }
1940
+ }
1873
1941
  const hookSource = cliAsset("assets", "intent", "intent-claude-gate.mjs");
1874
1942
  const hookTarget = path6.join(root, HOOK_RELATIVE);
1875
1943
  const hookDesired = fs6.readFileSync(hookSource, "utf-8");
@@ -2260,12 +2328,19 @@ var init_app_check_resolve = __esm({
2260
2328
  });
2261
2329
 
2262
2330
  // packages/mesh-cli/src/utils/first-party-contexts.ts
2331
+ function loginContextForPlatform(platform) {
2332
+ if (!Object.hasOwn(FIRST_PARTY_PLATFORM_CONTEXTS, platform)) return platform;
2333
+ return FIRST_PARTY_PLATFORM_CONTEXTS[platform] || platform;
2334
+ }
2263
2335
  function firstPartyDomainFor(context) {
2264
2336
  if (!Object.hasOwn(FIRST_PARTY_CONTEXTS, context)) return null;
2265
2337
  const domain = FIRST_PARTY_CONTEXTS[context];
2266
2338
  return typeof domain === "string" && domain ? domain : null;
2267
2339
  }
2268
- var FIRST_PARTY_CONTEXTS;
2340
+ function isPlatformLoginAlias(context) {
2341
+ return Object.values(FIRST_PARTY_PLATFORM_CONTEXTS).includes(context);
2342
+ }
2343
+ var FIRST_PARTY_CONTEXTS, FIRST_PARTY_PLATFORM_CONTEXTS;
2269
2344
  var init_first_party_contexts = __esm({
2270
2345
  "packages/mesh-cli/src/utils/first-party-contexts.ts"() {
2271
2346
  "use strict";
@@ -2276,7 +2351,15 @@ var init_first_party_contexts = __esm({
2276
2351
  // platform lives in a different AWS account with no registry; the
2277
2352
  // platform-dev stack in the registry-owning account is the developer front
2278
2353
  // door. mesh-sandbox#32 / #3587.)
2279
- "mesh.dev": "dev.platform.meshtech.io"
2354
+ "mesh.dev": "dev.platform.meshtech.io",
2355
+ // Verified 2026-09-25: https://cli.dev.mesh-platform.trabian.com/.well-known/mesh.json
2356
+ // answers anonymously with the `mesh` tenant's dev platform issuer
2357
+ // (identity.dev.mesh-platform.trabian.com) — the platform every `mesh`/dev
2358
+ // tenant environment and its `*-apps-deployer` role live on.
2359
+ "mesh-platform.dev": "dev.mesh-platform.trabian.com"
2360
+ });
2361
+ FIRST_PARTY_PLATFORM_CONTEXTS = Object.freeze({
2362
+ "mesh.dev": "mesh-platform.dev"
2280
2363
  });
2281
2364
  }
2282
2365
  });
@@ -4091,11 +4174,11 @@ function readAllCredentials() {
4091
4174
  function readCredentials(context) {
4092
4175
  return readAllCredentials()[context] ?? null;
4093
4176
  }
4094
- function atomicWriteFileSync(path49, data, mode) {
4095
- const tmpPath = `${path49}.${process.pid}.${atomicWriteCounter++}.tmp`;
4177
+ function atomicWriteFileSync(path50, data, mode) {
4178
+ const tmpPath = `${path50}.${process.pid}.${atomicWriteCounter++}.tmp`;
4096
4179
  try {
4097
4180
  fs11.writeFileSync(tmpPath, data, { mode });
4098
- fs11.renameSync(tmpPath, path49);
4181
+ fs11.renameSync(tmpPath, path50);
4099
4182
  } catch (err) {
4100
4183
  try {
4101
4184
  fs11.unlinkSync(tmpPath);
@@ -4830,7 +4913,7 @@ async function ensureLogin(context, opts = {}) {
4830
4913
  }
4831
4914
  async function discoverConfigGuarded(context, opts = {}) {
4832
4915
  let config = null;
4833
- if (parseTenantEnv(context)) {
4916
+ if (parseTenantEnv(context) && !isPlatformLoginAlias(context)) {
4834
4917
  config = await discoverConfigFromSsm(context, opts);
4835
4918
  }
4836
4919
  if (!config) {
@@ -5501,7 +5584,7 @@ import {
5501
5584
  GetCallerIdentityCommand,
5502
5585
  STSClient
5503
5586
  } from "@aws-sdk/client-sts";
5504
- function derivePlatformContext(appRoot, stack) {
5587
+ function readStackPlatform(appRoot, stack) {
5505
5588
  const configFile = path13.join(appRoot, `Pulumi.${stack}.yaml`);
5506
5589
  if (!fs13.existsSync(configFile)) return null;
5507
5590
  const content = fs13.readFileSync(configFile, "utf-8");
@@ -5520,6 +5603,12 @@ function derivePlatformContext(appRoot, stack) {
5520
5603
  }
5521
5604
  return null;
5522
5605
  }
5606
+ function derivePlatformContext(appRoot, stack) {
5607
+ const explicit = readStackConfig(appRoot, stack, "mesh:loginContext");
5608
+ if (explicit) return explicit;
5609
+ const platform = readStackPlatform(appRoot, stack);
5610
+ return platform ? loginContextForPlatform(platform) : null;
5611
+ }
5523
5612
  function quoteCredProcessToken(token) {
5524
5613
  return /\s/.test(token) ? `"${token}"` : token;
5525
5614
  }
@@ -5821,6 +5910,8 @@ var init_aws_auth = __esm({
5821
5910
  init_log();
5822
5911
  init_auth_preflight();
5823
5912
  init_login();
5913
+ init_first_party_contexts();
5914
+ init_pulumi();
5824
5915
  DEFAULT_REGION = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
5825
5916
  MANAGED_START_PREFIX = `# >>> mesh-cli managed profile `;
5826
5917
  MANAGED_START = (context) => `${MANAGED_START_PREFIX}${context} >>>`;
@@ -5871,7 +5962,9 @@ var init_vpn_join = __esm({
5871
5962
  });
5872
5963
 
5873
5964
  // packages/mesh-cli/src/utils/pulumi-run.ts
5874
- function credProbeToPreflightError(probe, context) {
5965
+ function credProbeToPreflightError(probe, context, platform) {
5966
+ const aliasNote = platform && platform !== context && loginContextForPlatform(platform) === context ? `
5967
+ This stack deploys to platform ${platform}, which signs in as ${context} \u2014 a \`mesh login ${platform}\` session is a different platform and is not trusted here.` : "";
5875
5968
  switch (probe.state) {
5876
5969
  case "ok":
5877
5970
  return null;
@@ -5879,19 +5972,21 @@ function credProbeToPreflightError(probe, context) {
5879
5972
  return {
5880
5973
  fatal: true,
5881
5974
  message: `Not logged in for ${context}. Run:
5882
- mesh login ${context} --device`
5975
+ mesh login ${context} --device` + aliasNote
5883
5976
  };
5884
5977
  case "expired-session":
5885
5978
  return {
5886
5979
  fatal: true,
5887
5980
  message: `Your mesh login session for ${context} expired. Run:
5888
- mesh login ${context} --device`
5981
+ mesh login ${context} --device` + aliasNote
5889
5982
  };
5890
5983
  case "assume-denied":
5891
5984
  return {
5892
5985
  fatal: true,
5893
5986
  message: `Logged in, but couldn't assume the deployer role \u2014 ${probe.detail}
5894
- Check mesh:deployerRole in the stack config and your IAM access.`
5987
+ If you signed in to another platform, sign in to this stack's:
5988
+ mesh login ${context} --device
5989
+ Otherwise check mesh:deployerRole in the stack config and your IAM access.` + aliasNote
5895
5990
  };
5896
5991
  case "stale-env-override":
5897
5992
  return {
@@ -5921,7 +6016,7 @@ async function resolvePulumiEnv(opts) {
5921
6016
  const context = derivePlatformContext(appRoot, stack);
5922
6017
  if (!hasAmbientCreds && context) {
5923
6018
  const probe = await probeCredentials(context, roleArn);
5924
- const preflight = credProbeToPreflightError(probe, context);
6019
+ const preflight = credProbeToPreflightError(probe, context, readStackPlatform(appRoot, stack));
5925
6020
  if (preflight?.fatal) {
5926
6021
  logError(preflight.message);
5927
6022
  process.exit(1);
@@ -5945,6 +6040,7 @@ var init_pulumi_run = __esm({
5945
6040
  init_log();
5946
6041
  init_pulumi();
5947
6042
  init_aws_auth();
6043
+ init_first_party_contexts();
5948
6044
  init_login();
5949
6045
  }
5950
6046
  });
@@ -10780,7 +10876,7 @@ function registerDevCommand(program2) {
10780
10876
  }
10781
10877
  const resolved = deployerRole ? await resolveAwsCredentials(deployerRole, appRoot, stack) : null;
10782
10878
  if (deployerRole && !resolved && !process.env.AWS_ACCESS_KEY_ID) {
10783
- const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx) : null;
10879
+ const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx, readStackPlatform(appRoot, stack)) : null;
10784
10880
  logError(
10785
10881
  pf?.message ?? `Couldn't resolve AWS credentials to restart '${service}'. Run: mesh login ${ctx ?? "mesh.dev"} --device`
10786
10882
  );
@@ -12040,7 +12136,7 @@ function scanApp(root, appRel, resolution = probeResolution(root, appRel)) {
12040
12136
  const files = [...new Set(surface.filter((h) => re.test(h.text)).map((h) => h.file))].sort().slice(0, 3);
12041
12137
  for (const f of files) hit(code, rel(root, f), why);
12042
12138
  };
12043
- 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");
12139
+ anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC \u2014 grants belong in the Hub (Access \u2192 Users), not in a config redeploy");
12044
12140
  anti(
12045
12141
  "POINTER_PROJECT_OVERRIDE",
12046
12142
  RE_POINTER_OVERRIDE,
@@ -12244,7 +12340,7 @@ var init_app_check = __esm({
12244
12340
  gate: "H",
12245
12341
  title: "No role grants in IaC",
12246
12342
  severity: "block",
12247
- remediation: "remove the per-user grant map; grant roles from the Hub (App \u2192 Access \u2192 People)",
12343
+ remediation: "remove the per-user grant map; grant roles from the Hub (App \u2192 Access \u2192 Users)",
12248
12344
  scope: "app"
12249
12345
  },
12250
12346
  POINTER_PROJECT_OVERRIDE: {
@@ -12873,11 +12969,11 @@ function createVcsFolderReader(opts) {
12873
12969
  }
12874
12970
  return p;
12875
12971
  };
12876
- const readPath = async (repo, path49) => {
12972
+ const readPath = async (repo, path50) => {
12877
12973
  const dir = await cloneRepo(repo);
12878
- const ref = await resolveFolderRef(dir, path49);
12879
- if (!ref) throw new Error(`path "${path49}" not found on any ref of ${repo}`);
12880
- const files = await readFolderAtRef(dir, ref, path49);
12974
+ const ref = await resolveFolderRef(dir, path50);
12975
+ if (!ref) throw new Error(`path "${path50}" not found on any ref of ${repo}`);
12976
+ const files = await readFolderAtRef(dir, ref, path50);
12881
12977
  return { ref, files };
12882
12978
  };
12883
12979
  const cleanup = async () => {
@@ -13197,7 +13293,7 @@ Use --stack <name> (the deployed stack, e.g. dev).` : "No stack configs found in
13197
13293
  const ctx = derivePlatformContext(appRoot, stack);
13198
13294
  const resolved = deployerRole ? await resolveAwsCredentials(deployerRole, appRoot, stack) : null;
13199
13295
  if (deployerRole && !resolved && !process.env.AWS_ACCESS_KEY_ID) {
13200
- const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx) : null;
13296
+ const pf = ctx ? credProbeToPreflightError(await probeCredentials(ctx, deployerRole), ctx, readStackPlatform(appRoot, stack)) : null;
13201
13297
  throw new Error(
13202
13298
  pf?.message ?? `Couldn't resolve AWS credentials. Run: mesh login ${ctx ?? "mesh.dev"} --device`
13203
13299
  );
@@ -15252,7 +15348,7 @@ var init_discover = __esm({
15252
15348
 
15253
15349
  // packages/mesh-cli/src/docs/assemble.ts
15254
15350
  import { execFileSync as execFileSync20 } from "node:child_process";
15255
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync25, rmSync as rmSync5, writeFileSync as writeFileSync15 } from "node:fs";
15351
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync25, rmSync as rmSync6, writeFileSync as writeFileSync15 } from "node:fs";
15256
15352
  import path30 from "node:path";
15257
15353
  import { parse as parseYaml5 } from "yaml";
15258
15354
  function splitFrontMatter(markdown) {
@@ -15534,7 +15630,7 @@ function assemblePortal(options) {
15534
15630
  const routes = new Set(pages.map((p) => p.route));
15535
15631
  const brokenLinks = [];
15536
15632
  const externalLinks = [];
15537
- rmSync5(outDir, { recursive: true, force: true });
15633
+ rmSync6(outDir, { recursive: true, force: true });
15538
15634
  mkdirSync12(outDir, { recursive: true });
15539
15635
  for (const page of pages) {
15540
15636
  const raw = readFileSync25(path30.join(repoRoot2, page.source), "utf-8");
@@ -15872,8 +15968,8 @@ function formatDefault(value, paths = currentMachinePaths()) {
15872
15968
  if (typeof value === "boolean" || typeof value === "number") return String(value);
15873
15969
  return normalizeMachinePaths(JSON.stringify(value), paths);
15874
15970
  }
15875
- function slugifyCommandPath(path49) {
15876
- return path49.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
15971
+ function slugifyCommandPath(path50) {
15972
+ return path50.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
15877
15973
  }
15878
15974
  function extractOptions(command) {
15879
15975
  const options = command.options ?? [];
@@ -15907,17 +16003,17 @@ function isUndocumented(command) {
15907
16003
  function extractCommand(command, parentPath, depth) {
15908
16004
  const internals = command;
15909
16005
  const name = internals._name ?? "";
15910
- const path49 = parentPath ? `${parentPath} ${name}` : name;
16006
+ const path50 = parentPath ? `${parentPath} ${name}` : name;
15911
16007
  return {
15912
- path: path49,
16008
+ path: path50,
15913
16009
  name,
15914
16010
  aliases: [...internals._aliases ?? []],
15915
16011
  description: internals._description ?? "",
15916
16012
  args: extractArgs(command),
15917
16013
  options: extractOptions(command),
15918
- slug: slugifyCommandPath(path49),
16014
+ slug: slugifyCommandPath(path50),
15919
16015
  depth,
15920
- subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, path49, depth + 1))
16016
+ subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, path50, depth + 1))
15921
16017
  };
15922
16018
  }
15923
16019
  function extractCliReference(program2) {
@@ -16405,7 +16501,7 @@ var init_serve = __esm({
16405
16501
 
16406
16502
  // packages/mesh-cli/src/docs/registry-docs.ts
16407
16503
  import { execFileSync as execFileSync22 } from "node:child_process";
16408
- import { existsSync as existsSync25, mkdirSync as mkdirSync14, readFileSync as readFileSync28, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
16504
+ import { existsSync as existsSync25, mkdirSync as mkdirSync14, readFileSync as readFileSync28, renameSync as renameSync3, rmSync as rmSync7, writeFileSync as writeFileSync17 } from "node:fs";
16409
16505
  import { tmpdir as tmpdir7 } from "node:os";
16410
16506
  import path33 from "node:path";
16411
16507
  function readDocsRegistryAuth(npmrcPath2 = homeNpmrcPath()) {
@@ -16493,20 +16589,20 @@ async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fet
16493
16589
  const tgzPath = path33.join(cacheRoot, `.${version}.tgz`);
16494
16590
  writeFileSync17(tgzPath, Buffer.from(await tgzResponse.arrayBuffer()));
16495
16591
  const staging = path33.join(cacheRoot, `.staging-${version}`);
16496
- rmSync6(staging, { recursive: true, force: true });
16592
+ rmSync7(staging, { recursive: true, force: true });
16497
16593
  mkdirSync14(staging, { recursive: true });
16498
16594
  try {
16499
16595
  execFileSync22("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
16500
16596
  stdio: ["pipe", "pipe", "pipe"]
16501
16597
  });
16502
16598
  } finally {
16503
- rmSync6(tgzPath, { force: true });
16599
+ rmSync7(tgzPath, { force: true });
16504
16600
  }
16505
16601
  if (!existsSync25(path33.join(staging, "dist", "index.html"))) {
16506
- rmSync6(staging, { recursive: true, force: true });
16602
+ rmSync7(staging, { recursive: true, force: true });
16507
16603
  throw new Error(`${DOCS_PACKAGE}@${version} unpacked without a dist/index.html \u2014 the artifact is malformed`);
16508
16604
  }
16509
- rmSync6(targetDir, { recursive: true, force: true });
16605
+ rmSync7(targetDir, { recursive: true, force: true });
16510
16606
  renameSync3(staging, targetDir);
16511
16607
  return targetDir;
16512
16608
  }
@@ -21458,24 +21554,290 @@ var init_site = __esm({
21458
21554
  }
21459
21555
  });
21460
21556
 
21461
- // packages/mesh-cli/src/commands/stack.ts
21462
- import { execFileSync as execFileSync31 } from "child_process";
21463
- import * as path45 from "path";
21557
+ // packages/mesh-cli/src/commands/stack-status.ts
21464
21558
  import * as fs38 from "fs";
21559
+ import * as path45 from "path";
21560
+ import { execFileSync as execFileSync31 } from "child_process";
21561
+ function splitLayerStack(stack, tenant) {
21562
+ if (tenant && stack.startsWith(`${tenant}-`)) return { tenant, env: stack.slice(tenant.length + 1) };
21563
+ const cut = stack.indexOf("-");
21564
+ if (cut <= 0) return { tenant: tenant ?? stack, env: stack };
21565
+ return { tenant: tenant ?? stack.slice(0, cut), env: stack.slice(cut + 1) };
21566
+ }
21567
+ function targetForStack(projectDir, stack) {
21568
+ const platform = readStackPlatform(projectDir, stack);
21569
+ if (platform) {
21570
+ const [tenant2, env2] = platform.split(".");
21571
+ return { stack, kind: "app", tenant: tenant2, env: env2, coreTenant: tenant2, coreEnv: env2 };
21572
+ }
21573
+ const declared = readStackConfig(projectDir, stack, "mesh:tenant");
21574
+ const layerDir = ["core", "platform"].includes(path45.basename(projectDir));
21575
+ if (!declared && !layerDir) return null;
21576
+ const { tenant, env } = splitLayerStack(stack, declared);
21577
+ const isCore = path45.basename(projectDir) === "core";
21578
+ return {
21579
+ stack,
21580
+ kind: "layer",
21581
+ tenant,
21582
+ env,
21583
+ coreTenant: readStackConfig(projectDir, stack, "mesh:coreTenant") ?? tenant,
21584
+ coreEnv: readStackConfig(projectDir, stack, "mesh:coreEnv") ?? env,
21585
+ ...isCore ? { coreDir: projectDir } : { platformDir: projectDir }
21586
+ };
21587
+ }
21588
+ function findStatusTargets(cwd, stack) {
21589
+ const project = findAppRoot(cwd);
21590
+ let projectDir = project;
21591
+ let repoRoot2 = null;
21592
+ if (!projectDir) {
21593
+ const platformDir = path45.join(cwd, "platform");
21594
+ if (fs38.existsSync(path45.join(platformDir, "Pulumi.yaml"))) {
21595
+ projectDir = platformDir;
21596
+ repoRoot2 = cwd;
21597
+ }
21598
+ } else if (["core", "platform"].includes(path45.basename(projectDir))) {
21599
+ repoRoot2 = path45.dirname(projectDir);
21600
+ }
21601
+ if (!projectDir) return [];
21602
+ const stacks = stack ? [stack] : findStackConfigs(projectDir);
21603
+ const targets = [];
21604
+ for (const name of stacks) {
21605
+ const target = targetForStack(projectDir, name);
21606
+ if (!target) continue;
21607
+ if (repoRoot2 && target.kind === "layer") {
21608
+ const core = path45.join(repoRoot2, "core");
21609
+ const platform = path45.join(repoRoot2, "platform");
21610
+ if (fs38.existsSync(path45.join(core, "package.json"))) target.coreDir = core;
21611
+ if (fs38.existsSync(path45.join(platform, "package.json"))) target.platformDir = platform;
21612
+ }
21613
+ targets.push(target);
21614
+ }
21615
+ return targets;
21616
+ }
21617
+ function pinnedVersion(dir, pkg) {
21618
+ if (!dir) return null;
21619
+ try {
21620
+ const manifest = JSON.parse(fs38.readFileSync(path45.join(dir, "package.json"), "utf-8"));
21621
+ const spec = manifest.dependencies?.[pkg] ?? manifest.devDependencies?.[pkg];
21622
+ return typeof spec === "string" ? spec.replace(/^[\^~=v]+/, "") : null;
21623
+ } catch {
21624
+ return null;
21625
+ }
21626
+ }
21627
+ function compareVersions2(a, b) {
21628
+ const parse4 = (v) => v.split(/[.+-]/).slice(0, 3).map((n) => Number.parseInt(n, 10) || 0);
21629
+ const [x, y] = [parse4(a), parse4(b)];
21630
+ for (let i = 0; i < 3; i++) {
21631
+ const diff = (x[i] ?? 0) - (y[i] ?? 0);
21632
+ if (diff) return diff;
21633
+ }
21634
+ return 0;
21635
+ }
21636
+ function layerState(deployed, latest) {
21637
+ if (!deployed) return "not-deployed";
21638
+ if (!latest) return "unknown";
21639
+ return compareVersions2(deployed, latest) < 0 ? "behind" : "current";
21640
+ }
21641
+ function parseRegistration(raw) {
21642
+ try {
21643
+ const data = JSON.parse(raw);
21644
+ const out = {};
21645
+ if (typeof data.build?.infraVersion === "string") out.infraVersion = data.build.infraVersion;
21646
+ if (typeof data.hub?.hubVersion === "string") out.hubVersion = data.hub.hubVersion;
21647
+ return out;
21648
+ } catch {
21649
+ return {};
21650
+ }
21651
+ }
21652
+ function buildRows(target, deployed, latest) {
21653
+ const corePath = `/mesh-platform/${target.coreTenant}/${target.coreEnv}/core`;
21654
+ const platformPath = `/mesh-platform/${target.tenant}/${target.env}/platform`;
21655
+ const row = (layer, registration, source, field, pinned, latestVersion) => {
21656
+ const value = source && !(source instanceof Error) ? source[field] ?? null : null;
21657
+ const predatesExport = !!source && !(source instanceof Error) && value === null;
21658
+ const note = source instanceof Error ? source.message : source === null ? "no registration \u2014 never deployed, or not readable with these credentials" : predatesExport ? "registration predates version export \u2014 last deployed with an infra-components older than any current release" : void 0;
21659
+ return {
21660
+ layer,
21661
+ registration,
21662
+ pinned,
21663
+ deployed: value,
21664
+ latest: latestVersion,
21665
+ state: source instanceof Error ? "unknown" : predatesExport ? "behind" : layerState(value, latestVersion),
21666
+ undeployedPin: !!pinned && !!value && pinned !== value,
21667
+ ...note ? { note } : {}
21668
+ };
21669
+ };
21670
+ return [
21671
+ row("core", corePath, deployed.core, "infraVersion", pinnedVersion(target.coreDir, INFRA_PACKAGE), latest.infra),
21672
+ row(
21673
+ "platform",
21674
+ platformPath,
21675
+ deployed.platform,
21676
+ "infraVersion",
21677
+ pinnedVersion(target.platformDir, INFRA_PACKAGE),
21678
+ latest.infra
21679
+ ),
21680
+ row("hub", platformPath, deployed.platform, "hubVersion", pinnedVersion(target.platformDir, HUB_PACKAGE2), latest.hub)
21681
+ ];
21682
+ }
21683
+ function renderStatus(reports, cli) {
21684
+ const lines = [];
21685
+ const show = (v) => v ?? "\u2014";
21686
+ for (const { target, rows } of reports) {
21687
+ const what = target.kind === "app" ? `App stack ${target.stack} \u2192 platform ${target.tenant}/${target.env}` : `Platform ${target.tenant}/${target.env} (stack ${target.stack}) on core ${target.coreTenant}/${target.coreEnv}`;
21688
+ lines.push(what);
21689
+ lines.push(` ${"layer".padEnd(9)} ${"pinned".padEnd(10)} ${"deployed".padEnd(10)} ${"latest".padEnd(10)} state`);
21690
+ for (const r of rows) {
21691
+ const flag = r.undeployedPin ? " (pin not deployed)" : "";
21692
+ lines.push(
21693
+ ` ${r.layer.padEnd(9)} ${show(r.pinned).padEnd(10)} ${show(r.deployed).padEnd(10)} ${show(r.latest).padEnd(10)} ${r.state}${flag}`
21694
+ );
21695
+ if (r.note) lines.push(` ${r.registration}: ${r.note}`);
21696
+ }
21697
+ if (rows.some((r) => r.state === "behind" || r.undeployedPin)) {
21698
+ lines.push(...upgradeSteps(target, rows));
21699
+ }
21700
+ lines.push("");
21701
+ }
21702
+ lines.push(
21703
+ `CLI ${CLI_PACKAGE} ${cli.running}` + (cli.latest ? ` (latest ${cli.latest})${cli.behind ? " \u2014 behind: pnpm add -D @mesh-tech/mesh-cli@latest" : ""}` : " (latest unknown)")
21704
+ );
21705
+ return lines.join("\n");
21706
+ }
21707
+ function upgradeSteps(target, rows) {
21708
+ const latest = rows.find((r) => r.latest)?.latest;
21709
+ if (target.kind === "app") {
21710
+ return [
21711
+ " This platform is behind. Its operator upgrades it from the org's platform repo:",
21712
+ " run `mesh stack status` there, then upgrade core and platform together."
21713
+ ];
21714
+ }
21715
+ const to = latest ? `@${latest}` : "@latest";
21716
+ return [
21717
+ " To upgrade (both layers \u2014 deploying platform/ alone leaves core on the old version):",
21718
+ ` 1. core/ and platform/: set ${INFRA_PACKAGE}${to} (and ${HUB_PACKAGE2}${to} in platform/), then pnpm install`,
21719
+ ` 2. cd core && mesh deploy preview --stack ${target.coreTenant}-${target.coreEnv} # review: this layer is the VPC/EKS/RDS`,
21720
+ ` 3. mesh deploy up --stack ${target.coreTenant}-${target.coreEnv} && cd ../platform && mesh deploy up --stack ${target.stack}`,
21721
+ " 4. mesh stack status # both layers now report the new version"
21722
+ ];
21723
+ }
21724
+ function latestPublished(pkg, cwd) {
21725
+ try {
21726
+ const out = execFileSync31("npm", ["view", pkg, "version"], {
21727
+ cwd,
21728
+ encoding: "utf-8",
21729
+ timeout: 2e4,
21730
+ stdio: ["ignore", "pipe", "ignore"]
21731
+ }).trim();
21732
+ return /^\d+\.\d+\.\d+$/.test(out) ? out : null;
21733
+ } catch {
21734
+ return null;
21735
+ }
21736
+ }
21737
+ async function readRegistration(name, region, auth) {
21738
+ const { credentials, profile, signInHint } = auth;
21739
+ try {
21740
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
21741
+ const client = new SSMClient5({
21742
+ region,
21743
+ ...profile && !credentials?.AWS_ACCESS_KEY_ID ? { profile } : {},
21744
+ ...credentials?.AWS_ACCESS_KEY_ID ? {
21745
+ credentials: {
21746
+ accessKeyId: credentials.AWS_ACCESS_KEY_ID,
21747
+ secretAccessKey: credentials.AWS_SECRET_ACCESS_KEY ?? "",
21748
+ sessionToken: credentials.AWS_SESSION_TOKEN
21749
+ }
21750
+ } : {}
21751
+ });
21752
+ const resp = await client.send(new GetParameterCommand2({ Name: name }));
21753
+ return resp.Parameter?.Value ? parseRegistration(resp.Parameter.Value) : null;
21754
+ } catch (err) {
21755
+ const e = err;
21756
+ const kind = e.name || e.__type || e.message || String(err);
21757
+ if (/ParameterNotFound/.test(kind)) return null;
21758
+ if (/credential|ExpiredToken|AccessDenied|UnrecognizedClient|security token/i.test(`${kind} ${e.message ?? ""}`)) {
21759
+ return new Error(`could not read \u2014 no AWS credentials for this account (${signInHint})`);
21760
+ }
21761
+ return new Error(`could not read \u2014 ${e.message ?? kind}`);
21762
+ }
21763
+ }
21764
+ async function runStackStatus(cwd, opts) {
21765
+ const targets = findStatusTargets(cwd, opts.stack);
21766
+ if (targets.length === 0) {
21767
+ logWarn(
21768
+ "No Mesh stack found here. Run from an app directory (a Pulumi.<stack>.yaml naming mesh:platform),\n or from your org's Mesh platform repo (its root, core/ or platform/)."
21769
+ );
21770
+ return true;
21771
+ }
21772
+ const infraLatest = latestPublished(INFRA_PACKAGE, cwd);
21773
+ const hubLatest = latestPublished(HUB_PACKAGE2, cwd);
21774
+ const running = resolveCliRuntime({ vcs: false }).version;
21775
+ const cliLatest = latestPublished(CLI_PACKAGE, cwd);
21776
+ const cli = { running, latest: cliLatest, behind: !!cliLatest && compareVersions2(running, cliLatest) < 0 };
21777
+ if (!infraLatest) logInfo("Registry lookup failed \u2014 `latest` is unknown. Run `mesh registry login` and retry.");
21778
+ const reports = [];
21779
+ for (const target of targets) {
21780
+ const projectDir = findAppRoot(cwd) ?? target.platformDir ?? cwd;
21781
+ const region = opts.region ?? readStackConfig(projectDir, target.stack, "aws:region") ?? "us-east-2";
21782
+ const ambient = !!process.env.AWS_ACCESS_KEY_ID || !!process.env.AWS_PROFILE;
21783
+ let profile = opts.profile;
21784
+ let credentials;
21785
+ if (!profile && !ambient) {
21786
+ profile = readStackConfig(projectDir, target.stack, "aws:profile") ?? void 0;
21787
+ if (!profile && target.kind === "app") {
21788
+ const role = readStackConfig(projectDir, target.stack, "mesh:deployerRole");
21789
+ if (role) credentials = (await resolveAwsCredentials(role, projectDir, target.stack))?.env;
21790
+ }
21791
+ }
21792
+ const loginContext = target.kind === "app" ? derivePlatformContext(projectDir, target.stack) : null;
21793
+ const signInHint = loginContext ? `sign in with \`mesh login ${loginContext} --device\`, or pass --profile` : "set AWS_PROFILE or pass --profile with access to the core's account";
21794
+ const auth = { credentials, profile, signInHint };
21795
+ const [core, platform] = await Promise.all([
21796
+ readRegistration(`/mesh-platform/${target.coreTenant}/${target.coreEnv}/core`, region, auth),
21797
+ readRegistration(`/mesh-platform/${target.tenant}/${target.env}/platform`, region, auth)
21798
+ ]);
21799
+ reports.push({ target, rows: buildRows(target, { core, platform }, { infra: infraLatest, hub: hubLatest }) });
21800
+ }
21801
+ if (opts.json) {
21802
+ process.stdout.write(JSON.stringify({ platforms: reports, cli }, null, 2) + "\n");
21803
+ } else {
21804
+ process.stdout.write(renderStatus(reports, cli) + "\n");
21805
+ }
21806
+ const behind = cli.behind || reports.some((r) => r.rows.some((row) => row.state === "behind" || row.undeployedPin));
21807
+ return !behind;
21808
+ }
21809
+ var INFRA_PACKAGE, HUB_PACKAGE2, CLI_PACKAGE;
21810
+ var init_stack_status = __esm({
21811
+ "packages/mesh-cli/src/commands/stack-status.ts"() {
21812
+ "use strict";
21813
+ init_log();
21814
+ init_pulumi();
21815
+ init_aws_auth();
21816
+ init_build_info();
21817
+ INFRA_PACKAGE = "@mesh-tech/infra-components";
21818
+ HUB_PACKAGE2 = "@mesh-tech/hub";
21819
+ CLI_PACKAGE = "@mesh-tech/mesh-cli";
21820
+ }
21821
+ });
21822
+
21823
+ // packages/mesh-cli/src/commands/stack.ts
21824
+ import { execFileSync as execFileSync32 } from "child_process";
21825
+ import * as path46 from "path";
21826
+ import * as fs39 from "fs";
21465
21827
  import { parse as parseYaml6 } from "yaml";
21466
21828
  function readTopLevelYamlKey(appRoot, stack, key) {
21467
- const configFile = path45.join(appRoot, `Pulumi.${stack}.yaml`);
21468
- if (!fs38.existsSync(configFile)) return null;
21469
- const content = fs38.readFileSync(configFile, "utf-8");
21829
+ const configFile = path46.join(appRoot, `Pulumi.${stack}.yaml`);
21830
+ if (!fs39.existsSync(configFile)) return null;
21831
+ const content = fs39.readFileSync(configFile, "utf-8");
21470
21832
  const pattern = new RegExp(`^${key}:\\s*(.+)$`, "m");
21471
21833
  const match = content.match(pattern);
21472
21834
  if (!match) return null;
21473
21835
  return match[1].trim().replace(/^["']|["']$/g, "");
21474
21836
  }
21475
21837
  function readConfigBlockKey(appRoot, stack, key) {
21476
- const configFile = path45.join(appRoot, `Pulumi.${stack}.yaml`);
21477
- if (!fs38.existsSync(configFile)) return null;
21478
- const content = fs38.readFileSync(configFile, "utf-8");
21838
+ const configFile = path46.join(appRoot, `Pulumi.${stack}.yaml`);
21839
+ if (!fs39.existsSync(configFile)) return null;
21840
+ const content = fs39.readFileSync(configFile, "utf-8");
21479
21841
  const pattern = new RegExp(`^\\s{2}${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
21480
21842
  const match = content.match(pattern);
21481
21843
  if (!match) return null;
@@ -21535,11 +21897,11 @@ ${records}
21535
21897
  }
21536
21898
  }
21537
21899
  function readBaseConfigFromYaml(appRoot, stack) {
21538
- const file = path45.join(appRoot, `Pulumi.${stack}.yaml`);
21539
- if (!fs38.existsSync(file)) return {};
21900
+ const file = path46.join(appRoot, `Pulumi.${stack}.yaml`);
21901
+ if (!fs39.existsSync(file)) return {};
21540
21902
  let doc;
21541
21903
  try {
21542
- doc = parseYaml6(fs38.readFileSync(file, "utf-8"));
21904
+ doc = parseYaml6(fs39.readFileSync(file, "utf-8"));
21543
21905
  } catch {
21544
21906
  return {};
21545
21907
  }
@@ -21552,7 +21914,7 @@ function readBaseConfigFromYaml(appRoot, stack) {
21552
21914
  }
21553
21915
  function getGitHubUsername() {
21554
21916
  try {
21555
- const result = execFileSync31("gh", ["api", "user", "--jq", ".login"], {
21917
+ const result = execFileSync32("gh", ["api", "user", "--jq", ".login"], {
21556
21918
  encoding: "utf-8",
21557
21919
  stdio: ["pipe", "pipe", "pipe"]
21558
21920
  });
@@ -21561,7 +21923,7 @@ function getGitHubUsername() {
21561
21923
  } catch {
21562
21924
  }
21563
21925
  try {
21564
- const result = execFileSync31("git", ["config", "user.email"], {
21926
+ const result = execFileSync32("git", ["config", "user.email"], {
21565
21927
  encoding: "utf-8",
21566
21928
  stdio: ["pipe", "pipe", "pipe"]
21567
21929
  });
@@ -21573,7 +21935,7 @@ function getGitHubUsername() {
21573
21935
  return null;
21574
21936
  }
21575
21937
  function registerStackCommand(program2) {
21576
- const stack = program2.command("stack").description("Manage personal Pulumi stacks");
21938
+ const stack = program2.command("stack").description("Manage personal Pulumi stacks and check a Mesh instance against the latest release");
21577
21939
  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(
21578
21940
  "--worktree",
21579
21941
  "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)"
@@ -21616,7 +21978,7 @@ Specify which to base on: mesh stack init --from <stack>`
21616
21978
  }
21617
21979
  }
21618
21980
  const baseConfigPath = `${appRoot}/Pulumi.${baseStack}.yaml`;
21619
- if (!fs38.existsSync(baseConfigPath)) {
21981
+ if (!fs39.existsSync(baseConfigPath)) {
21620
21982
  logError(`Stack config not found: Pulumi.${baseStack}.yaml`);
21621
21983
  process.exit(1);
21622
21984
  }
@@ -21638,8 +22000,8 @@ Specify which to base on: mesh stack init --from <stack>`
21638
22000
  );
21639
22001
  }
21640
22002
  }
21641
- const newConfigPath = path45.join(appRoot, `Pulumi.${newStack}.yaml`);
21642
- const configExists = fs38.existsSync(newConfigPath);
22003
+ const newConfigPath = path46.join(appRoot, `Pulumi.${newStack}.yaml`);
22004
+ const configExists = fs39.existsSync(newConfigPath);
21643
22005
  const secretsProvider = readTopLevelYamlKey(appRoot, baseStack, "secretsprovider");
21644
22006
  const credEnv = await resolvePulumiEnv({ appRoot, stack: baseStack });
21645
22007
  const pulumiEnv = { ...process.env, ...credEnv };
@@ -21647,7 +22009,7 @@ Specify which to base on: mesh stack init --from <stack>`
21647
22009
  if (!opts.adopt) {
21648
22010
  let existing = [];
21649
22011
  try {
21650
- const raw = execFileSync31("pulumi", ["stack", "ls", "--json"], {
22012
+ const raw = execFileSync32("pulumi", ["stack", "ls", "--json"], {
21651
22013
  cwd: appRoot,
21652
22014
  encoding: "utf-8",
21653
22015
  env: pulumiEnv,
@@ -21671,7 +22033,7 @@ Specify which to base on: mesh stack init --from <stack>`
21671
22033
  logInfo(`Using KMS secrets provider: ${secretsProvider}`);
21672
22034
  }
21673
22035
  try {
21674
- execFileSync31("pulumi", initArgs, {
22036
+ execFileSync32("pulumi", initArgs, {
21675
22037
  cwd: appRoot,
21676
22038
  env: pulumiEnv,
21677
22039
  stdio: "inherit"
@@ -21688,7 +22050,7 @@ Specify which to base on: mesh stack init --from <stack>`
21688
22050
  }
21689
22051
  }
21690
22052
  try {
21691
- execFileSync31("pulumi", ["stack", "select", newStack], {
22053
+ execFileSync32("pulumi", ["stack", "select", newStack], {
21692
22054
  cwd: appRoot,
21693
22055
  env: pulumiEnv,
21694
22056
  stdio: ["pipe", "pipe", "pipe"]
@@ -21698,7 +22060,7 @@ Specify which to base on: mesh stack init --from <stack>`
21698
22060
  if (!configExists) {
21699
22061
  let baseConfig = {};
21700
22062
  try {
21701
- const raw = execFileSync31(
22063
+ const raw = execFileSync32(
21702
22064
  "pulumi",
21703
22065
  ["config", "--json", "--stack", baseStack],
21704
22066
  { cwd: appRoot, env: pulumiEnv, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
@@ -21716,19 +22078,19 @@ Specify which to base on: mesh stack init --from <stack>`
21716
22078
  if (key === "mesh:deploy") continue;
21717
22079
  try {
21718
22080
  if (entry.objectValue !== void 0) {
21719
- execFileSync31(
22081
+ execFileSync32(
21720
22082
  "pulumi",
21721
22083
  ["config", "set", key, JSON.stringify(entry.objectValue)],
21722
22084
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
21723
22085
  );
21724
22086
  } else if (entry.value === "true" || entry.value === "false") {
21725
- execFileSync31(
22087
+ execFileSync32(
21726
22088
  "pulumi",
21727
22089
  ["config", "set", "--type", "bool", key, entry.value],
21728
22090
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
21729
22091
  );
21730
22092
  } else {
21731
- execFileSync31(
22093
+ execFileSync32(
21732
22094
  "pulumi",
21733
22095
  ["config", "set", key, entry.value],
21734
22096
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
@@ -21741,7 +22103,7 @@ Specify which to base on: mesh stack init --from <stack>`
21741
22103
  const baseEnv = readConfigBlockKey(appRoot, baseStack, "mesh:coreEnv") ?? (baseTenant && baseStack.startsWith(`${baseTenant}-`) ? baseStack.slice(baseTenant.length + 1) : baseStack);
21742
22104
  const setCfg = (args) => {
21743
22105
  try {
21744
- execFileSync31("pulumi", ["config", "set", ...args], {
22106
+ execFileSync32("pulumi", ["config", "set", ...args], {
21745
22107
  cwd: appRoot,
21746
22108
  env: pulumiEnv,
21747
22109
  stdio: ["pipe", "pipe", "pipe"]
@@ -21790,7 +22152,7 @@ Specify which to base on: mesh stack init --from <stack>`
21790
22152
  `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}` : ""})`
21791
22153
  );
21792
22154
  } else {
21793
- execFileSync31("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
22155
+ execFileSync32("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
21794
22156
  cwd: appRoot,
21795
22157
  env: pulumiEnv,
21796
22158
  stdio: ["pipe", "pipe", "pipe"]
@@ -21820,12 +22182,18 @@ Specify which to base on: mesh stack init --from <stack>`
21820
22182
  const args = ["stack", "rm", name];
21821
22183
  if (opts.yes) args.push("--yes");
21822
22184
  try {
21823
- execFileSync31("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
22185
+ execFileSync32("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
21824
22186
  logSuccess(`Removed stack ${name}`);
21825
22187
  } catch (err) {
21826
22188
  process.exit(err.status ?? 1);
21827
22189
  }
21828
22190
  });
22191
+ stack.command("status").description(
22192
+ "Read-only drift check: for each platform this directory deploys (a platform repo's core/platform stacks) or deploys apps to (an app's mesh:platform), compare the pinned, deployed (the core and platform SSM registrations written by the last successful deploy) and latest published @mesh-tech/infra-components and @mesh-tech/hub, plus this CLI against the latest @mesh-tech/mesh-cli, and print the upgrade steps when anything is behind. Changes nothing."
22193
+ ).option("--stack <name>", "check one stack (default: every Pulumi.<stack>.yaml here)").option("--profile <profile>", "AWS profile to read the SSM registrations with (default: AWS_PROFILE, else the stack's aws:profile, else an app stack's deployer role)").option("--region <region>", "AWS region of the registrations (default: the stack's aws:region, else us-east-2)").option("--json", "print the report as JSON", false).option("--check", "exit 1 when any layer or the CLI is behind, or a pin is not deployed", false).action(async (opts) => {
22194
+ const ok = await runStackStatus(process.cwd(), opts);
22195
+ if (opts.check && !ok) process.exit(1);
22196
+ });
21829
22197
  }
21830
22198
  var init_stack2 = __esm({
21831
22199
  "packages/mesh-cli/src/commands/stack.ts"() {
@@ -21834,6 +22202,7 @@ var init_stack2 = __esm({
21834
22202
  init_pulumi();
21835
22203
  init_worktree_identity();
21836
22204
  init_pulumi_run();
22205
+ init_stack_status();
21837
22206
  }
21838
22207
  });
21839
22208
 
@@ -22032,12 +22401,12 @@ var init_recover_conversation = __esm({
22032
22401
  });
22033
22402
 
22034
22403
  // packages/mesh-cli/src/utils/temporal-codec.ts
22035
- import { execFileSync as execFileSync32 } from "child_process";
22404
+ import { execFileSync as execFileSync33 } from "child_process";
22036
22405
  import { webcrypto as crypto4 } from "node:crypto";
22037
22406
  function resolveTemporalEncodingKeyFromK8s(namespace) {
22038
22407
  const secretName = `${namespace}-temporal-encoding-key`;
22039
22408
  try {
22040
- const b64 = execFileSync32(
22409
+ const b64 = execFileSync33(
22041
22410
  "kubectl",
22042
22411
  [
22043
22412
  "get",
@@ -22288,7 +22657,7 @@ var init_capture_history = __esm({
22288
22657
  import { spawnSync as spawnSync4 } from "node:child_process";
22289
22658
  import { writeFileSync as writeFileSync24, mkdirSync as mkdirSync20 } from "node:fs";
22290
22659
  import { homedir as homedir6 } from "node:os";
22291
- import { dirname as dirname29, join as join42, resolve as resolve19 } from "node:path";
22660
+ import { dirname as dirname30, join as join43, resolve as resolve19 } from "node:path";
22292
22661
  async function resolveConnection(options) {
22293
22662
  if (options.address && options.namespace) {
22294
22663
  return { address: options.address, namespace: options.namespace };
@@ -22716,8 +23085,8 @@ async function captureHistory(workflowId, runId, options) {
22716
23085
  }
22717
23086
  const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
22718
23087
  const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
22719
- const outPath = options.out ?? join42(homedir6(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
22720
- mkdirSync20(dirname29(outPath), { recursive: true });
23088
+ const outPath = options.out ?? join43(homedir6(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
23089
+ mkdirSync20(dirname30(outPath), { recursive: true });
22721
23090
  if (options.out) warnIfNotGitIgnored(options.out);
22722
23091
  writeFileSync24(outPath, JSON.stringify(fixture, null, 2) + "\n");
22723
23092
  logSuccess(
@@ -22736,7 +23105,7 @@ function sanitizeFileId(id) {
22736
23105
  function warnIfNotGitIgnored(outPath) {
22737
23106
  const abs = resolve19(outPath);
22738
23107
  try {
22739
- const res = spawnSync4("git", ["-C", dirname29(abs), "check-ignore", "-q", abs], {
23108
+ const res = spawnSync4("git", ["-C", dirname30(abs), "check-ignore", "-q", abs], {
22740
23109
  stdio: "ignore"
22741
23110
  });
22742
23111
  if (res.status !== 1) return;
@@ -22908,8 +23277,8 @@ var init_temporal = __esm({
22908
23277
 
22909
23278
  // packages/mesh-cli/src/commands/tenant.ts
22910
23279
  import chalk7 from "chalk";
22911
- import * as fs39 from "fs";
22912
- import * as path46 from "path";
23280
+ import * as fs40 from "fs";
23281
+ import * as path47 from "path";
22913
23282
  import { parseDocument, YAMLMap, isMap } from "yaml";
22914
23283
  function validateTenantName(name) {
22915
23284
  return /^[a-z][a-z0-9-]*$/.test(name) && !name.endsWith("-");
@@ -22977,16 +23346,16 @@ function resolveStackConfig(explicitStack) {
22977
23346
  { remediation: { command: "mesh tenant add <name> --stack <stack>" } }
22978
23347
  );
22979
23348
  }
22980
- const file = path46.join(appRoot, `Pulumi.${stack}.yaml`);
22981
- if (!fs39.existsSync(file)) {
23349
+ const file = path47.join(appRoot, `Pulumi.${stack}.yaml`);
23350
+ if (!fs40.existsSync(file)) {
22982
23351
  throw new MeshCliError(`Stack config not found: ${file}`, {
22983
23352
  remediation: { command: "mesh tenant add <name> --stack <stack>" }
22984
23353
  });
22985
23354
  }
22986
- return { appRoot, stack, file, content: fs39.readFileSync(file, "utf-8") };
23355
+ return { appRoot, stack, file, content: fs40.readFileSync(file, "utf-8") };
22987
23356
  }
22988
23357
  function readProjectName(appRoot) {
22989
- const projectDoc = parseDocument(fs39.readFileSync(path46.join(appRoot, "Pulumi.yaml"), "utf-8"));
23358
+ const projectDoc = parseDocument(fs40.readFileSync(path47.join(appRoot, "Pulumi.yaml"), "utf-8"));
22990
23359
  return String(projectDoc.get("name") ?? "");
22991
23360
  }
22992
23361
  function looksLikePlatformLayer(projectName) {
@@ -23029,12 +23398,12 @@ function registerTenantCommands(program2) {
23029
23398
  if (!result.ok) {
23030
23399
  if (result.reason === "exists") {
23031
23400
  throw new MeshCliError(
23032
- `${result.detail} (${path46.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
23401
+ `${result.detail} (${path47.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
23033
23402
  );
23034
23403
  }
23035
23404
  throw new MeshCliError(`${resolved.file}: ${result.detail}`);
23036
23405
  }
23037
- atomicWriteFileSync(resolved.file, result.yaml, fs39.statSync(resolved.file).mode & 511);
23406
+ atomicWriteFileSync(resolved.file, result.yaml, fs40.statSync(resolved.file).mode & 511);
23038
23407
  if (opts.json) {
23039
23408
  emitJsonPayload({
23040
23409
  ok: true,
@@ -23047,7 +23416,7 @@ function registerTenantCommands(program2) {
23047
23416
  return;
23048
23417
  }
23049
23418
  logInfo(
23050
- `Tenant '${name}' registered in ${path46.basename(resolved.file)} (mesh:tenants, subdomain '${subdomain}')${result.createdTenantsBlock ? " \u2014 created the mesh:tenants block" : ""}`
23419
+ `Tenant '${name}' registered in ${path47.basename(resolved.file)} (mesh:tenants, subdomain '${subdomain}')${result.createdTenantsBlock ? " \u2014 created the mesh:tenants block" : ""}`
23051
23420
  );
23052
23421
  console.log("");
23053
23422
  console.log("Next steps:");
@@ -23073,7 +23442,7 @@ function registerTenantCommands(program2) {
23073
23442
  `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).`
23074
23443
  );
23075
23444
  }
23076
- logInfo(`No tenants declared in ${path46.basename(resolved.file)}.`);
23445
+ logInfo(`No tenants declared in ${path47.basename(resolved.file)}.`);
23077
23446
  return;
23078
23447
  }
23079
23448
  logInfo(`Tenants on stack '${resolved.stack}':`);
@@ -23095,9 +23464,9 @@ var init_tenant = __esm({
23095
23464
 
23096
23465
  // packages/mesh-cli/src/commands/tunnel/index.ts
23097
23466
  import { spawn as spawn9 } from "child_process";
23098
- import * as fs40 from "fs";
23467
+ import * as fs41 from "fs";
23099
23468
  import * as os12 from "os";
23100
- import * as path47 from "path";
23469
+ import * as path48 from "path";
23101
23470
  import {
23102
23471
  SecretsManagerClient as SecretsManagerClient9,
23103
23472
  GetSecretValueCommand as GetSecretValueCommand9
@@ -23117,9 +23486,9 @@ function readPulumiAwsRegion(stage) {
23117
23486
  const preferred = stage ? `Pulumi.${stage}.yaml` : void 0;
23118
23487
  const rank = (f) => f === preferred ? 0 : f === "Pulumi.yaml" ? 2 : 1;
23119
23488
  try {
23120
- const files = fs40.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
23489
+ const files = fs41.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
23121
23490
  for (const f of files) {
23122
- const m = fs40.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
23491
+ const m = fs41.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
23123
23492
  if (m?.[1]) return m[1].trim();
23124
23493
  }
23125
23494
  } catch {
@@ -23128,14 +23497,14 @@ function readPulumiAwsRegion(stage) {
23128
23497
  }
23129
23498
  function awsProfileSections(name) {
23130
23499
  return [
23131
- [path47.join(os12.homedir(), ".aws", "config"), `[profile ${name}]`],
23132
- [path47.join(os12.homedir(), ".aws", "credentials"), `[${name}]`]
23500
+ [path48.join(os12.homedir(), ".aws", "config"), `[profile ${name}]`],
23501
+ [path48.join(os12.homedir(), ".aws", "credentials"), `[${name}]`]
23133
23502
  ];
23134
23503
  }
23135
23504
  function awsProfileExists(name) {
23136
23505
  for (const [file, header] of awsProfileSections(name)) {
23137
23506
  try {
23138
- const lines = fs40.readFileSync(file, "utf-8").split("\n");
23507
+ const lines = fs41.readFileSync(file, "utf-8").split("\n");
23139
23508
  if (lines.some((l) => l.trim() === header)) return true;
23140
23509
  } catch {
23141
23510
  }
@@ -23161,7 +23530,7 @@ function awsProfileRegion(profile) {
23161
23530
  if (!profile) return void 0;
23162
23531
  for (const [file, header] of awsProfileSections(profile)) {
23163
23532
  try {
23164
- const region = parseProfileRegion(fs40.readFileSync(file, "utf-8"), header);
23533
+ const region = parseProfileRegion(fs41.readFileSync(file, "utf-8"), header);
23165
23534
  if (region) return region;
23166
23535
  } catch {
23167
23536
  }
@@ -23170,14 +23539,14 @@ function awsProfileRegion(profile) {
23170
23539
  }
23171
23540
  function awsDefaultProfileSections() {
23172
23541
  return [
23173
- [path47.join(os12.homedir(), ".aws", "config"), "[default]"],
23174
- [path47.join(os12.homedir(), ".aws", "credentials"), "[default]"]
23542
+ [path48.join(os12.homedir(), ".aws", "config"), "[default]"],
23543
+ [path48.join(os12.homedir(), ".aws", "credentials"), "[default]"]
23175
23544
  ];
23176
23545
  }
23177
23546
  function awsDefaultProfileRegion() {
23178
23547
  for (const [file, header] of awsDefaultProfileSections()) {
23179
23548
  try {
23180
- const region = parseProfileRegion(fs40.readFileSync(file, "utf-8"), header);
23549
+ const region = parseProfileRegion(fs41.readFileSync(file, "utf-8"), header);
23181
23550
  if (region) return region;
23182
23551
  } catch {
23183
23552
  }
@@ -23564,8 +23933,8 @@ async function resolveToken(opts) {
23564
23933
  "no token: pass --token, set VCS_TOKEN, or pass --context <platform-context> (after mesh login)"
23565
23934
  );
23566
23935
  }
23567
- async function vcsApi(baseUrl, token, path49, method = "GET", body) {
23568
- const res = await fetch(`${baseUrl}${path49}`, {
23936
+ async function vcsApi(baseUrl, token, path50, method = "GET", body) {
23937
+ const res = await fetch(`${baseUrl}${path50}`, {
23569
23938
  method,
23570
23939
  headers: {
23571
23940
  authorization: `Bearer ${token}`,
@@ -23576,7 +23945,7 @@ async function vcsApi(baseUrl, token, path49, method = "GET", body) {
23576
23945
  const data = await res.json().catch(() => ({}));
23577
23946
  if (!res.ok) {
23578
23947
  throw new Error(
23579
- `${method} ${path49} failed (${res.status}): ${String(data.error ?? "unknown error")}`
23948
+ `${method} ${path50} failed (${res.status}): ${String(data.error ?? "unknown error")}`
23580
23949
  );
23581
23950
  }
23582
23951
  return data;
@@ -23622,8 +23991,8 @@ var init_clone = __esm({
23622
23991
 
23623
23992
  // packages/mesh-cli/src/commands/vcs/get.ts
23624
23993
  import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
23625
- import { join as join45, dirname as dirname30 } from "node:path";
23626
- async function getCommand(repo, path49, opts) {
23994
+ import { join as join46, dirname as dirname31 } from "node:path";
23995
+ async function getCommand(repo, path50, opts) {
23627
23996
  let vcsBaseUrl;
23628
23997
  let token;
23629
23998
  if (opts.target) {
@@ -23638,18 +24007,18 @@ async function getCommand(repo, path49, opts) {
23638
24007
  if (!token) throw new Error("no token: pass --target (after mesh login), or --token / --context");
23639
24008
  const reader = createVcsFolderReader({ vcsBaseUrl, token });
23640
24009
  try {
23641
- const { ref, files } = await reader.readPath(repo, path49);
24010
+ const { ref, files } = await reader.readPath(repo, path50);
23642
24011
  if (files.length === 0) {
23643
- logInfo(`no files at ${repo}:${path49}`);
24012
+ logInfo(`no files at ${repo}:${path50}`);
23644
24013
  return;
23645
24014
  }
23646
24015
  if (opts.output) {
23647
24016
  for (const f of files) {
23648
- const abs = join45(opts.output, f.path);
23649
- await mkdir2(dirname30(abs), { recursive: true });
24017
+ const abs = join46(opts.output, f.path);
24018
+ await mkdir2(dirname31(abs), { recursive: true });
23650
24019
  await writeFile2(abs, f.contents);
23651
24020
  }
23652
- logInfo(`wrote ${files.length} file(s) from ${repo}:${path49} (${ref}) \u2192 ${opts.output}`);
24021
+ logInfo(`wrote ${files.length} file(s) from ${repo}:${path50} (${ref}) \u2192 ${opts.output}`);
23653
24022
  } else {
23654
24023
  for (const f of files) {
23655
24024
  if (files.length > 1) process.stdout.write(`
@@ -23674,10 +24043,10 @@ var init_get = __esm({
23674
24043
  });
23675
24044
 
23676
24045
  // packages/mesh-cli/src/commands/vcs/drafts.ts
23677
- async function call(opts, repo, path49, method = "GET", body) {
24046
+ async function call(opts, repo, path50, method = "GET", body) {
23678
24047
  const target = await resolveTarget2({ ...opts, repo });
23679
24048
  const token = await resolveToken(opts);
23680
- return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path49}`, method, body);
24049
+ return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path50}`, method, body);
23681
24050
  }
23682
24051
  async function draftsListCommand(repo, opts) {
23683
24052
  const data = await call(opts, repo, `/drafts${opts.all ? "?all=true" : ""}`);
@@ -23732,7 +24101,7 @@ var init_drafts = __esm({
23732
24101
 
23733
24102
  // packages/mesh-cli/src/commands/vcs/propose.ts
23734
24103
  import { readFile as readFile3 } from "node:fs/promises";
23735
- import { join as join46 } from "node:path";
24104
+ import { join as join47 } from "node:path";
23736
24105
  function parseStatus(out) {
23737
24106
  const tokens = out.split("\0");
23738
24107
  const changes = [];
@@ -23765,13 +24134,13 @@ function parseStatus(out) {
23765
24134
  }
23766
24135
  return changes;
23767
24136
  }
23768
- async function writeOp(cwd, path49) {
23769
- const buf = await readFile3(join46(cwd, path49));
24137
+ async function writeOp(cwd, path50) {
24138
+ const buf = await readFile3(join47(cwd, path50));
23770
24139
  const asUtf8 = buf.toString("utf8");
23771
24140
  if (Buffer.from(asUtf8, "utf8").equals(buf)) {
23772
- return { op: "write", path: path49, content: asUtf8 };
24141
+ return { op: "write", path: path50, content: asUtf8 };
23773
24142
  }
23774
- return { op: "write", path: path49, content: buf.toString("base64"), encoding: "base64" };
24143
+ return { op: "write", path: path50, content: buf.toString("base64"), encoding: "base64" };
23775
24144
  }
23776
24145
  async function proposeCommand(opts) {
23777
24146
  const cwd = process.cwd();
@@ -23815,8 +24184,8 @@ async function proposeCommand(opts) {
23815
24184
  operations,
23816
24185
  ...opts.mergeParent ? { mergeParent: opts.mergeParent } : {}
23817
24186
  };
23818
- const path49 = opts.revise ? `/v1/repos/${target.repo}/proposals/${opts.revise}/revisions` : `/v1/repos/${target.repo}/proposals`;
23819
- const data = await vcsApi(target.baseUrl, token, path49, "POST", body);
24187
+ const path50 = opts.revise ? `/v1/repos/${target.repo}/proposals/${opts.revise}/revisions` : `/v1/repos/${target.repo}/proposals`;
24188
+ const data = await vcsApi(target.baseUrl, token, path50, "POST", body);
23820
24189
  console.log(
23821
24190
  `proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 review: mesh vcs show ${target.repo} ${data.proposalId} --url ${target.baseUrl}`
23822
24191
  );
@@ -23831,28 +24200,28 @@ var init_propose = __esm({
23831
24200
  // packages/mesh-cli/src/commands/vcs/rm.ts
23832
24201
  import { mkdtemp as mkdtemp2, rm as rmrf } from "node:fs/promises";
23833
24202
  import { tmpdir as tmpdir10 } from "node:os";
23834
- import { join as join47 } from "node:path";
24203
+ import { join as join48 } from "node:path";
23835
24204
  function normalize2(p) {
23836
24205
  return p.trim().replace(/^\.\//, "").replace(/\/+$/, "");
23837
24206
  }
23838
24207
  function expandPathsToDeletions(repoFiles, requested) {
23839
24208
  const out = /* @__PURE__ */ new Set();
23840
24209
  for (const raw of requested) {
23841
- const path49 = normalize2(raw);
23842
- if (path49.startsWith("/") || path49.split("/").includes("..")) {
24210
+ const path50 = normalize2(raw);
24211
+ if (path50.startsWith("/") || path50.split("/").includes("..")) {
23843
24212
  throw new Error(
23844
24213
  `"${raw}" is not a repo-relative path \u2014 vcs paths never start with "/" or traverse with "..".`
23845
24214
  );
23846
24215
  }
23847
- const exact = repoFiles.filter((f) => f === path49);
24216
+ const exact = repoFiles.filter((f) => f === path50);
23848
24217
  if (exact.length > 0) {
23849
24218
  for (const f of exact) out.add(f);
23850
24219
  continue;
23851
24220
  }
23852
- const under = repoFiles.filter((f) => f.startsWith(`${path49}/`));
24221
+ const under = repoFiles.filter((f) => f.startsWith(`${path50}/`));
23853
24222
  if (under.length === 0) {
23854
24223
  throw new Error(
23855
- `"${raw}" matched no files in the repo \u2014 nothing was proposed. Check the path with: mesh vcs get <repo> ${path49}`
24224
+ `"${raw}" matched no files in the repo \u2014 nothing was proposed. Check the path with: mesh vcs get <repo> ${path50}`
23856
24225
  );
23857
24226
  }
23858
24227
  for (const f of under) out.add(f);
@@ -23863,7 +24232,7 @@ async function rmCommand(repo, paths, opts) {
23863
24232
  const baseUrl = (opts.url ?? process.env.VCS_URL)?.replace(/\/$/, "");
23864
24233
  if (!baseUrl) throw new Error("rm requires --url or VCS_URL");
23865
24234
  const token = await resolveToken(opts);
23866
- const dir = await mkdtemp2(join47(tmpdir10(), "mesh-vcs-rm-"));
24235
+ const dir = await mkdtemp2(join48(tmpdir10(), "mesh-vcs-rm-"));
23867
24236
  try {
23868
24237
  const git = (args, cwd) => gitWithAuth(args, token, cwd);
23869
24238
  await git([
@@ -23888,7 +24257,7 @@ async function rmCommand(repo, paths, opts) {
23888
24257
  const body = {
23889
24258
  baseCommit: head.trim(),
23890
24259
  message: opts.message ?? `Delete ${doomed.length} file(s)`,
23891
- operations: doomed.map((path49) => ({ op: "delete", path: path49 }))
24260
+ operations: doomed.map((path50) => ({ op: "delete", path: path50 }))
23892
24261
  };
23893
24262
  const data = await vcsApi(
23894
24263
  baseUrl,
@@ -23914,10 +24283,10 @@ var init_rm = __esm({
23914
24283
  });
23915
24284
 
23916
24285
  // packages/mesh-cli/src/commands/vcs/review.ts
23917
- async function call2(opts, repo, path49, method = "GET", body) {
24286
+ async function call2(opts, repo, path50, method = "GET", body) {
23918
24287
  const target = await resolveTarget2({ ...opts, repo });
23919
24288
  const token = await resolveToken(opts);
23920
- return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path49}`, method, body);
24289
+ return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path50}`, method, body);
23921
24290
  }
23922
24291
  async function proposalsCommand(repo, opts) {
23923
24292
  console.log(JSON.stringify(await call2(opts, repo, "/proposals"), null, 2));
@@ -24064,8 +24433,8 @@ function computeHappyPath(nodes, edges) {
24064
24433
  ];
24065
24434
  const visited = /* @__PURE__ */ new Set([startNode.id]);
24066
24435
  while (queue.length > 0) {
24067
- const { id, path: path49 } = queue.shift();
24068
- if (targetIds.has(id)) return path49;
24436
+ const { id, path: path50 } = queue.shift();
24437
+ if (targetIds.has(id)) return path50;
24069
24438
  const neighbors = adjacency.get(id) ?? [];
24070
24439
  const hasMainEdge = neighbors.some((e) => e.isMainPath);
24071
24440
  const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
@@ -24085,7 +24454,7 @@ function computeHappyPath(nodes, edges) {
24085
24454
  if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
24086
24455
  continue;
24087
24456
  visited.add(neighbor.to);
24088
- queue.push({ id: neighbor.to, path: [...path49, neighbor.to] });
24457
+ queue.push({ id: neighbor.to, path: [...path50, neighbor.to] });
24089
24458
  }
24090
24459
  }
24091
24460
  return null;
@@ -24370,11 +24739,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
24370
24739
  }
24371
24740
  }
24372
24741
  if (found) {
24373
- const path49 = [];
24374
- for (let c = found; c !== void 0; c = prev.get(c)) path49.unshift(c);
24375
- for (let i = 0; i < path49.length - 1; i++) mainEdgeKeys.add(edgeKey(path49[i], path49[i + 1]));
24742
+ const path50 = [];
24743
+ for (let c = found; c !== void 0; c = prev.get(c)) path50.unshift(c);
24744
+ for (let i = 0; i < path50.length - 1; i++) mainEdgeKeys.add(edgeKey(path50[i], path50[i + 1]));
24376
24745
  mainEdgeKeys.add(edgeKey(found, successExit.id));
24377
- mainBody = path49;
24746
+ mainBody = path50;
24378
24747
  mainTail = [successExit.id];
24379
24748
  }
24380
24749
  }
@@ -24910,11 +25279,11 @@ function buildProcessManifest(artifact, outcomeNodeIds) {
24910
25279
  ...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
24911
25280
  };
24912
25281
  }
24913
- function formatPath(path49) {
24914
- return path49.length > 0 ? z4.core.toDotPath(path49) : "(root)";
25282
+ function formatPath(path50) {
25283
+ return path50.length > 0 ? z4.core.toDotPath(path50) : "(root)";
24915
25284
  }
24916
25285
  function issueToDiagnostic(issue) {
24917
- const path49 = formatPath(issue.path);
25286
+ const path50 = formatPath(issue.path);
24918
25287
  let code = "SCHEMA_INVALID";
24919
25288
  if (issue.code === "custom") {
24920
25289
  const paramCode = issue.params?.code;
@@ -24925,8 +25294,8 @@ function issueToDiagnostic(issue) {
24925
25294
  return {
24926
25295
  severity: "error",
24927
25296
  code,
24928
- message: `${path49}: ${issue.message}`,
24929
- path: path49
25297
+ message: `${path50}: ${issue.message}`,
25298
+ path: path50
24930
25299
  };
24931
25300
  }
24932
25301
  function upgradeV1(artifact) {
@@ -25093,24 +25462,24 @@ var init_process_artifact = __esm({
25093
25462
  });
25094
25463
  }
25095
25464
  const declaredSelectors = new Set(Object.keys(process2.selectors ?? {}));
25096
- const checkActor = (actorId, path49) => {
25465
+ const checkActor = (actorId, path50) => {
25097
25466
  if (actorId === void 0) return;
25098
25467
  if (!Object.prototype.hasOwnProperty.call(process2.actors, actorId)) {
25099
25468
  ctx.addIssue({
25100
25469
  code: "custom",
25101
- path: path49,
25102
- message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path49)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
25470
+ path: path50,
25471
+ message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path50)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
25103
25472
  params: { code: "UNDECLARED_ACTOR" }
25104
25473
  });
25105
25474
  }
25106
25475
  };
25107
- const checkSelector = (selector, path49) => {
25476
+ const checkSelector = (selector, path50) => {
25108
25477
  if (selector === void 0) return;
25109
25478
  if (!declaredSelectors.has(selector)) {
25110
25479
  ctx.addIssue({
25111
25480
  code: "custom",
25112
- path: path49,
25113
- message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path49)} but not declared in process.selectors. Declare the selector or fix the reference.`,
25481
+ path: path50,
25482
+ message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path50)} but not declared in process.selectors. Declare the selector or fix the reference.`,
25114
25483
  params: { code: "UNDECLARED_SELECTOR" }
25115
25484
  });
25116
25485
  }
@@ -25124,20 +25493,20 @@ var init_process_artifact = __esm({
25124
25493
  outcome: "DUPLICATE_OUTCOME_ID"
25125
25494
  };
25126
25495
  const idFirstSeenAt = /* @__PURE__ */ new Map();
25127
- const checkId = (id, kind, path49) => {
25496
+ const checkId = (id, kind, path50) => {
25128
25497
  const first = idFirstSeenAt.get(id);
25129
25498
  if (first) {
25130
25499
  const kindLabel = kind === first.kind ? kind : "process";
25131
25500
  ctx.addIssue({
25132
25501
  code: "custom",
25133
- path: path49,
25134
- message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(path49)} collides with ${first.kind} at ${z4.core.toDotPath(first.path)}.`,
25502
+ path: path50,
25503
+ message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(path50)} collides with ${first.kind} at ${z4.core.toDotPath(first.path)}.`,
25135
25504
  params: {
25136
25505
  code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
25137
25506
  }
25138
25507
  });
25139
25508
  } else {
25140
- idFirstSeenAt.set(id, { kind, path: path49 });
25509
+ idFirstSeenAt.set(id, { kind, path: path50 });
25141
25510
  }
25142
25511
  };
25143
25512
  process2.stages.forEach((stage, stageIndex) => {
@@ -25184,13 +25553,13 @@ function lintProcess(artifact, inventory, predicateNames, options = {}) {
25184
25553
  const internalCommands = process2.internalCommands ?? [];
25185
25554
  const internalSet = new Set(internalCommands);
25186
25555
  const boundCommandPaths = /* @__PURE__ */ new Map();
25187
- const checkPredicate = (predicate, path49) => {
25556
+ const checkPredicate = (predicate, path50) => {
25188
25557
  if (!predicateSet.has(predicate)) {
25189
25558
  diagnostics.push({
25190
25559
  severity: "error",
25191
25560
  code: "UNKNOWN_PREDICATE",
25192
- message: `Predicate "${predicate}" referenced at ${path49} is not in the predicate registry.`,
25193
- path: path49
25561
+ message: `Predicate "${predicate}" referenced at ${path50} is not in the predicate registry.`,
25562
+ path: path50
25194
25563
  });
25195
25564
  }
25196
25565
  };
@@ -25250,19 +25619,19 @@ function lintProcess(artifact, inventory, predicateNames, options = {}) {
25250
25619
  process2.outcomes.forEach((outcome, outcomeIndex) => {
25251
25620
  const whenPath = `process.outcomes[${outcomeIndex}].when`;
25252
25621
  outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
25253
- const path49 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
25622
+ const path50 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
25254
25623
  if (trigger.kind === "predicate") {
25255
- checkPredicate(trigger.name, `${path49}.predicate`);
25624
+ checkPredicate(trigger.name, `${path50}.predicate`);
25256
25625
  } else if (trigger.kind === "command") {
25257
25626
  if (!boundCommandPaths.has(trigger.name)) {
25258
- boundCommandPaths.set(trigger.name, `${path49}.command`);
25627
+ boundCommandPaths.set(trigger.name, `${path50}.command`);
25259
25628
  }
25260
25629
  if (!commandNames.has(trigger.name)) {
25261
25630
  diagnostics.push({
25262
25631
  severity: "error",
25263
25632
  code: "BOUND_COMMAND_NOT_IN_INVENTORY",
25264
- message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path49}.command) does not exist in the inventory.`,
25265
- path: `${path49}.command`
25633
+ message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path50}.command) does not exist in the inventory.`,
25634
+ path: `${path50}.command`
25266
25635
  });
25267
25636
  }
25268
25637
  }
@@ -25374,10 +25743,10 @@ var init_src4 = __esm({
25374
25743
  });
25375
25744
 
25376
25745
  // packages/mesh-cli/src/commands/workflow.ts
25377
- import * as fs41 from "fs";
25378
- import * as path48 from "path";
25746
+ import * as fs42 from "fs";
25747
+ import * as path49 from "path";
25379
25748
  import { createRequire as createRequire2 } from "module";
25380
- import { execFileSync as execFileSync33 } from "child_process";
25749
+ import { execFileSync as execFileSync34 } from "child_process";
25381
25750
  function resolveExtractorPath() {
25382
25751
  try {
25383
25752
  const require2 = createRequire2(import.meta.url);
@@ -25391,7 +25760,7 @@ function runExtraction(targetPath, extractorPath, explicitProcessPath) {
25391
25760
  let explicitProcessArtifact;
25392
25761
  if (explicitProcessPath) {
25393
25762
  try {
25394
- explicitProcessArtifact = JSON.parse(fs41.readFileSync(explicitProcessPath, "utf-8"));
25763
+ explicitProcessArtifact = JSON.parse(fs42.readFileSync(explicitProcessPath, "utf-8"));
25395
25764
  } catch (err) {
25396
25765
  logError(
25397
25766
  `Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
@@ -25432,7 +25801,7 @@ for (const file of files) {
25432
25801
  process.stdout.write(JSON.stringify(results));
25433
25802
  `;
25434
25803
  try {
25435
- const result = execFileSync33("npx", ["tsx", "--eval", script], {
25804
+ const result = execFileSync34("npx", ["tsx", "--eval", script], {
25436
25805
  encoding: "utf-8",
25437
25806
  stdio: ["pipe", "pipe", "inherit"],
25438
25807
  maxBuffer: 10 * 1024 * 1024
@@ -25474,7 +25843,7 @@ for (const file of files) {
25474
25843
  process.stdout.write(JSON.stringify(results));
25475
25844
  `;
25476
25845
  try {
25477
- const result = execFileSync33("npx", ["tsx", "--eval", script], {
25846
+ const result = execFileSync34("npx", ["tsx", "--eval", script], {
25478
25847
  encoding: "utf-8",
25479
25848
  stdio: ["pipe", "pipe", "inherit"],
25480
25849
  maxBuffer: 10 * 1024 * 1024
@@ -25491,7 +25860,7 @@ function runLintExtraction(targetPath, extractorPath, explicitProcessPath) {
25491
25860
  let explicitProcessArtifact;
25492
25861
  if (explicitProcessPath) {
25493
25862
  try {
25494
- explicitProcessArtifact = JSON.parse(fs41.readFileSync(explicitProcessPath, "utf-8"));
25863
+ explicitProcessArtifact = JSON.parse(fs42.readFileSync(explicitProcessPath, "utf-8"));
25495
25864
  } catch (err) {
25496
25865
  logError(
25497
25866
  `Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
@@ -25529,7 +25898,7 @@ for (const file of files) {
25529
25898
  process.stdout.write(JSON.stringify(results));
25530
25899
  `;
25531
25900
  try {
25532
- const result = execFileSync33("npx", ["tsx", "--eval", script], {
25901
+ const result = execFileSync34("npx", ["tsx", "--eval", script], {
25533
25902
  encoding: "utf-8",
25534
25903
  stdio: ["pipe", "pipe", "inherit"],
25535
25904
  maxBuffer: 10 * 1024 * 1024
@@ -25599,16 +25968,16 @@ function registerWorkflowCommands(program2) {
25599
25968
  ).action(
25600
25969
  async (targetPath, opts) => {
25601
25970
  try {
25602
- const resolvedPath = path48.resolve(targetPath);
25603
- if (!fs41.existsSync(resolvedPath)) {
25971
+ const resolvedPath = path49.resolve(targetPath);
25972
+ if (!fs42.existsSync(resolvedPath)) {
25604
25973
  logError(`Path does not exist: ${resolvedPath}`);
25605
25974
  process.exitCode = 1;
25606
25975
  return;
25607
25976
  }
25608
25977
  let resolvedProcessPath;
25609
25978
  if (opts.process) {
25610
- resolvedProcessPath = path48.resolve(opts.process);
25611
- if (!fs41.existsSync(resolvedProcessPath)) {
25979
+ resolvedProcessPath = path49.resolve(opts.process);
25980
+ if (!fs42.existsSync(resolvedProcessPath)) {
25612
25981
  logError(`--process path does not exist: ${resolvedProcessPath}`);
25613
25982
  process.exitCode = 1;
25614
25983
  return;
@@ -25657,8 +26026,8 @@ function registerWorkflowCommands(program2) {
25657
26026
  "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."
25658
26027
  ).action(async (targetPath) => {
25659
26028
  try {
25660
- const resolvedPath = path48.resolve(targetPath);
25661
- if (!fs41.existsSync(resolvedPath)) {
26029
+ const resolvedPath = path49.resolve(targetPath);
26030
+ if (!fs42.existsSync(resolvedPath)) {
25662
26031
  logError(`Path does not exist: ${resolvedPath}`);
25663
26032
  process.exitCode = 1;
25664
26033
  return;
@@ -25704,16 +26073,16 @@ function registerWorkflowCommands(program2) {
25704
26073
  "Comma-separated named-selector registry (enables UNKNOWN_SELECTOR checks)"
25705
26074
  ).action(async (targetPath, opts) => {
25706
26075
  try {
25707
- const resolvedPath = path48.resolve(targetPath);
25708
- if (!fs41.existsSync(resolvedPath)) {
26076
+ const resolvedPath = path49.resolve(targetPath);
26077
+ if (!fs42.existsSync(resolvedPath)) {
25709
26078
  logError(`Path does not exist: ${resolvedPath}`);
25710
26079
  process.exitCode = 1;
25711
26080
  return;
25712
26081
  }
25713
26082
  let resolvedProcessPath;
25714
26083
  if (opts.process) {
25715
- resolvedProcessPath = path48.resolve(opts.process);
25716
- if (!fs41.existsSync(resolvedProcessPath)) {
26084
+ resolvedProcessPath = path49.resolve(opts.process);
26085
+ if (!fs42.existsSync(resolvedProcessPath)) {
25717
26086
  logError(`--process path does not exist: ${resolvedProcessPath}`);
25718
26087
  process.exitCode = 1;
25719
26088
  return;
@@ -25967,7 +26336,7 @@ var init_program = __esm({
25967
26336
  init_vpn2();
25968
26337
  init_workflow();
25969
26338
  CLI_NAME = "mesh";
25970
- CLI_DESCRIPTION = "CLI for Mesh Lab development utilities";
26339
+ CLI_DESCRIPTION = "Build, run and deploy apps on a Mesh platform";
25971
26340
  }
25972
26341
  });
25973
26342