@mesh-tech/mesh-cli 0.12.7 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +227 -0
  2. package/assets/app-skill/SKILL.md +59 -0
  3. package/dist/bin/mesh.js +579 -140
  4. package/dist/bin/mesh.js.map +4 -4
  5. package/dist/build-info.json +2 -2
  6. package/dist/src/commands/create-app.d.ts +37 -0
  7. package/dist/src/commands/create-app.d.ts.map +1 -1
  8. package/dist/src/commands/create-app.js +141 -26
  9. package/dist/src/commands/create-app.js.map +1 -1
  10. package/dist/src/commands/secrets/index.d.ts +1 -0
  11. package/dist/src/commands/secrets/index.d.ts.map +1 -1
  12. package/dist/src/commands/secrets/index.js +12 -0
  13. package/dist/src/commands/secrets/index.js.map +1 -1
  14. package/dist/src/commands/secrets/reindex.d.ts +48 -0
  15. package/dist/src/commands/secrets/reindex.d.ts.map +1 -0
  16. package/dist/src/commands/secrets/reindex.js +157 -0
  17. package/dist/src/commands/secrets/reindex.js.map +1 -0
  18. package/dist/src/commands/secrets/set.d.ts +63 -0
  19. package/dist/src/commands/secrets/set.d.ts.map +1 -1
  20. package/dist/src/commands/secrets/set.js +78 -3
  21. package/dist/src/commands/secrets/set.js.map +1 -1
  22. package/dist/src/commands/skills.d.ts +48 -8
  23. package/dist/src/commands/skills.d.ts.map +1 -1
  24. package/dist/src/commands/skills.js +204 -12
  25. package/dist/src/commands/skills.js.map +1 -1
  26. package/dist/src/utils/scaffold-versions.d.ts +57 -0
  27. package/dist/src/utils/scaffold-versions.d.ts.map +1 -0
  28. package/dist/src/utils/scaffold-versions.js +75 -0
  29. package/dist/src/utils/scaffold-versions.js.map +1 -0
  30. package/fragments/agents/AGENTS.md.hbs +4 -3
  31. package/fragments/base/package.json.hbs +4 -5
  32. package/fragments/service/api/package.json.hbs +5 -5
  33. package/fragments/temporal/worker/package.json.hbs +5 -5
  34. package/package.json +3 -2
  35. package/skills/core/SKILL.md +15 -3
  36. package/templates/apps-repo/package.json.hbs +2 -1
  37. package/templates/apps-repo/pnpm-workspace.yaml +9 -1
package/dist/bin/mesh.js CHANGED
@@ -890,8 +890,8 @@ ${body}${MANAGED_END}
890
890
  return existing.slice(0, startIdx) + section + existing.slice(tail);
891
891
  }
892
892
  if (existing.length === 0) return section;
893
- const sep4 = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
894
- return existing + sep4 + section;
893
+ const sep5 = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
894
+ return existing + sep5 + section;
895
895
  }
896
896
  function newStsClient() {
897
897
  return new STSClient({ region: DEFAULT_REGION });
@@ -1859,8 +1859,8 @@ function resolveHubPlatformName(platform) {
1859
1859
  return platform?.name ?? "mesh";
1860
1860
  }
1861
1861
  async function ssmGetParameter(name) {
1862
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
1863
- const ssm = new SSMClient4({ region: process.env.AWS_REGION || "us-east-2" });
1862
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
1863
+ const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
1864
1864
  const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
1865
1865
  return resp.Parameter?.Value;
1866
1866
  }
@@ -1901,8 +1901,8 @@ var init_kubeconfig = __esm({
1901
1901
  // libs/mesh-cli/src/utils/temporal-auth.ts
1902
1902
  import { execFileSync as execFileSync3 } from "node:child_process";
1903
1903
  async function resolveTemporalAuth(tenant, env, platformName = tenant) {
1904
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
1905
- const ssm = new SSMClient4({ region: process.env.AWS_REGION || "us-east-2" });
1904
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
1905
+ const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
1906
1906
  const results = {};
1907
1907
  async function trySSM(name) {
1908
1908
  try {
@@ -2297,8 +2297,8 @@ function buildFabricCheckPayload(targetBytes = 8e3) {
2297
2297
  return JSON.stringify({ ...base, pad: "x".repeat(Math.max(0, targetBytes - overhead)) });
2298
2298
  }
2299
2299
  async function verifyFabric() {
2300
- const { SSMClient: SSMClient4, PutParameterCommand, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
2301
- const ssm = new SSMClient4(AWS_CONFIG);
2300
+ const { SSMClient: SSMClient5, PutParameterCommand, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
2301
+ const ssm = new SSMClient5(AWS_CONFIG);
2302
2302
  const payload = buildFabricCheckPayload();
2303
2303
  const mainParam = FABRIC_CHECK_PATH;
2304
2304
  const childParams = [`${FABRIC_CHECK_PATH}/vpc`, `${FABRIC_CHECK_PATH}/eks`];
@@ -2350,8 +2350,8 @@ async function verifyFabric() {
2350
2350
  }
2351
2351
  }
2352
2352
  async function registerTenantEnv(tenant, opts = {}) {
2353
- const { SSMClient: SSMClient4, PutParameterCommand } = await import("@aws-sdk/client-ssm");
2354
- const ssm = new SSMClient4(AWS_CONFIG);
2353
+ const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
2354
+ const ssm = new SSMClient5(AWS_CONFIG);
2355
2355
  await ssm.send(
2356
2356
  new PutParameterCommand({
2357
2357
  Name: `/mesh-platform/${tenant}`,
@@ -2379,8 +2379,8 @@ async function registerTenantEnv(tenant, opts = {}) {
2379
2379
  );
2380
2380
  }
2381
2381
  async function seedLocalPlatform() {
2382
- const { SSMClient: SSMClient4, PutParameterCommand } = await import("@aws-sdk/client-ssm");
2383
- const ssm = new SSMClient4(AWS_CONFIG);
2382
+ const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
2383
+ const ssm = new SSMClient5(AWS_CONFIG);
2384
2384
  await ssm.send(
2385
2385
  new PutParameterCommand({
2386
2386
  Name: APP_TENANTS_PARAM,
@@ -2434,14 +2434,14 @@ var init_seed = __esm({
2434
2434
  // libs/mesh-cli/src/commands/local/helpers.ts
2435
2435
  import * as net2 from "net";
2436
2436
  async function upsertLocalSecret(secretId, value, client) {
2437
- const { SecretsManagerClient: SecretsManagerClient8, CreateSecretCommand: CreateSecretCommand3, PutSecretValueCommand: PutSecretValueCommand3 } = await import("@aws-sdk/client-secrets-manager");
2438
- const sm = client ?? new SecretsManagerClient8(LOCAL_AWS_CONFIG);
2437
+ const { SecretsManagerClient: SecretsManagerClient9, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
2438
+ const sm = client ?? new SecretsManagerClient9(LOCAL_AWS_CONFIG);
2439
2439
  const secretString = JSON.stringify(value);
2440
2440
  try {
2441
- await sm.send(new CreateSecretCommand3({ Name: secretId, SecretString: secretString }));
2441
+ await sm.send(new CreateSecretCommand4({ Name: secretId, SecretString: secretString }));
2442
2442
  } catch (err) {
2443
2443
  if (err?.name === "ResourceExistsException") {
2444
- await sm.send(new PutSecretValueCommand3({ SecretId: secretId, SecretString: secretString }));
2444
+ await sm.send(new PutSecretValueCommand4({ SecretId: secretId, SecretString: secretString }));
2445
2445
  } else {
2446
2446
  throw err;
2447
2447
  }
@@ -3158,12 +3158,12 @@ async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
3158
3158
  const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
3159
3159
  const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE ? process.env.MESH_AWS_PROFILE ?? profile : void 0;
3160
3160
  try {
3161
- const { SecretsManagerClient: SecretsManagerClient8, GetSecretValueCommand: GetSecretValueCommand8 } = await import("@aws-sdk/client-secrets-manager");
3161
+ const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
3162
3162
  if (fallbackProfile) process.env.AWS_PROFILE = fallbackProfile;
3163
- const sm = new SecretsManagerClient8({ region });
3163
+ const sm = new SecretsManagerClient9({ region });
3164
3164
  let res;
3165
3165
  try {
3166
- res = await sm.send(new GetSecretValueCommand8({ SecretId: secretId }));
3166
+ res = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
3167
3167
  } finally {
3168
3168
  if (fallbackProfile) delete process.env.AWS_PROFILE;
3169
3169
  }
@@ -3277,8 +3277,8 @@ async function seedLocalMock(args) {
3277
3277
  `${secretName}/.config`,
3278
3278
  Object.fromEntries(Object.entries(value).filter(([key]) => !SECRET_KEY_RE.test(key)))
3279
3279
  );
3280
- const { SSMClient: SSMClient4, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3281
- const ssm = new SSMClient4(LOCAL_AWS_CONFIG);
3280
+ const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3281
+ const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
3282
3282
  const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/external-services/${decl.external}`;
3283
3283
  await ssm.send(
3284
3284
  new PutParameterCommand({
@@ -3720,8 +3720,8 @@ async function seedHubAuth(pat) {
3720
3720
  return config;
3721
3721
  }
3722
3722
  async function writeRegistryParams(cliClientId, awsConfig) {
3723
- const { SSMClient: SSMClient4, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3724
- const ssm = new SSMClient4(awsConfig);
3723
+ const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3724
+ const ssm = new SSMClient5(awsConfig);
3725
3725
  await ssm.send(
3726
3726
  new PutParameterCommand({
3727
3727
  Name: ZITADEL_SSM_PARAM,
@@ -3911,9 +3911,9 @@ async function ensureM2mCaller(pat, tenant, app, orgId, projectId, roles) {
3911
3911
  return false;
3912
3912
  }
3913
3913
  async function authSecretExists(tenant, app, service) {
3914
- const { SecretsManagerClient: SecretsManagerClient8, GetSecretValueCommand: GetSecretValueCommand8 } = await import("@aws-sdk/client-secrets-manager");
3915
- const sm = new SecretsManagerClient8(LOCAL_AWS_CONFIG);
3916
- return sm.send(new GetSecretValueCommand8({ SecretId: authSecretPath(tenant, app, service) })).then(
3914
+ const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
3915
+ const sm = new SecretsManagerClient9(LOCAL_AWS_CONFIG);
3916
+ return sm.send(new GetSecretValueCommand9({ SecretId: authSecretPath(tenant, app, service) })).then(
3917
3917
  () => true,
3918
3918
  () => false
3919
3919
  );
@@ -3924,8 +3924,8 @@ async function writeAuthSecret(tenant, app, service, value) {
3924
3924
  logSuccess(`Stored service credentials \u2192 ${name}`);
3925
3925
  }
3926
3926
  async function registerLocalApp(args) {
3927
- const { SSMClient: SSMClient4, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3928
- const ssm = new SSMClient4(LOCAL_AWS_CONFIG);
3927
+ const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3928
+ const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
3929
3929
  const put2 = (name, value, description) => ssm.send(
3930
3930
  new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: JSON.stringify(value), Description: description })
3931
3931
  );
@@ -3975,8 +3975,8 @@ async function registerLocalApp(args) {
3975
3975
  }
3976
3976
  async function reconcileRegistryFromZitadel() {
3977
3977
  const pat = readSeederPat();
3978
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3979
- const ssm = new SSMClient4(LOCAL_AWS_CONFIG);
3978
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
3979
+ const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
3980
3980
  const orgs = await api(pat, "POST", "/admin/v1/orgs/_search", { query: { limit: 200 } });
3981
3981
  const tenants = [];
3982
3982
  let apps = 0;
@@ -7297,8 +7297,8 @@ function registerDevCommand(program2) {
7297
7297
  });
7298
7298
  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(
7299
7299
  async (name, opts) => {
7300
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3 } = await import("@aws-sdk/client-ssm");
7301
- const ssm = new SSMClient4({ region: opts.region });
7300
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3 } = await import("@aws-sdk/client-ssm");
7301
+ const ssm = new SSMClient5({ region: opts.region });
7302
7302
  const basePath = `/mesh-platform/${opts.tenant}/${opts.env}/temporal/test-users`;
7303
7303
  if (!name) {
7304
7304
  try {
@@ -9291,9 +9291,9 @@ function parseTenantEnv(context) {
9291
9291
  }
9292
9292
  async function getRegistrySsmExport(tenant, env) {
9293
9293
  try {
9294
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
9294
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
9295
9295
  const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? CA_REGION;
9296
- const ssm = new SSMClient4({ region });
9296
+ const ssm = new SSMClient5({ region });
9297
9297
  const ssmPath = `/mesh-platform/${tenant}/${env}/registry`;
9298
9298
  const response = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
9299
9299
  if (!response.Parameter?.Value) return null;
@@ -9883,9 +9883,9 @@ async function discoverConfigFromSsm(context) {
9883
9883
  const ssmPath = `/mesh-platform/${tenant}/${env}/platform/zitadel`;
9884
9884
  logInfo(`Attempting SSM discovery from ${ssmPath}...`);
9885
9885
  try {
9886
- const { SSMClient: SSMClient4, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
9886
+ const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
9887
9887
  const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
9888
- const ssm = new SSMClient4({ region });
9888
+ const ssm = new SSMClient5({ region });
9889
9889
  const resp = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
9890
9890
  const raw = resp.Parameter?.Value;
9891
9891
  if (!raw) {
@@ -11415,6 +11415,63 @@ ${raw}`;
11415
11415
  ${MANAGED_MARKER}
11416
11416
  ${raw.slice(frontmatter.length)}`;
11417
11417
  }
11418
+ function meshTechScopeDirs(root, maxDepth = 6) {
11419
+ const scopes = [];
11420
+ const walk = (dir, depth) => {
11421
+ if (depth > maxDepth) return;
11422
+ let entries;
11423
+ try {
11424
+ entries = fs21.readdirSync(dir, { withFileTypes: true });
11425
+ } catch {
11426
+ return;
11427
+ }
11428
+ for (const entry of entries) {
11429
+ if (!entry.isDirectory()) continue;
11430
+ if (entry.name === "node_modules") {
11431
+ const scope = path23.join(dir, entry.name, "@mesh-tech");
11432
+ if (fs21.existsSync(scope)) scopes.push(scope);
11433
+ continue;
11434
+ }
11435
+ if (entry.name.startsWith(".")) continue;
11436
+ walk(path23.join(dir, entry.name), depth + 1);
11437
+ }
11438
+ };
11439
+ walk(root, 0);
11440
+ return scopes;
11441
+ }
11442
+ function listPackageSkills(root) {
11443
+ const byName = /* @__PURE__ */ new Map();
11444
+ for (const scopeDir of meshTechScopeDirs(root)) {
11445
+ for (const pkg of fs21.readdirSync(scopeDir, { withFileTypes: true })) {
11446
+ if (!pkg.isDirectory() && !pkg.isSymbolicLink()) continue;
11447
+ if (pkg.name === BASE_SKILL_PACKAGE) continue;
11448
+ const skillsDir = path23.join(scopeDir, pkg.name, "skills");
11449
+ if (!fs21.existsSync(skillsDir)) continue;
11450
+ for (const domain of fs21.readdirSync(skillsDir, { withFileTypes: true })) {
11451
+ const source = path23.join(skillsDir, domain.name, "SKILL.md");
11452
+ if (!fs21.existsSync(source)) continue;
11453
+ const name = `mesh-${pkg.name}-${domain.name}`;
11454
+ if (!byName.has(name)) byName.set(name, { name, dir: path23.join(skillsDir, domain.name), source });
11455
+ }
11456
+ }
11457
+ }
11458
+ return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
11459
+ }
11460
+ function copySkillExtras(sourceDir, targetDir) {
11461
+ for (const entry of fs21.readdirSync(sourceDir, { withFileTypes: true })) {
11462
+ if (entry.name === "SKILL.md") continue;
11463
+ fs21.cpSync(path23.join(sourceDir, entry.name), path23.join(targetDir, entry.name), { recursive: true });
11464
+ }
11465
+ }
11466
+ function seedAppSkill(root, appName, appRelPath) {
11467
+ const target = path23.join(root, ".claude", "skills", appName, "SKILL.md");
11468
+ if (fs21.existsSync(target)) return null;
11469
+ const template = fs21.readFileSync(cliAsset("assets", "app-skill", "SKILL.md"), "utf-8");
11470
+ const body = template.replaceAll("__APP_NAME__", appName).replaceAll("__APP_PATH__", appRelPath || ".");
11471
+ fs21.mkdirSync(path23.dirname(target), { recursive: true });
11472
+ fs21.writeFileSync(target, body);
11473
+ return path23.relative(root, target);
11474
+ }
11418
11475
  function planSync(root) {
11419
11476
  const items = [];
11420
11477
  for (const skill of listBaseSkills()) {
@@ -11441,6 +11498,43 @@ function planSync(root) {
11441
11498
  }
11442
11499
  }
11443
11500
  }
11501
+ const packageSkills = listPackageSkills(root);
11502
+ for (const skill of packageSkills) {
11503
+ const targetDir = path23.join(root, ".claude", "skills", skill.name);
11504
+ const target = path23.join(targetDir, "SKILL.md");
11505
+ const desired = renderManagedSkill(skill.source);
11506
+ const label = `.claude/skills/${skill.name}/SKILL.md (platform skill)`;
11507
+ if (!fs21.existsSync(target)) {
11508
+ items.push({
11509
+ label,
11510
+ state: "write",
11511
+ apply: () => {
11512
+ fs21.mkdirSync(targetDir, { recursive: true });
11513
+ fs21.writeFileSync(target, desired);
11514
+ copySkillExtras(skill.dir, targetDir);
11515
+ }
11516
+ });
11517
+ } else {
11518
+ const current = fs21.readFileSync(target, "utf-8");
11519
+ if (current === desired) {
11520
+ items.push({ label, state: "ok" });
11521
+ } else if (current.includes(MANAGED_MARKER)) {
11522
+ items.push({
11523
+ label,
11524
+ state: "write",
11525
+ apply: () => {
11526
+ fs21.writeFileSync(target, desired);
11527
+ copySkillExtras(skill.dir, targetDir);
11528
+ }
11529
+ });
11530
+ } else {
11531
+ items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
11532
+ }
11533
+ }
11534
+ }
11535
+ if (packageSkills.length === 0 && meshTechScopeDirs(root).length === 0) {
11536
+ logInfo("No @mesh-tech packages installed yet \u2014 run `pnpm install`, then `mesh skills sync` again for the platform skills.");
11537
+ }
11444
11538
  const hookSource = cliAsset("assets", "intent", "intent-claude-gate.mjs");
11445
11539
  const hookTarget = path23.join(root, HOOK_RELATIVE);
11446
11540
  const hookDesired = fs21.readFileSync(hookSource, "utf-8");
@@ -11455,6 +11549,38 @@ function planSync(root) {
11455
11549
  }
11456
11550
  }
11457
11551
  );
11552
+ const rootPkgPath = path23.join(root, "package.json");
11553
+ if (fs21.existsSync(rootPkgPath)) {
11554
+ let rootPkg = null;
11555
+ try {
11556
+ rootPkg = JSON.parse(fs21.readFileSync(rootPkgPath, "utf-8"));
11557
+ } catch {
11558
+ rootPkg = null;
11559
+ }
11560
+ const label = "package.json (@tanstack/intent devDependency)";
11561
+ if (!rootPkg) {
11562
+ items.push({ label: `${label} \u2014 package.json is unparseable, leaving as-is`, state: "skip" });
11563
+ } else if (rootPkg.devDependencies?.["@tanstack/intent"] || rootPkg.dependencies?.["@tanstack/intent"]) {
11564
+ items.push({ label, state: "ok" });
11565
+ } else {
11566
+ items.push({
11567
+ label,
11568
+ // Writing the dependency does not install it. Bare `mesh skills sync`
11569
+ // in an already-installed repo leaves `pnpm exec intent` unresolvable
11570
+ // until the next install, so say so rather than reporting success.
11571
+ remediation: "Added @tanstack/intent \u2014 run `pnpm install` so `pnpm exec intent` resolves.",
11572
+ state: "write",
11573
+ apply: () => {
11574
+ rootPkg.devDependencies = rootPkg.devDependencies ?? {};
11575
+ rootPkg.devDependencies["@tanstack/intent"] = INTENT_RANGE;
11576
+ rootPkg.devDependencies = Object.fromEntries(
11577
+ Object.entries(rootPkg.devDependencies).sort(([a], [b]) => a.localeCompare(b))
11578
+ );
11579
+ fs21.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + "\n");
11580
+ }
11581
+ });
11582
+ }
11583
+ }
11458
11584
  const settingsPath = path23.join(root, ".claude", "settings.json");
11459
11585
  let settings = {};
11460
11586
  try {
@@ -11523,6 +11649,7 @@ function syncSkills(root, opts = {}) {
11523
11649
  } else {
11524
11650
  item.apply?.();
11525
11651
  logSuccess(`synced: ${item.label}`);
11652
+ if (item.remediation) logInfo(item.remediation);
11526
11653
  }
11527
11654
  }
11528
11655
  if (!dirty) {
@@ -11533,7 +11660,7 @@ function syncSkills(root, opts = {}) {
11533
11660
  function registerSkillsCommands(program2) {
11534
11661
  const skills = program2.command("skills").description("Agent-skill distribution (base skills + Intent discovery)");
11535
11662
  skills.command("sync").description(
11536
- "Install the base building-with-Mesh skills into .claude/skills/ and wire TanStack-Intent discovery (hook + settings + AGENTS.md fence) for package skills"
11663
+ "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)"
11537
11664
  ).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) => {
11538
11665
  const root = path23.resolve(opts.root ?? resolveTargetRoot());
11539
11666
  const ok = syncSkills(root, { check: opts.check });
@@ -11544,7 +11671,7 @@ function registerSkillsCommands(program2) {
11544
11671
  }
11545
11672
  });
11546
11673
  }
11547
- var MANAGED_MARKER, FENCE_START, HOOK_RELATIVE;
11674
+ var MANAGED_MARKER, FENCE_START, HOOK_RELATIVE, INTENT_RANGE, BASE_SKILL_PACKAGE;
11548
11675
  var init_skills = __esm({
11549
11676
  "libs/mesh-cli/src/commands/skills.ts"() {
11550
11677
  "use strict";
@@ -11554,26 +11681,78 @@ var init_skills = __esm({
11554
11681
  MANAGED_MARKER = "<!-- managed-by: mesh skills sync \u2014 edits are overwritten; copy content elsewhere to customize -->";
11555
11682
  FENCE_START = "<!-- intent-skills:start -->";
11556
11683
  HOOK_RELATIVE = path23.join(".intent", "hooks", "intent-claude-gate.mjs");
11684
+ INTENT_RANGE = "^0.3.6";
11685
+ BASE_SKILL_PACKAGE = "mesh-cli";
11686
+ }
11687
+ });
11688
+
11689
+ // libs/mesh-cli/src/utils/scaffold-versions.ts
11690
+ import { execFileSync as execFileSync18 } from "child_process";
11691
+ function isPrerelease(range) {
11692
+ return /[-+]/.test(range.replace(/^\^/, ""));
11693
+ }
11694
+ function toCaretRange(version) {
11695
+ const trimmed = version.trim();
11696
+ return /^\d+\.\d+\.\d+(?:[-+].*)?$/.test(trimmed) ? `^${trimmed}` : null;
11697
+ }
11698
+ function resolvePublishedRange(pkg, cwd) {
11699
+ try {
11700
+ const out = execFileSync18("npm", ["view", pkg, "version"], {
11701
+ cwd,
11702
+ encoding: "utf-8",
11703
+ timeout: 15e3,
11704
+ stdio: ["ignore", "pipe", "ignore"]
11705
+ });
11706
+ const range = toCaretRange(out);
11707
+ if (range && !isPrerelease(range)) return { range, resolved: true };
11708
+ } catch {
11709
+ }
11710
+ return { range: SCAFFOLD_PACKAGE_LINES[pkg], resolved: false };
11711
+ }
11712
+ var SCAFFOLD_PACKAGE_LINES, SCAFFOLD_TOOLCHAIN;
11713
+ var init_scaffold_versions = __esm({
11714
+ "libs/mesh-cli/src/utils/scaffold-versions.ts"() {
11715
+ "use strict";
11716
+ SCAFFOLD_PACKAGE_LINES = {
11717
+ "@mesh-tech/app-kit": "^1.45.0",
11718
+ "@mesh-tech/esm-bundle": "^0.1.1"
11719
+ };
11720
+ SCAFFOLD_TOOLCHAIN = {
11721
+ "@types/node": "^24.2.1",
11722
+ tsx: "^4.20.4",
11723
+ typescript: "^5.9.2"
11724
+ };
11557
11725
  }
11558
11726
  });
11559
11727
 
11560
11728
  // libs/mesh-cli/src/commands/create-app.ts
11561
11729
  var create_app_exports = {};
11562
11730
  __export(create_app_exports, {
11731
+ PLATFORM_MONOREPO_NAME: () => PLATFORM_MONOREPO_NAME,
11563
11732
  bootstrapAppsRepo: () => bootstrapAppsRepo,
11564
11733
  copyTemplate: () => copyTemplate,
11734
+ ensureWorkspaceGlobs: () => ensureWorkspaceGlobs,
11735
+ isInsidePlatformMonorepo: () => isInsidePlatformMonorepo,
11565
11736
  registerCreateAppCommand: () => registerCreateAppCommand,
11566
- shouldBootstrapAppsRepo: () => shouldBootstrapAppsRepo
11737
+ shouldBootstrapAppsRepo: () => shouldBootstrapAppsRepo,
11738
+ workspaceGlobForApp: () => workspaceGlobForApp
11567
11739
  });
11568
11740
  import * as fs22 from "fs";
11569
11741
  import * as os8 from "os";
11570
11742
  import * as path24 from "path";
11571
11743
  import { fileURLToPath as fileURLToPath2 } from "url";
11572
11744
  import Handlebars from "handlebars";
11573
- function isInsideMonorepo(dir) {
11745
+ import { parse as parseYaml2 } from "yaml";
11746
+ function isInsidePlatformMonorepo(dir) {
11574
11747
  let cur = path24.resolve(dir);
11575
11748
  while (cur !== path24.dirname(cur)) {
11576
- if (fs22.existsSync(path24.join(cur, "pnpm-workspace.yaml"))) return true;
11749
+ if (fs22.existsSync(path24.join(cur, "pnpm-workspace.yaml"))) {
11750
+ try {
11751
+ const pkg = JSON.parse(fs22.readFileSync(path24.join(cur, "package.json"), "utf-8"));
11752
+ if (pkg?.name === PLATFORM_MONOREPO_NAME) return true;
11753
+ } catch {
11754
+ }
11755
+ }
11577
11756
  cur = path24.dirname(cur);
11578
11757
  }
11579
11758
  return false;
@@ -11583,7 +11762,7 @@ function resolveDeployerRoleArn(tenant, platformName, env) {
11583
11762
  return `arn:aws:iam::${account}:role/${tenant}-${env}-apps-deployer`;
11584
11763
  }
11585
11764
  function shouldBootstrapAppsRepo(cwd) {
11586
- return fs22.existsSync(path24.join(cwd, ".git")) && !isInsideMonorepo(cwd) && !fs22.existsSync(path24.join(cwd, "package.json")) && !fs22.existsSync(path24.join(cwd, "tenants")) && !fs22.existsSync(path24.join(cwd, "apps"));
11765
+ return fs22.existsSync(path24.join(cwd, ".git")) && !isInsidePlatformMonorepo(cwd) && !fs22.existsSync(path24.join(cwd, "package.json")) && !fs22.existsSync(path24.join(cwd, "tenants")) && !fs22.existsSync(path24.join(cwd, "apps"));
11587
11766
  }
11588
11767
  function bootstrapAppsRepo(cwd, tenant) {
11589
11768
  const templateDir = path24.join(packageRoot, "templates", "apps-repo");
@@ -11592,7 +11771,8 @@ function bootstrapAppsRepo(cwd, tenant) {
11592
11771
  repoName: path24.basename(cwd),
11593
11772
  tenant,
11594
11773
  tenantTitle: tenant.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "),
11595
- registryUrl: `https://mesh-platform-${registryAccount}.d.codeartifact.us-east-2.amazonaws.com/npm/mesh-packages/`
11774
+ registryUrl: `https://mesh-platform-${registryAccount}.d.codeartifact.us-east-2.amazonaws.com/npm/mesh-packages/`,
11775
+ intentRange: INTENT_RANGE
11596
11776
  };
11597
11777
  const staging = fs22.mkdtempSync(path24.join(os8.tmpdir(), "mesh-apps-repo-"));
11598
11778
  try {
@@ -11618,6 +11798,41 @@ function bootstrapAppsRepo(cwd, tenant) {
11618
11798
  fs22.rmSync(staging, { recursive: true, force: true });
11619
11799
  }
11620
11800
  }
11801
+ function ensureWorkspaceGlobs(root, required) {
11802
+ const file = path24.join(root, "pnpm-workspace.yaml");
11803
+ if (!fs22.existsSync(file)) return [];
11804
+ const text = fs22.readFileSync(file, "utf-8");
11805
+ let declared;
11806
+ try {
11807
+ declared = parseYaml2(text)?.packages ?? [];
11808
+ } catch {
11809
+ return [];
11810
+ }
11811
+ const missing = required.filter((glob) => !declared.includes(glob));
11812
+ if (missing.length === 0) return [];
11813
+ const lines = text.split("\n");
11814
+ const start = lines.findIndex((line) => /^packages:\s*$/.test(line));
11815
+ if (start === -1) return [];
11816
+ let end = start;
11817
+ for (let i = start + 1; i < lines.length; i++) {
11818
+ const line = lines[i] ?? "";
11819
+ const isEntry = /^\s+-\s/.test(line);
11820
+ if (isEntry || /^\s*#/.test(line) || line.trim() === "") {
11821
+ if (isEntry) end = i;
11822
+ continue;
11823
+ }
11824
+ break;
11825
+ }
11826
+ const indent = (lines[end] ?? "").match(/^(\s*)-/)?.[1] ?? " ";
11827
+ lines.splice(end + 1, 0, ...missing.map((glob) => `${indent}- ${glob}`));
11828
+ fs22.writeFileSync(file, lines.join("\n"));
11829
+ return missing;
11830
+ }
11831
+ function workspaceGlobForApp(root, appDir) {
11832
+ const rel = path24.relative(root, appDir).split(path24.sep).join("/");
11833
+ if (rel === "" || rel.split("/")[0] === "..") return null;
11834
+ return `${path24.posix.dirname(rel)}/*/*`;
11835
+ }
11621
11836
  function maybeBootstrapAppsRepo(cwd, tenant, test) {
11622
11837
  if (test || !shouldBootstrapAppsRepo(cwd)) return;
11623
11838
  const created = bootstrapAppsRepo(cwd, tenant);
@@ -11858,15 +12073,22 @@ async function runLegacyTemplate(tenant, name, template, test) {
11858
12073
  fs22.rmSync(appDir, { recursive: true, force: true });
11859
12074
  process.exit(1);
11860
12075
  }
11861
- autoSyncSkills(appDir);
12076
+ autoSyncSkills(appDir, name);
11862
12077
  printLegacyNextSteps(appDir, template);
11863
12078
  }
11864
- function autoSyncSkills(appDir) {
12079
+ function autoSyncSkills(appDir, appName) {
12080
+ const root = resolveTargetRoot(appDir);
11865
12081
  try {
11866
- syncSkills(resolveTargetRoot(appDir));
12082
+ syncSkills(root);
11867
12083
  } catch (err) {
11868
12084
  logWarn(`Agent-skill sync skipped: ${err instanceof Error ? err.message : err} \u2014 run: mesh skills sync`);
11869
12085
  }
12086
+ try {
12087
+ const seeded = seedAppSkill(root, appName, path24.relative(root, appDir));
12088
+ if (seeded) logSuccess(`seeded: ${seeded} (yours to grow \u2014 not managed by mesh skills sync)`);
12089
+ } catch (err) {
12090
+ logWarn(`App skill stub skipped: ${err instanceof Error ? err.message : err}`);
12091
+ }
11870
12092
  }
11871
12093
  async function runComposable(tenant, name, primitives, test) {
11872
12094
  logInfo(`Creating app '${name}' for tenant '${tenant}'...`);
@@ -11883,6 +12105,15 @@ async function runComposable(tenant, name, primitives, test) {
11883
12105
  const region = "us-east-2";
11884
12106
  const platformName = "mesh";
11885
12107
  const platformEnv = "dev";
12108
+ const workspaceDeps = isInsidePlatformMonorepo(appDir);
12109
+ const repoRoot2 = resolveTargetRoot(process.cwd());
12110
+ const mesh = workspaceDeps ? { range: "", resolved: true } : resolvePublishedRange("@mesh-tech/app-kit", repoRoot2);
12111
+ const esmBundle = workspaceDeps ? { range: "", resolved: true } : resolvePublishedRange("@mesh-tech/esm-bundle", repoRoot2);
12112
+ if (!workspaceDeps) {
12113
+ logInfo(
12114
+ 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`
12115
+ );
12116
+ }
11886
12117
  const context = {
11887
12118
  name,
11888
12119
  tenant,
@@ -11891,9 +12122,12 @@ async function runComposable(tenant, name, primitives, test) {
11891
12122
  temporal: primitives.includes("temporal"),
11892
12123
  bucket: primitives.includes("bucket"),
11893
12124
  region,
11894
- // Link local source when generating inside this monorepo; pin only for
11895
- // out-of-repo tenant repos that install `@mesh-tech/*` from the registry.
11896
- workspaceDeps: isInsideMonorepo(appDir),
12125
+ workspaceDeps,
12126
+ meshRange: mesh.range,
12127
+ esmBundleRange: esmBundle.range,
12128
+ typesNodeRange: SCAFFOLD_TOOLCHAIN["@types/node"],
12129
+ tsxRange: SCAFFOLD_TOOLCHAIN.tsx,
12130
+ typescriptRange: SCAFFOLD_TOOLCHAIN.typescript,
11897
12131
  deployerRoleArn: resolveDeployerRoleArn(tenant, platformName, platformEnv)
11898
12132
  };
11899
12133
  fs22.mkdirSync(appDir, { recursive: true });
@@ -11905,20 +12139,18 @@ async function runComposable(tenant, name, primitives, test) {
11905
12139
  fs22.rmSync(appDir, { recursive: true, force: true });
11906
12140
  process.exit(1);
11907
12141
  }
11908
- const configPath = path24.join(path24.dirname(appDir), "config.ts");
11909
- if (!fs22.existsSync(configPath)) {
11910
- logWarn(`No config.ts found at ${configPath}`);
11911
- logInfo("You may need to create a shared config.ts for tenant apps.");
12142
+ const nested = fs22.readdirSync(appDir, { withFileTypes: true }).some((entry) => entry.isDirectory() && fs22.existsSync(path24.join(appDir, entry.name, "package.json")));
12143
+ const glob = workspaceGlobForApp(repoRoot2, appDir);
12144
+ if (nested && glob) {
12145
+ const added = ensureWorkspaceGlobs(repoRoot2, [glob]);
12146
+ if (added.length > 0) {
12147
+ logInfo(`pnpm-workspace.yaml: added ${added.join(", ")} so the app's api/worker packages install`);
12148
+ }
11912
12149
  }
11913
- autoSyncSkills(appDir);
12150
+ autoSyncSkills(appDir, name);
11914
12151
  printComposableNextSteps(appDir, context);
11915
12152
  }
11916
12153
  function printLegacyNextSteps(appDir, _template) {
11917
- const configPath = path24.join(path24.dirname(appDir), "config.ts");
11918
- if (!fs22.existsSync(configPath)) {
11919
- logWarn(`No config.ts found at ${configPath}`);
11920
- logInfo("You may need to create a shared config.ts for tenant apps.");
11921
- }
11922
12154
  console.log("");
11923
12155
  logSuccess("App created successfully!");
11924
12156
  console.log("");
@@ -11955,18 +12187,21 @@ function printComposableNextSteps(appDir, context) {
11955
12187
  console.log("Next steps:");
11956
12188
  console.log(` cd ${appDir}`);
11957
12189
  console.log(" pnpm install");
12190
+ console.log(" mesh skills sync # re-run once deps are installed: picks up package skills + Intent");
12191
+ console.log(" pnpm install # again only if skills sync reports it added @tanstack/intent");
11958
12192
  console.log(" mesh stack init # personal dev stack (deploy: false)");
11959
12193
  console.log(" mesh deploy up --yes # deploy via the stack's deployer role");
11960
12194
  console.log(" mesh dev # run locally");
11961
12195
  console.log("");
11962
12196
  }
11963
- var __filename, __dirname, packageRoot, TEMPLATES, REMOVED_TEMPLATES, PRIMITIVES, HUB_ACCOUNTS, VALID_PRIMITIVES;
12197
+ var __filename, __dirname, packageRoot, TEMPLATES, REMOVED_TEMPLATES, PRIMITIVES, HUB_ACCOUNTS, PLATFORM_MONOREPO_NAME, VALID_PRIMITIVES;
11964
12198
  var init_create_app = __esm({
11965
12199
  "libs/mesh-cli/src/commands/create-app.ts"() {
11966
12200
  "use strict";
11967
12201
  init_utils();
11968
12202
  init_skills();
11969
12203
  init_stack();
12204
+ init_scaffold_versions();
11970
12205
  __filename = fileURLToPath2(import.meta.url);
11971
12206
  __dirname = path24.dirname(__filename);
11972
12207
  packageRoot = findPackageRoot(__dirname);
@@ -11989,6 +12224,7 @@ var init_create_app = __esm({
11989
12224
  HUB_ACCOUNTS = {
11990
12225
  mesh: "159923586610"
11991
12226
  };
12227
+ PLATFORM_MONOREPO_NAME = "mesh-platform";
11992
12228
  VALID_PRIMITIVES = Object.keys(PRIMITIVES);
11993
12229
  Handlebars.registerHelper("titleCase", (str) => {
11994
12230
  return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
@@ -12600,8 +12836,8 @@ var init_db = __esm({
12600
12836
 
12601
12837
  // libs/mesh-cli/src/utils/deploy-preflight.ts
12602
12838
  import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
12603
- import { dirname as dirname22, join as join25, relative as relative3 } from "node:path";
12604
- import { parse as parseYaml2 } from "yaml";
12839
+ import { dirname as dirname22, join as join25, relative as relative5 } from "node:path";
12840
+ import { parse as parseYaml3 } from "yaml";
12605
12841
  function isGated(name) {
12606
12842
  return GATED_PREFIXES.some((p) => name.startsWith(p));
12607
12843
  }
@@ -12659,14 +12895,14 @@ function checkDeployDepsFresh(appRoot) {
12659
12895
  if (!lockPath) return null;
12660
12896
  let lockDoc;
12661
12897
  try {
12662
- lockDoc = parseYaml2(readFileSync21(lockPath, "utf8"));
12898
+ lockDoc = parseYaml3(readFileSync21(lockPath, "utf8"));
12663
12899
  } catch {
12664
12900
  logWarn(
12665
12901
  `deploy preflight: could not parse ${lockPath} \u2014 skipping the stale-node_modules check.`
12666
12902
  );
12667
12903
  return null;
12668
12904
  }
12669
- const importerRel = relative3(dirname22(lockPath), appRoot) || ".";
12905
+ const importerRel = relative5(dirname22(lockPath), appRoot) || ".";
12670
12906
  const expected = extractImporterMeshVersions(lockDoc, importerRel);
12671
12907
  const names = Object.keys(expected);
12672
12908
  if (names.length === 0) return [];
@@ -12715,7 +12951,7 @@ var init_deploy_preflight = __esm({
12715
12951
  });
12716
12952
 
12717
12953
  // libs/mesh-cli/src/commands/deploy.ts
12718
- import { execFileSync as execFileSync18 } from "child_process";
12954
+ import { execFileSync as execFileSync19 } from "child_process";
12719
12955
  function buildPulumiArgs(pulumiArgs, stack) {
12720
12956
  const base = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? ["up", ...pulumiArgs] : [...pulumiArgs];
12721
12957
  const op = base[0];
@@ -12778,7 +13014,7 @@ function registerDeployCommand(program2) {
12778
13014
  delete env.AWS_PROFILE;
12779
13015
  const finalArgs = buildPulumiArgs(pulumiArgs, stack);
12780
13016
  try {
12781
- execFileSync18("pulumi", finalArgs, {
13017
+ execFileSync19("pulumi", finalArgs, {
12782
13018
  cwd: appRoot,
12783
13019
  env,
12784
13020
  stdio: "inherit"
@@ -13071,10 +13307,10 @@ var init_discover = __esm({
13071
13307
  });
13072
13308
 
13073
13309
  // libs/mesh-cli/src/docs/assemble.ts
13074
- import { execFileSync as execFileSync19 } from "node:child_process";
13310
+ import { execFileSync as execFileSync20 } from "node:child_process";
13075
13311
  import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync15 } from "node:fs";
13076
13312
  import path27 from "node:path";
13077
- import { parse as parseYaml3 } from "yaml";
13313
+ import { parse as parseYaml4 } from "yaml";
13078
13314
  function splitFrontMatter(markdown) {
13079
13315
  const normalized = markdown.replace(/^\uFEFF/, "");
13080
13316
  if (!normalized.startsWith("---\n") && !normalized.startsWith("---\r\n")) {
@@ -13086,7 +13322,7 @@ function splitFrontMatter(markdown) {
13086
13322
  const bodyStart = normalized.indexOf("\n", end + 1);
13087
13323
  const body = (bodyStart === -1 ? "" : normalized.slice(bodyStart + 1)).replace(/^\r?\n/, "");
13088
13324
  try {
13089
- const data = parseYaml3(rawBlock);
13325
+ const data = parseYaml4(rawBlock);
13090
13326
  if (data && typeof data === "object" && !Array.isArray(data)) {
13091
13327
  return {
13092
13328
  data,
@@ -13562,7 +13798,7 @@ function renderVersionJson(args) {
13562
13798
  }
13563
13799
  function currentCommit(repoRoot2) {
13564
13800
  try {
13565
- return execFileSync19("git", ["rev-parse", "--short", "HEAD"], {
13801
+ return execFileSync20("git", ["rev-parse", "--short", "HEAD"], {
13566
13802
  cwd: repoRoot2,
13567
13803
  encoding: "utf-8"
13568
13804
  }).trim();
@@ -13581,7 +13817,7 @@ function currentBaseline(repoRoot2) {
13581
13817
  }
13582
13818
  }
13583
13819
  function publishSetAtRef(repoRoot2, ref) {
13584
- const git = (gitArgs) => execFileSync19("git", gitArgs, {
13820
+ const git = (gitArgs) => execFileSync20("git", gitArgs, {
13585
13821
  cwd: repoRoot2,
13586
13822
  encoding: "utf-8",
13587
13823
  maxBuffer: 64 * 1024 * 1024
@@ -14069,7 +14305,7 @@ var init_portal = __esm({
14069
14305
  });
14070
14306
 
14071
14307
  // libs/mesh-cli/src/utils/build-info.ts
14072
- import { execFileSync as execFileSync20 } from "child_process";
14308
+ import { execFileSync as execFileSync21 } from "child_process";
14073
14309
  import * as fs24 from "fs";
14074
14310
  import * as path29 from "path";
14075
14311
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -14127,7 +14363,7 @@ function resolveCliRuntime(opts) {
14127
14363
  }
14128
14364
  if (!vcs) return info;
14129
14365
  try {
14130
- const git = (args) => execFileSync20("git", args, {
14366
+ const git = (args) => execFileSync21("git", args, {
14131
14367
  cwd: root,
14132
14368
  encoding: "utf-8",
14133
14369
  stdio: ["ignore", "pipe", "pipe"]
@@ -14147,10 +14383,10 @@ var init_build_info = __esm({
14147
14383
  // libs/mesh-cli/src/docs/serve.ts
14148
14384
  import { createServer as createServer8 } from "node:http";
14149
14385
  import { readFile } from "node:fs/promises";
14150
- import { extname, join as join27, normalize, sep as sep3 } from "node:path";
14386
+ import { extname, join as join27, normalize, sep as sep4 } from "node:path";
14151
14387
  async function serveDocsSite(args) {
14152
14388
  const root = normalize(args.root);
14153
- const rootPrefix = root.endsWith(sep3) ? root : root + sep3;
14389
+ const rootPrefix = root.endsWith(sep4) ? root : root + sep4;
14154
14390
  const server = createServer8((req, res) => {
14155
14391
  void (async () => {
14156
14392
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -14224,7 +14460,7 @@ var init_serve = __esm({
14224
14460
  });
14225
14461
 
14226
14462
  // libs/mesh-cli/src/docs/registry-docs.ts
14227
- import { execFileSync as execFileSync21 } from "node:child_process";
14463
+ import { execFileSync as execFileSync22 } from "node:child_process";
14228
14464
  import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync26, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
14229
14465
  import { tmpdir as tmpdir7 } from "node:os";
14230
14466
  import path30 from "node:path";
@@ -14316,7 +14552,7 @@ async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fet
14316
14552
  rmSync6(staging, { recursive: true, force: true });
14317
14553
  mkdirSync17(staging, { recursive: true });
14318
14554
  try {
14319
- execFileSync21("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
14555
+ execFileSync22("tar", ["-xzf", tgzPath, "-C", staging, "--strip-components", "1"], {
14320
14556
  stdio: ["pipe", "pipe", "pipe"]
14321
14557
  });
14322
14558
  } finally {
@@ -14354,7 +14590,7 @@ __export(start_exports, {
14354
14590
  tmuxInstallHint: () => tmuxInstallHint,
14355
14591
  tmuxServeArgs: () => tmuxServeArgs
14356
14592
  });
14357
- import { execFileSync as execFileSync22 } from "node:child_process";
14593
+ import { execFileSync as execFileSync23 } from "node:child_process";
14358
14594
  import { appendFileSync as appendFileSync2, existsSync as existsSync24, writeFileSync as writeFileSync18 } from "node:fs";
14359
14595
  import path31 from "node:path";
14360
14596
  function registryAuthOrThrow() {
@@ -14387,7 +14623,7 @@ function tmuxInstallHint(platform = process.platform) {
14387
14623
  }
14388
14624
  function tmuxAvailable() {
14389
14625
  try {
14390
- execFileSync22("tmux", ["-V"], { stdio: ["pipe", "pipe", "pipe"] });
14626
+ execFileSync23("tmux", ["-V"], { stdio: ["pipe", "pipe", "pipe"] });
14391
14627
  return true;
14392
14628
  } catch {
14393
14629
  return false;
@@ -14398,7 +14634,7 @@ function shouldDetach(args) {
14398
14634
  }
14399
14635
  function docsSessionExists() {
14400
14636
  try {
14401
- execFileSync22("tmux", ["has-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14637
+ execFileSync23("tmux", ["has-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14402
14638
  return true;
14403
14639
  } catch {
14404
14640
  return false;
@@ -14422,12 +14658,12 @@ Or run in the foreground instead: mesh docs start --foreground`
14422
14658
  if (docsSessionExists()) {
14423
14659
  logInfo(`Replacing the docs server already running in tmux session "${DOCS_TMUX_SESSION}".`);
14424
14660
  try {
14425
- execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14661
+ execFileSync23("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14426
14662
  } catch {
14427
14663
  }
14428
14664
  }
14429
14665
  const { command, cwd } = tmuxServeArgs(args);
14430
- execFileSync22(
14666
+ execFileSync23(
14431
14667
  "tmux",
14432
14668
  ["new-session", "-d", "-s", DOCS_TMUX_SESSION, "-c", cwd, "--", ...command],
14433
14669
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -14465,7 +14701,7 @@ function printReady(url, label) {
14465
14701
  }
14466
14702
  function runDocsStop() {
14467
14703
  try {
14468
- execFileSync22("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14704
+ execFileSync23("tmux", ["kill-session", "-t", DOCS_TMUX_SESSION], { stdio: ["pipe", "pipe", "pipe"] });
14469
14705
  logSuccess(`Stopped the docs server (tmux session "${DOCS_TMUX_SESSION}").`);
14470
14706
  } catch {
14471
14707
  logInfo(`No docs server is running (no tmux session named "${DOCS_TMUX_SESSION}").`);
@@ -14838,7 +15074,7 @@ var init_docs = __esm({
14838
15074
  });
14839
15075
 
14840
15076
  // libs/mesh-cli/src/commands/hub/index.ts
14841
- import { execFileSync as execFileSync23 } from "node:child_process";
15077
+ import { execFileSync as execFileSync24 } from "node:child_process";
14842
15078
  import * as fs26 from "node:fs";
14843
15079
  import * as net12 from "node:net";
14844
15080
  import * as os10 from "node:os";
@@ -14904,7 +15140,7 @@ function parseTmuxEnv(output) {
14904
15140
  }
14905
15141
  function readTmuxSessionEnv(sessionName) {
14906
15142
  try {
14907
- const out = execFileSync23("tmux", ["show-environment", "-t", sessionName], {
15143
+ const out = execFileSync24("tmux", ["show-environment", "-t", sessionName], {
14908
15144
  encoding: "utf-8",
14909
15145
  stdio: ["ignore", "pipe", "ignore"]
14910
15146
  });
@@ -14915,7 +15151,7 @@ function readTmuxSessionEnv(sessionName) {
14915
15151
  }
14916
15152
  function tmuxSessionExists(sessionName) {
14917
15153
  try {
14918
- execFileSync23("tmux", ["has-session", "-t", sessionName], { stdio: "ignore" });
15154
+ execFileSync24("tmux", ["has-session", "-t", sessionName], { stdio: "ignore" });
14919
15155
  return true;
14920
15156
  } catch {
14921
15157
  return false;
@@ -15079,7 +15315,7 @@ function redactEnv(env) {
15079
15315
  async function hubDevAction(opts) {
15080
15316
  if (opts.kill) {
15081
15317
  if (tmuxSessionExists(HUB_SESSION)) {
15082
- execFileSync23("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
15318
+ execFileSync24("tmux", ["kill-session", "-t", HUB_SESSION], { stdio: "ignore" });
15083
15319
  fs26.rmSync(hubEnvDir(), { recursive: true, force: true });
15084
15320
  logSuccess(`Killed Hub session '${HUB_SESSION}'.`);
15085
15321
  } else {
@@ -15133,17 +15369,17 @@ async function hubDevAction(opts) {
15133
15369
  for (const warning of assembled.warnings) logWarn(warning);
15134
15370
  const apiDir = path33.join(platformDir, "apps", "hub", "api");
15135
15371
  const uiDir = path33.join(platformDir, "apps", "hub", "ui");
15136
- execFileSync23("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
15372
+ execFileSync24("tmux", ["new-session", "-d", "-s", HUB_SESSION, "-n", "api", "-c", apiDir]);
15137
15373
  if (!assembled.apiEnv.DEV_USER_TOKEN_URL && assembled.apiEnv.DEV_USER_ID_TOKEN) {
15138
15374
  const platform = session.state.devOutput.platform;
15139
15375
  const credContext = `mesh.${platform.env}`;
15140
15376
  const tokenPort = await findFreePort4();
15141
15377
  const tokenUrl = `http://127.0.0.1:${tokenPort}`;
15142
- execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
15143
- execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
15378
+ execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", "token-server", "-c", platformDir]);
15379
+ execFileSync24("tmux", ["set-option", "-t", `${HUB_SESSION}:token-server`, "remain-on-exit", "on"], {
15144
15380
  stdio: "ignore"
15145
15381
  });
15146
- execFileSync23("tmux", [
15382
+ execFileSync24("tmux", [
15147
15383
  "send-keys",
15148
15384
  "-t",
15149
15385
  `${HUB_SESSION}:token-server`,
@@ -15161,12 +15397,12 @@ async function hubDevAction(opts) {
15161
15397
  const envFile = path33.join(hubEnvDir(), `${window}.env.sh`);
15162
15398
  writeEnvFile(envFile, env);
15163
15399
  if (createWindow) {
15164
- execFileSync23("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
15400
+ execFileSync24("tmux", ["new-window", "-t", HUB_SESSION, "-n", window, "-c", dir]);
15165
15401
  }
15166
- execFileSync23("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
15402
+ execFileSync24("tmux", ["set-option", "-t", `${HUB_SESSION}:${window}`, "remain-on-exit", "on"], {
15167
15403
  stdio: "ignore"
15168
15404
  });
15169
- execFileSync23("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
15405
+ execFileSync24("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
15170
15406
  };
15171
15407
  launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
15172
15408
  launch("ui", uiDir, assembled.uiEnv, `pnpm dev -- --port ${uiPort} --strictPort`, true);
@@ -15492,7 +15728,7 @@ var init_init = __esm({
15492
15728
  });
15493
15729
 
15494
15730
  // libs/mesh-cli/src/commands/install-shim.ts
15495
- import { execFileSync as execFileSync24 } from "child_process";
15731
+ import { execFileSync as execFileSync25 } from "child_process";
15496
15732
  import * as fs28 from "fs";
15497
15733
  import * as os11 from "os";
15498
15734
  import * as path35 from "path";
@@ -15605,7 +15841,7 @@ function registerInstallShimCommand(program2) {
15605
15841
  let status = 0;
15606
15842
  let ok = true;
15607
15843
  try {
15608
- stdout = execFileSync24(target, ["--help"], {
15844
+ stdout = execFileSync25(target, ["--help"], {
15609
15845
  cwd: process.cwd(),
15610
15846
  encoding: "utf8",
15611
15847
  stdio: ["ignore", "pipe", "pipe"]
@@ -15652,7 +15888,7 @@ var init_install_shim = __esm({
15652
15888
  });
15653
15889
 
15654
15890
  // libs/mesh-cli/src/commands/local/hub-local.ts
15655
- import { execFile as execFile3, execFileSync as execFileSync25 } from "child_process";
15891
+ import { execFile as execFile3, execFileSync as execFileSync26 } from "child_process";
15656
15892
  import * as fs29 from "fs";
15657
15893
  import * as os12 from "os";
15658
15894
  import * as path36 from "path";
@@ -15674,7 +15910,7 @@ function npmrcPath() {
15674
15910
  }
15675
15911
  function imageExists(tag) {
15676
15912
  try {
15677
- execFileSync25("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
15913
+ execFileSync26("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
15678
15914
  return true;
15679
15915
  } catch {
15680
15916
  return false;
@@ -15684,7 +15920,7 @@ function ensureHubAuthImage() {
15684
15920
  if (imageExists(HUB_AUTH_IMAGE)) return;
15685
15921
  logInfo(`Building ${HUB_AUTH_IMAGE} (Hub auth proxy)\u2026`);
15686
15922
  const hubStackDir = path36.join(findPackageRoot(), "stack", "hub");
15687
- execFileSync25(
15923
+ execFileSync26(
15688
15924
  "docker",
15689
15925
  ["build", "-f", path36.join(hubStackDir, "Dockerfile.auth"), "-t", HUB_AUTH_IMAGE, hubStackDir],
15690
15926
  { stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
@@ -15698,7 +15934,7 @@ function hasRegistryAuth() {
15698
15934
  function localHubVersion() {
15699
15935
  const versions = HUB_IMAGES.map((name) => {
15700
15936
  try {
15701
- const out = execFileSync25("docker", ["images", name, "--format", "{{.Tag}}"], {
15937
+ const out = execFileSync26("docker", ["images", name, "--format", "{{.Tag}}"], {
15702
15938
  encoding: "utf-8",
15703
15939
  stdio: ["ignore", "pipe", "pipe"]
15704
15940
  });
@@ -15777,7 +16013,7 @@ async function ensureHubImages() {
15777
16013
  const context = path36.join(cacheDir(), `context-${version}`);
15778
16014
  fs29.rmSync(context, { recursive: true, force: true });
15779
16015
  fs29.mkdirSync(context, { recursive: true });
15780
- execFileSync25("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
16016
+ execFileSync26("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
15781
16017
  stdio: ["ignore", "pipe", "pipe"]
15782
16018
  });
15783
16019
  const hubStackDir = path36.join(findPackageRoot(), "stack", "hub");
@@ -15788,7 +16024,7 @@ async function ensureHubImages() {
15788
16024
  const tag = `${name}:${version}`;
15789
16025
  if (imageExists(tag)) continue;
15790
16026
  logInfo(`Building ${tag} from the published tarball\u2026`);
15791
- execFileSync25(
16027
+ execFileSync26(
15792
16028
  "docker",
15793
16029
  [
15794
16030
  "build",
@@ -16285,6 +16521,12 @@ import {
16285
16521
  } from "@aws-sdk/client-secrets-manager";
16286
16522
  import input from "@inquirer/input";
16287
16523
  import password from "@inquirer/password";
16524
+ import {
16525
+ buildInstanceIndexValue,
16526
+ instanceIndexSecretId,
16527
+ isInstanceKey,
16528
+ parseInstanceIndex
16529
+ } from "@mesh-tech/secrets";
16288
16530
  async function ensureAwsCredentialsForStack(stack) {
16289
16531
  if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN) return;
16290
16532
  if (!stack) return;
@@ -16469,6 +16711,12 @@ Usage: mesh secrets set external/<name>`);
16469
16711
  );
16470
16712
  process.exit(1);
16471
16713
  }
16714
+ if (opts.key && !isInstanceKey(opts.key)) {
16715
+ logError(
16716
+ `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.`
16717
+ );
16718
+ process.exit(1);
16719
+ }
16472
16720
  let existing = {};
16473
16721
  try {
16474
16722
  const response = await smClient.send(
@@ -16493,6 +16741,9 @@ Usage: mesh secrets set external/<name>`);
16493
16741
  const merged2 = deepMerge(existing, values2);
16494
16742
  await writeSecret(smClient, secretId, merged2, svc.meta);
16495
16743
  await writeConfigMirror(smClient, secretId, schema, merged2, svc.meta);
16744
+ if (opts.key) {
16745
+ await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
16746
+ }
16496
16747
  logSuccess(`Credentials written to ${secretId}`);
16497
16748
  return;
16498
16749
  }
@@ -16508,6 +16759,9 @@ Usage: mesh secrets set external/<name>`);
16508
16759
  const merged = deepMerge(existing, values);
16509
16760
  await writeSecret(smClient, secretId, merged, svc.meta);
16510
16761
  await writeConfigMirror(smClient, secretId, schema, merged, svc.meta);
16762
+ if (opts.key) {
16763
+ await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
16764
+ }
16511
16765
  logSuccess(`Credentials written to ${secretId}`);
16512
16766
  console.error("\nStored fields:");
16513
16767
  displaySummary(schema.fields, merged);
@@ -16573,6 +16827,45 @@ function extractNonSecretFields(fields, values) {
16573
16827
  }
16574
16828
  return result;
16575
16829
  }
16830
+ async function addKeyToInstanceIndex(client, secretPrefix, key, serviceName) {
16831
+ const indexId = instanceIndexSecretId(secretPrefix);
16832
+ try {
16833
+ let keys = [];
16834
+ try {
16835
+ const res = await client.send(new GetSecretValueCommand5({ SecretId: indexId }));
16836
+ keys = parseInstanceIndex(res.SecretString ?? "");
16837
+ } catch (err) {
16838
+ if (!isResourceNotFound(err)) throw err;
16839
+ }
16840
+ if (keys.includes(key)) return;
16841
+ const secretString = buildInstanceIndexValue([...keys, key]);
16842
+ try {
16843
+ await client.send(
16844
+ new PutSecretValueCommand({ SecretId: indexId, SecretString: secretString })
16845
+ );
16846
+ } catch (err) {
16847
+ if (!isResourceNotFound(err)) throw err;
16848
+ await client.send(
16849
+ new CreateSecretCommand({
16850
+ Name: indexId,
16851
+ Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
16852
+ SecretString: secretString,
16853
+ Tags: [
16854
+ { Key: "mesh:type", Value: "external-service-index" },
16855
+ { Key: "mesh:service", Value: serviceName }
16856
+ ]
16857
+ })
16858
+ );
16859
+ logInfo(
16860
+ `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.`
16861
+ );
16862
+ }
16863
+ } catch (err) {
16864
+ logInfo(
16865
+ `Warning: failed to record instance "${key}" in ${indexId} \u2014 listInstances() will not see it until you run: mesh secrets reindex external/${serviceName} (${err})`
16866
+ );
16867
+ }
16868
+ }
16576
16869
  async function writeConfigMirror(client, secretId, schema, merged, meta) {
16577
16870
  const configId = `${secretId}/.config`;
16578
16871
  const configValues = extractNonSecretFields(schema.fields, merged);
@@ -16614,17 +16907,157 @@ var init_set = __esm({
16614
16907
  }
16615
16908
  });
16616
16909
 
16910
+ // libs/mesh-cli/src/commands/secrets/reindex.ts
16911
+ import { SSMClient as SSMClient3 } from "@aws-sdk/client-ssm";
16912
+ import {
16913
+ SecretsManagerClient as SecretsManagerClient6,
16914
+ ListSecretsCommand,
16915
+ GetSecretValueCommand as GetSecretValueCommand6,
16916
+ PutSecretValueCommand as PutSecretValueCommand2,
16917
+ CreateSecretCommand as CreateSecretCommand2
16918
+ } from "@aws-sdk/client-secrets-manager";
16919
+ import {
16920
+ buildInstanceIndexValue as buildInstanceIndexValue2,
16921
+ instanceIndexSecretId as instanceIndexSecretId2,
16922
+ instanceKeyFromSecretName,
16923
+ parseInstanceIndex as parseInstanceIndex2
16924
+ } from "@mesh-tech/secrets";
16925
+ async function scanInstanceKeys(client, secretPrefix) {
16926
+ const keys = [];
16927
+ let nextToken;
16928
+ do {
16929
+ const response = await client.send(
16930
+ new ListSecretsCommand({
16931
+ Filters: [{ Key: "name", Values: [`${secretPrefix}/`] }],
16932
+ NextToken: nextToken
16933
+ })
16934
+ );
16935
+ for (const secret of response.SecretList ?? []) {
16936
+ const key = secret.Name ? instanceKeyFromSecretName(secretPrefix, secret.Name) : null;
16937
+ if (key) keys.push(key);
16938
+ }
16939
+ nextToken = response.NextToken;
16940
+ } while (nextToken);
16941
+ return keys;
16942
+ }
16943
+ function indexAlreadyMatches(missing, stale, indexExists, malformed) {
16944
+ return missing.length === 0 && stale.length === 0 && indexExists && !malformed;
16945
+ }
16946
+ async function reindexCommand(servicePath, opts) {
16947
+ const stackOpt = resolveStackOption(opts);
16948
+ const context = detectContext(stackOpt);
16949
+ const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
16950
+ await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
16951
+ const ssmClient = new SSMClient3({ region });
16952
+ const smClient = new SecretsManagerClient6({ region });
16953
+ const services = await discoverExternalServices(
16954
+ ssmClient,
16955
+ context.tenant,
16956
+ context.platformEnv
16957
+ );
16958
+ const serviceName = servicePath?.replace(/^external\//, "");
16959
+ if (!serviceName || !services.has(serviceName)) {
16960
+ if (serviceName) {
16961
+ logError(`External service "${serviceName}" not found.`);
16962
+ }
16963
+ const keyed = [...services.entries()].filter(([, s]) => s.schema?.keyedBy);
16964
+ console.error("\nMulti-instance external services:");
16965
+ for (const [name, svc2] of keyed) {
16966
+ console.error(` ${name} (keyed by ${svc2.schema.keyedBy})`);
16967
+ }
16968
+ if (keyed.length === 0) console.error(" (none)");
16969
+ console.error(`
16970
+ Usage: mesh secrets reindex external/<name>`);
16971
+ process.exit(serviceName ? 1 : 0);
16972
+ }
16973
+ const svc = services.get(serviceName);
16974
+ if (!svc.schema?.keyedBy) {
16975
+ logError(
16976
+ `"${serviceName}" is single-instance (no keyedBy) \u2014 it has no instance index.`
16977
+ );
16978
+ process.exit(1);
16979
+ }
16980
+ const secretPrefix = svc.meta.secretPrefix;
16981
+ const indexId = instanceIndexSecretId2(secretPrefix);
16982
+ const actual = [...new Set(await scanInstanceKeys(smClient, secretPrefix))].sort();
16983
+ let listed = [];
16984
+ let indexExists = true;
16985
+ let malformed = false;
16986
+ try {
16987
+ const res = await smClient.send(new GetSecretValueCommand6({ SecretId: indexId }));
16988
+ try {
16989
+ listed = parseInstanceIndex2(res.SecretString ?? "");
16990
+ } catch {
16991
+ malformed = true;
16992
+ logInfo(`Existing index at ${indexId} is malformed \u2014 rebuilding from scratch.`);
16993
+ }
16994
+ } catch (err) {
16995
+ if (!isResourceNotFound(err)) throw err;
16996
+ indexExists = false;
16997
+ logInfo(`No index at ${indexId} yet \u2014 it will be created.`);
16998
+ }
16999
+ const missing = actual.filter((k) => !listed.includes(k));
17000
+ const stale = listed.filter((k) => !actual.includes(k));
17001
+ logInfo(`Instances in Secrets Manager: ${actual.length ? actual.join(", ") : "(none)"}`);
17002
+ if (missing.length > 0) logInfo(`Missing from index: ${missing.join(", ")}`);
17003
+ if (stale.length > 0) logInfo(`Stale in index (secret gone): ${stale.join(", ")}`);
17004
+ if (indexAlreadyMatches(missing, stale, indexExists, malformed)) {
17005
+ logSuccess(`Index at ${indexId} already matches \u2014 nothing to do.`);
17006
+ return;
17007
+ }
17008
+ if (opts.dryRun) {
17009
+ logInfo(`Dry run \u2014 would write ${indexId} with ${actual.length} instance(s).`);
17010
+ return;
17011
+ }
17012
+ await writeInstanceIndex(smClient, secretPrefix, serviceName, actual);
17013
+ logSuccess(`Index rebuilt: ${indexId} now lists ${actual.length} instance(s).`);
17014
+ }
17015
+ async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
17016
+ const indexId = instanceIndexSecretId2(secretPrefix);
17017
+ const secretString = buildInstanceIndexValue2(keys);
17018
+ try {
17019
+ await client.send(
17020
+ new PutSecretValueCommand2({ SecretId: indexId, SecretString: secretString })
17021
+ );
17022
+ } catch (err) {
17023
+ if (!isResourceNotFound(err)) throw err;
17024
+ await client.send(
17025
+ new CreateSecretCommand2({
17026
+ Name: indexId,
17027
+ Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
17028
+ SecretString: secretString,
17029
+ Tags: [
17030
+ { Key: "mesh:type", Value: "external-service-index" },
17031
+ { Key: "mesh:service", Value: serviceName }
17032
+ ]
17033
+ })
17034
+ );
17035
+ logInfo(
17036
+ `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.`
17037
+ );
17038
+ }
17039
+ }
17040
+ var init_reindex = __esm({
17041
+ "libs/mesh-cli/src/commands/secrets/reindex.ts"() {
17042
+ "use strict";
17043
+ init_context();
17044
+ init_log();
17045
+ init_stack_flag();
17046
+ init_set();
17047
+ }
17048
+ });
17049
+
16617
17050
  // libs/mesh-cli/src/commands/secrets/migrate-config.ts
16618
17051
  import {
16619
- SSMClient as SSMClient3,
17052
+ SSMClient as SSMClient4,
16620
17053
  GetParametersByPathCommand as GetParametersByPathCommand2
16621
17054
  } from "@aws-sdk/client-ssm";
16622
17055
  import {
16623
- SecretsManagerClient as SecretsManagerClient6,
16624
- GetSecretValueCommand as GetSecretValueCommand6,
16625
- PutSecretValueCommand as PutSecretValueCommand2,
16626
- CreateSecretCommand as CreateSecretCommand2,
16627
- ListSecretsCommand
17056
+ SecretsManagerClient as SecretsManagerClient7,
17057
+ GetSecretValueCommand as GetSecretValueCommand7,
17058
+ PutSecretValueCommand as PutSecretValueCommand3,
17059
+ CreateSecretCommand as CreateSecretCommand3,
17060
+ ListSecretsCommand as ListSecretsCommand2
16628
17061
  } from "@aws-sdk/client-secrets-manager";
16629
17062
  function normalizeEntry2(raw) {
16630
17063
  if (raw.type === "group" && raw.fields) {
@@ -16710,7 +17143,7 @@ function isResourceNotFound2(err) {
16710
17143
  }
16711
17144
  async function readSecret(client, secretId) {
16712
17145
  try {
16713
- const res = await client.send(new GetSecretValueCommand6({ SecretId: secretId }));
17146
+ const res = await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
16714
17147
  return res.SecretString ? JSON.parse(res.SecretString) : null;
16715
17148
  } catch (err) {
16716
17149
  if (isResourceNotFound2(err)) return null;
@@ -16720,10 +17153,10 @@ async function readSecret(client, secretId) {
16720
17153
  async function writeSecret2(client, secretId, values, description) {
16721
17154
  const secretString = JSON.stringify(values);
16722
17155
  try {
16723
- await client.send(new PutSecretValueCommand2({ SecretId: secretId, SecretString: secretString }));
17156
+ await client.send(new PutSecretValueCommand3({ SecretId: secretId, SecretString: secretString }));
16724
17157
  } catch (err) {
16725
17158
  if (isResourceNotFound2(err)) {
16726
- await client.send(new CreateSecretCommand2({
17159
+ await client.send(new CreateSecretCommand3({
16727
17160
  Name: secretId,
16728
17161
  Description: description,
16729
17162
  SecretString: secretString,
@@ -16736,7 +17169,7 @@ async function writeSecret2(client, secretId, values, description) {
16736
17169
  }
16737
17170
  async function secretExists(client, secretId) {
16738
17171
  try {
16739
- await client.send(new GetSecretValueCommand6({ SecretId: secretId }));
17172
+ await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
16740
17173
  return true;
16741
17174
  } catch (err) {
16742
17175
  if (isResourceNotFound2(err)) return false;
@@ -16749,7 +17182,7 @@ async function listInstanceKeys(client, secretPrefix) {
16749
17182
  let nextToken;
16750
17183
  do {
16751
17184
  const response = await client.send(
16752
- new ListSecretsCommand({
17185
+ new ListSecretsCommand2({
16753
17186
  Filters: [{ Key: "name", Values: [prefix2] }],
16754
17187
  NextToken: nextToken
16755
17188
  })
@@ -16815,8 +17248,8 @@ async function migrateService(smClient, meta, schema, opts) {
16815
17248
  async function migrateConfigCommand(opts) {
16816
17249
  const context = detectContext(resolveStackOption(opts));
16817
17250
  const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
16818
- const ssmClient = new SSMClient3({ region });
16819
- const smClient = new SecretsManagerClient6({ region });
17251
+ const ssmClient = new SSMClient4({ region });
17252
+ const smClient = new SecretsManagerClient7({ region });
16820
17253
  logInfo(`Migrating .config mirrors for ${context.tenant}/${context.platformEnv}`);
16821
17254
  if (opts.dryRun) logInfo("DRY-RUN mode \u2014 no writes");
16822
17255
  if (opts.force) logInfo("FORCE mode \u2014 overwrite existing .config");
@@ -16869,6 +17302,11 @@ function registerSecretsCommands(program2) {
16869
17302
  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) => {
16870
17303
  await setCommand(service, opts);
16871
17304
  });
17305
+ secrets.command("reindex [service]").description(
17306
+ "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"
17307
+ ).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) => {
17308
+ await reindexCommand(service, opts);
17309
+ });
16872
17310
  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) => {
16873
17311
  await migrateConfigCommand(opts);
16874
17312
  });
@@ -16878,15 +17316,16 @@ var init_secrets = __esm({
16878
17316
  "use strict";
16879
17317
  init_exec2();
16880
17318
  init_set();
17319
+ init_reindex();
16881
17320
  init_migrate_config();
16882
17321
  }
16883
17322
  });
16884
17323
 
16885
17324
  // libs/mesh-cli/src/commands/stack.ts
16886
- import { execFileSync as execFileSync26 } from "child_process";
17325
+ import { execFileSync as execFileSync27 } from "child_process";
16887
17326
  import * as path37 from "path";
16888
17327
  import * as fs30 from "fs";
16889
- import { parse as parseYaml4 } from "yaml";
17328
+ import { parse as parseYaml5 } from "yaml";
16890
17329
  function readTopLevelYamlKey(appRoot, stack, key) {
16891
17330
  const configFile = path37.join(appRoot, `Pulumi.${stack}.yaml`);
16892
17331
  if (!fs30.existsSync(configFile)) return null;
@@ -16963,7 +17402,7 @@ function readBaseConfigFromYaml(appRoot, stack) {
16963
17402
  if (!fs30.existsSync(file)) return {};
16964
17403
  let doc;
16965
17404
  try {
16966
- doc = parseYaml4(fs30.readFileSync(file, "utf-8"));
17405
+ doc = parseYaml5(fs30.readFileSync(file, "utf-8"));
16967
17406
  } catch {
16968
17407
  return {};
16969
17408
  }
@@ -16976,7 +17415,7 @@ function readBaseConfigFromYaml(appRoot, stack) {
16976
17415
  }
16977
17416
  function getGitHubUsername() {
16978
17417
  try {
16979
- const result = execFileSync26("gh", ["api", "user", "--jq", ".login"], {
17418
+ const result = execFileSync27("gh", ["api", "user", "--jq", ".login"], {
16980
17419
  encoding: "utf-8",
16981
17420
  stdio: ["pipe", "pipe", "pipe"]
16982
17421
  });
@@ -16985,7 +17424,7 @@ function getGitHubUsername() {
16985
17424
  } catch {
16986
17425
  }
16987
17426
  try {
16988
- const result = execFileSync26("git", ["config", "user.email"], {
17427
+ const result = execFileSync27("git", ["config", "user.email"], {
16989
17428
  encoding: "utf-8",
16990
17429
  stdio: ["pipe", "pipe", "pipe"]
16991
17430
  });
@@ -17071,7 +17510,7 @@ Specify which to base on: mesh stack init --from <stack>`
17071
17510
  if (!opts.adopt) {
17072
17511
  let existing = [];
17073
17512
  try {
17074
- const raw = execFileSync26("pulumi", ["stack", "ls", "--json"], {
17513
+ const raw = execFileSync27("pulumi", ["stack", "ls", "--json"], {
17075
17514
  cwd: appRoot,
17076
17515
  encoding: "utf-8",
17077
17516
  env: pulumiEnv,
@@ -17095,7 +17534,7 @@ Specify which to base on: mesh stack init --from <stack>`
17095
17534
  logInfo(`Using KMS secrets provider: ${secretsProvider}`);
17096
17535
  }
17097
17536
  try {
17098
- execFileSync26("pulumi", initArgs, {
17537
+ execFileSync27("pulumi", initArgs, {
17099
17538
  cwd: appRoot,
17100
17539
  env: pulumiEnv,
17101
17540
  stdio: "inherit"
@@ -17112,7 +17551,7 @@ Specify which to base on: mesh stack init --from <stack>`
17112
17551
  }
17113
17552
  }
17114
17553
  try {
17115
- execFileSync26("pulumi", ["stack", "select", newStack], {
17554
+ execFileSync27("pulumi", ["stack", "select", newStack], {
17116
17555
  cwd: appRoot,
17117
17556
  env: pulumiEnv,
17118
17557
  stdio: ["pipe", "pipe", "pipe"]
@@ -17122,7 +17561,7 @@ Specify which to base on: mesh stack init --from <stack>`
17122
17561
  if (!configExists) {
17123
17562
  let baseConfig = {};
17124
17563
  try {
17125
- const raw = execFileSync26(
17564
+ const raw = execFileSync27(
17126
17565
  "pulumi",
17127
17566
  ["config", "--json", "--stack", baseStack],
17128
17567
  { cwd: appRoot, env: pulumiEnv, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
@@ -17140,19 +17579,19 @@ Specify which to base on: mesh stack init --from <stack>`
17140
17579
  if (key === "mesh:deploy") continue;
17141
17580
  try {
17142
17581
  if (entry.objectValue !== void 0) {
17143
- execFileSync26(
17582
+ execFileSync27(
17144
17583
  "pulumi",
17145
17584
  ["config", "set", key, JSON.stringify(entry.objectValue)],
17146
17585
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
17147
17586
  );
17148
17587
  } else if (entry.value === "true" || entry.value === "false") {
17149
- execFileSync26(
17588
+ execFileSync27(
17150
17589
  "pulumi",
17151
17590
  ["config", "set", "--type", "bool", key, entry.value],
17152
17591
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
17153
17592
  );
17154
17593
  } else {
17155
- execFileSync26(
17594
+ execFileSync27(
17156
17595
  "pulumi",
17157
17596
  ["config", "set", key, entry.value],
17158
17597
  { cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
@@ -17165,7 +17604,7 @@ Specify which to base on: mesh stack init --from <stack>`
17165
17604
  const baseEnv = readConfigBlockKey(appRoot, baseStack, "mesh:coreEnv") ?? (baseTenant && baseStack.startsWith(`${baseTenant}-`) ? baseStack.slice(baseTenant.length + 1) : baseStack);
17166
17605
  const setCfg = (args) => {
17167
17606
  try {
17168
- execFileSync26("pulumi", ["config", "set", ...args], {
17607
+ execFileSync27("pulumi", ["config", "set", ...args], {
17169
17608
  cwd: appRoot,
17170
17609
  env: pulumiEnv,
17171
17610
  stdio: ["pipe", "pipe", "pipe"]
@@ -17214,7 +17653,7 @@ Specify which to base on: mesh stack init --from <stack>`
17214
17653
  `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}` : ""})`
17215
17654
  );
17216
17655
  } else {
17217
- execFileSync26("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
17656
+ execFileSync27("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
17218
17657
  cwd: appRoot,
17219
17658
  env: pulumiEnv,
17220
17659
  stdio: ["pipe", "pipe", "pipe"]
@@ -17244,7 +17683,7 @@ Specify which to base on: mesh stack init --from <stack>`
17244
17683
  const args = ["stack", "rm", name];
17245
17684
  if (opts.yes) args.push("--yes");
17246
17685
  try {
17247
- execFileSync26("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
17686
+ execFileSync27("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
17248
17687
  logSuccess(`Removed stack ${name}`);
17249
17688
  } catch (err) {
17250
17689
  process.exit(err.status ?? 1);
@@ -17456,12 +17895,12 @@ var init_recover_conversation = __esm({
17456
17895
  });
17457
17896
 
17458
17897
  // libs/mesh-cli/src/utils/temporal-codec.ts
17459
- import { execFileSync as execFileSync27 } from "child_process";
17898
+ import { execFileSync as execFileSync28 } from "child_process";
17460
17899
  import { webcrypto as crypto4 } from "node:crypto";
17461
17900
  function resolveTemporalEncodingKeyFromK8s(namespace) {
17462
17901
  const secretName = `${namespace}-temporal-encoding-key`;
17463
17902
  try {
17464
- const b64 = execFileSync27(
17903
+ const b64 = execFileSync28(
17465
17904
  "kubectl",
17466
17905
  [
17467
17906
  "get",
@@ -18519,8 +18958,8 @@ import * as fs32 from "fs";
18519
18958
  import * as os13 from "os";
18520
18959
  import * as path39 from "path";
18521
18960
  import {
18522
- SecretsManagerClient as SecretsManagerClient7,
18523
- GetSecretValueCommand as GetSecretValueCommand7
18961
+ SecretsManagerClient as SecretsManagerClient8,
18962
+ GetSecretValueCommand as GetSecretValueCommand8
18524
18963
  } from "@aws-sdk/client-secrets-manager";
18525
18964
  function resolveTenantEnv(options) {
18526
18965
  if (options.tenant && options.env) {
@@ -18783,10 +19222,10 @@ async function tunnelExternal(name, options) {
18783
19222
  const { appTenant, appStage } = resolveCredentialAxis(options);
18784
19223
  const secretId = externalSecretId(appTenant, appStage, name, key);
18785
19224
  const setHint = `mesh secrets set external/${name}${key ? ` --key=${key}` : ""}`;
18786
- const sm = new SecretsManagerClient7({});
19225
+ const sm = new SecretsManagerClient8({});
18787
19226
  let creds;
18788
19227
  try {
18789
- const out = await sm.send(new GetSecretValueCommand7({ SecretId: secretId }));
19228
+ const out = await sm.send(new GetSecretValueCommand8({ SecretId: secretId }));
18790
19229
  creds = JSON.parse(out.SecretString ?? "{}");
18791
19230
  } catch (err) {
18792
19231
  logError(
@@ -20339,7 +20778,7 @@ var init_src3 = __esm({
20339
20778
  import * as fs33 from "fs";
20340
20779
  import * as path40 from "path";
20341
20780
  import { createRequire as createRequire2 } from "module";
20342
- import { execFileSync as execFileSync28 } from "child_process";
20781
+ import { execFileSync as execFileSync29 } from "child_process";
20343
20782
  function resolveExtractorPath() {
20344
20783
  try {
20345
20784
  const require2 = createRequire2(import.meta.url);
@@ -20394,7 +20833,7 @@ for (const file of files) {
20394
20833
  process.stdout.write(JSON.stringify(results));
20395
20834
  `;
20396
20835
  try {
20397
- const result = execFileSync28("npx", ["tsx", "--eval", script], {
20836
+ const result = execFileSync29("npx", ["tsx", "--eval", script], {
20398
20837
  encoding: "utf-8",
20399
20838
  stdio: ["pipe", "pipe", "inherit"],
20400
20839
  maxBuffer: 10 * 1024 * 1024
@@ -20436,7 +20875,7 @@ for (const file of files) {
20436
20875
  process.stdout.write(JSON.stringify(results));
20437
20876
  `;
20438
20877
  try {
20439
- const result = execFileSync28("npx", ["tsx", "--eval", script], {
20878
+ const result = execFileSync29("npx", ["tsx", "--eval", script], {
20440
20879
  encoding: "utf-8",
20441
20880
  stdio: ["pipe", "pipe", "inherit"],
20442
20881
  maxBuffer: 10 * 1024 * 1024
@@ -20491,7 +20930,7 @@ for (const file of files) {
20491
20930
  process.stdout.write(JSON.stringify(results));
20492
20931
  `;
20493
20932
  try {
20494
- const result = execFileSync28("npx", ["tsx", "--eval", script], {
20933
+ const result = execFileSync29("npx", ["tsx", "--eval", script], {
20495
20934
  encoding: "utf-8",
20496
20935
  stdio: ["pipe", "pipe", "inherit"],
20497
20936
  maxBuffer: 10 * 1024 * 1024