@mesh-tech/mesh-cli 0.34.0 → 0.34.2

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.
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "1e6a7cc8e",
3
- "builtAt": "2026-09-22T21:56:57.206Z"
2
+ "commit": "bab7945c2",
3
+ "builtAt": "2026-09-24T17:48:24.813Z"
4
4
  }
@@ -1896,7 +1896,8 @@ export function registerDevCommand(program) {
1896
1896
  if (mode === 'local') {
1897
1897
  throw new MeshCliError(`Docker external '${name}' failed to start: ${err instanceof Error ? err.message : err}`, { remediation: { command: `docker compose -f ${decl.compose} up # debug it directly` } });
1898
1898
  }
1899
- logWarn(`External '${name}' credential seeding failed: ${err instanceof Error ? err.message : err}`);
1899
+ logWarn(`External '${name}' credential seeding failed: ${err instanceof Error ? err.message : err}` +
1900
+ ` — the app will resolve whatever was seeded previously, which may point at a stale port.`);
1900
1901
  }
1901
1902
  }
1902
1903
  if (dockerRunner) {
@@ -8,7 +8,7 @@ export const PLATFORM_SETUP_DEFAULTS = {
8
8
  profile: "default",
9
9
  };
10
10
  export const DOMAIN_REQUIRED_MESSAGE = "--domain is required: the core stack creates the public Route 53 zone <env>.<domain>, " +
11
- "and a placeholder domain scaffolds a zone that can never validate (the deploy stalls on the ACM certificate).";
11
+ "and a placeholder domain scaffolds a zone whose certificate can never validate, so ingress never serves HTTPS.";
12
12
  export function defaultStateBucket(tenant) {
13
13
  return `${tenant}-mesh-platform-pulumi-state`;
14
14
  }
@@ -31,7 +31,7 @@ export function validateDomain(value) {
31
31
  return "a public domain you control, e.g. q2poc.pub";
32
32
  }
33
33
  if (/(^|\.)(example\.(com|org|net)|invalid|localhost|test)$/.test(domain)) {
34
- return `${domain} is a placeholder — the deploy stalls on an ACM certificate for a zone nobody can delegate. Use a domain you control.`;
34
+ return `${domain} is a placeholder — its ACM certificate can never validate, because nobody can delegate that zone to you. Use a domain you control.`;
35
35
  }
36
36
  return true;
37
37
  }
@@ -177,7 +177,7 @@ export function renderPlatformNextSteps(setup, opts = {}) {
177
177
  if (!opts.bucket?.present) {
178
178
  steps.push(`${stateBucketCreateCommand(setup)} # the Pulumi state bucket, once`);
179
179
  }
180
- steps.push(`cd ${dir}`, "pnpm install", `aws sso login --profile ${setup.profile} # if this profile is SSO`, exportCredentialsCommand(setup.profile), `cd core && mesh deploy up # the VPC, EKS, RDS and the public zone ${setup.env}.${setup.domain}`, `# → then delegate the NS records of ${setup.env}.${setup.domain} from ${setup.domain}; the deploy waits on the ACM certificate until you do`, "cd ../platform && mesh deploy up # Temporal, Zitadel, the Hub (needs Docker — it builds service images)", `mesh login ${setup.tenant}.${setup.env} # your platform sign-in, once it is up`, "", "Then, in your apps repo, for the hello-world app:", ` mesh init --tenant ${setup.tenant} --platform ${setup.env}`, " mesh create-app --name hello --primitives service", " mesh deploy up # from the app directory");
180
+ steps.push(`cd ${dir}`, "pnpm install", `aws sso login --profile ${setup.profile} # if this profile is SSO`, exportCredentialsCommand(setup.profile), `cd core && mesh deploy up # the VPC, EKS, RDS and the public zone ${setup.env}.${setup.domain}`, `# → then delegate the NS records of ${setup.env}.${setup.domain} from ${setup.domain} at your registrar; the deploy stops at the mesh-gateway Ingress until ACM issues the certificate, then continues`, "cd ../platform && mesh deploy up # Temporal, Zitadel, the Hub (needs Docker — it builds service images)", `mesh login ${setup.tenant}.${setup.env} # your platform sign-in, once it is up`, "", "Then, in your apps repo, for the hello-world app:", ` mesh init --tenant ${setup.tenant} --platform ${setup.env}`, " mesh create-app --name hello --primitives service", " mesh deploy up # from the app directory");
181
181
  return steps;
182
182
  }
183
183
  export async function scaffoldPlatformRepo(setup) {
@@ -225,7 +225,7 @@ export function registerInitCommands(program) {
225
225
  .option("--platform <env>", "a deployed platform's environment; signs in to `<tenant>.<env>`")
226
226
  .option("--new-platform [env]", `nothing deployed yet — scaffold a tenant platform repo for \`<tenant>.<env>\` (default env: ${PLATFORM_SETUP_DEFAULTS.env}) and print its deploy sequence`)
227
227
  .option("--region <region>", `--new-platform: AWS region (default: ${PLATFORM_SETUP_DEFAULTS.region})`)
228
- .option("--domain <domain>", "--new-platform: base public domain you control; the core stack creates the zone {env}.{domain}. No default — a placeholder scaffolds a zone that can never validate")
228
+ .option("--domain <domain>", "--new-platform: base public domain you control; the core stack creates the zone {env}.{domain} and stops at the mesh-gateway Ingress until you delegate its NS records. No default — a placeholder scaffolds a zone that can never validate")
229
229
  .option("--state-bucket <bucket>", "--new-platform: Pulumi state S3 bucket (default: {tenant}-mesh-platform-pulumi-state)")
230
230
  .option("--create-state-bucket", "--new-platform: create the state bucket (versioned) if it does not exist")
231
231
  .option("--no-create-state-bucket", "--new-platform: never create the state bucket; print the aws command instead")
@@ -246,7 +246,7 @@ export function registerInitCommands(program) {
246
246
  .description("Scaffold a tenant PLATFORM repo (core + platform Pulumi layers on @mesh-tech/infra-components) that deploys with `mesh deploy` unmodified — the proven mesh-sandbox shape, annotated. On a terminal it ASKS for the values that differ per install (env, region, domain, state bucket, AWS profile), each pre-filled with its default; every answer is also a flag.")
247
247
  .option("--env <env>", `environment the first stack targets (default: ${PLATFORM_SETUP_DEFAULTS.env})`)
248
248
  .option("--region <region>", `AWS region (default: ${PLATFORM_SETUP_DEFAULTS.region})`)
249
- .option("--domain <domain>", "base public domain you control — the core stack creates the Route 53 zone {env}.{domain} and waits on its NS delegation. Required: it has no default, because a placeholder scaffolds a zone that can never validate")
249
+ .option("--domain <domain>", "base public domain you control — the core stack creates the Route 53 zone {env}.{domain}, whose NS records you then delegate at your registrar; the deploy stops at the mesh-gateway Ingress until that lets ACM issue the certificate. Required: it has no default, because a placeholder scaffolds a zone nobody can delegate to you")
250
250
  .option("--profile <profile>", `AWS profile the stack config references (default: ${PLATFORM_SETUP_DEFAULTS.profile})`)
251
251
  .option("--state-bucket <bucket>", "Pulumi state S3 bucket (default: {tenant}-mesh-platform-pulumi-state)")
252
252
  .option("--create-state-bucket", "create the state bucket (versioned) if it does not exist — prompted on a terminal")
@@ -1,6 +1,7 @@
1
1
  import * as net from "net";
2
2
  import { execFileSync } from "child_process";
3
3
  import { LOCAL_AWS_CONFIG } from "./seed.js";
4
+ import { isAwsErrorCode } from "../../utils/aws-errors.js";
4
5
  export async function upsertLocalSecret(secretId, value, client) {
5
6
  const { SecretsManagerClient, CreateSecretCommand, PutSecretValueCommand } = await import("@aws-sdk/client-secrets-manager");
6
7
  const sm = client ?? new SecretsManagerClient(LOCAL_AWS_CONFIG);
@@ -9,7 +10,7 @@ export async function upsertLocalSecret(secretId, value, client) {
9
10
  await sm.send(new CreateSecretCommand({ Name: secretId, SecretString: secretString }));
10
11
  }
11
12
  catch (err) {
12
- if (err?.name === "ResourceExistsException") {
13
+ if (isAwsErrorCode(err, "ResourceExistsException")) {
13
14
  await sm.send(new PutSecretValueCommand({ SecretId: secretId, SecretString: secretString }));
14
15
  }
15
16
  else {
@@ -126,7 +126,7 @@ export async function verifyFabric() {
126
126
  if (roundTrip.Parameter?.Value !== payload) {
127
127
  throw new MeshCliError(`Local AWS fabric failed the ${payload.length}-byte Advanced-tier SSM round-trip (got ${roundTrip.Parameter?.Value?.length ?? 0} bytes back).`, {
128
128
  remediation: {
129
- docs: "libs/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
129
+ docs: "packages/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
130
130
  },
131
131
  });
132
132
  }
@@ -136,7 +136,7 @@ export async function verifyFabric() {
136
136
  if (missing.length > 0) {
137
137
  throw new MeshCliError(`Local AWS fabric's GetParametersByPath missed child parameter(s): ${missing.join(", ")} (splitKeys exports would not resolve).`, {
138
138
  remediation: {
139
- docs: "libs/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
139
+ docs: "packages/mesh-cli/stack/docker-compose.yml — swap the ministack image for motoserver/moto (design §2.5 fallback)",
140
140
  },
141
141
  });
142
142
  }
@@ -2,6 +2,7 @@ import { SSMClient, GetParametersByPathCommand, } from "@aws-sdk/client-ssm";
2
2
  import { SecretsManagerClient, GetSecretValueCommand, PutSecretValueCommand, CreateSecretCommand, ListSecretsCommand, } from "@aws-sdk/client-secrets-manager";
3
3
  import { logInfo, logSuccess, logError } from "../../utils/log.js";
4
4
  import { resolveSecretsContext } from "./context.js";
5
+ import { isAwsErrorCode } from "../../utils/aws-errors.js";
5
6
  function normalizeEntry(raw) {
6
7
  if (raw.type === "group" && raw.fields) {
7
8
  return {
@@ -79,21 +80,13 @@ function extractNonSecretFields(fields, values) {
79
80
  }
80
81
  return result;
81
82
  }
82
- function isResourceNotFound(err) {
83
- const e = err;
84
- return (e?.name === "ResourceNotFoundException" ||
85
- e?.__type === "ResourceNotFoundException" ||
86
- e?.Code === "ResourceNotFoundException" ||
87
- e?.code === "ResourceNotFoundException" ||
88
- (typeof e?.message === "string" && e.message.includes("ResourceNotFoundException")));
89
- }
90
83
  async function readSecret(client, secretId) {
91
84
  try {
92
85
  const res = await client.send(new GetSecretValueCommand({ SecretId: secretId }));
93
86
  return res.SecretString ? JSON.parse(res.SecretString) : null;
94
87
  }
95
88
  catch (err) {
96
- if (isResourceNotFound(err))
89
+ if (isAwsErrorCode(err, "ResourceNotFoundException"))
97
90
  return null;
98
91
  throw err;
99
92
  }
@@ -104,7 +97,7 @@ async function writeSecret(client, secretId, values, description) {
104
97
  await client.send(new PutSecretValueCommand({ SecretId: secretId, SecretString: secretString }));
105
98
  }
106
99
  catch (err) {
107
- if (isResourceNotFound(err)) {
100
+ if (isAwsErrorCode(err, "ResourceNotFoundException")) {
108
101
  await client.send(new CreateSecretCommand({
109
102
  Name: secretId,
110
103
  Description: description,
@@ -123,7 +116,7 @@ async function secretExists(client, secretId) {
123
116
  return true;
124
117
  }
125
118
  catch (err) {
126
- if (isResourceNotFound(err))
119
+ if (isAwsErrorCode(err, "ResourceNotFoundException"))
127
120
  return false;
128
121
  throw err;
129
122
  }
@@ -8,6 +8,7 @@ import { resolveStackOption } from "../../utils/stack-flag.js";
8
8
  import { resolveSecretsContext } from "./context.js";
9
9
  import { findAppRoot, readStackConfig } from "../../utils/pulumi.js";
10
10
  import { resolveAwsCredentials } from "../../utils/aws-auth.js";
11
+ import { isAwsErrorCode } from "../../utils/aws-errors.js";
11
12
  export async function ensureAwsCredentialsForStack(stack) {
12
13
  if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN)
13
14
  return;
@@ -244,12 +245,7 @@ export async function setCommand(servicePath, opts) {
244
245
  console.error("");
245
246
  }
246
247
  export function isResourceNotFound(err) {
247
- const e = err;
248
- return (e?.name === "ResourceNotFoundException" ||
249
- e?.__type === "ResourceNotFoundException" ||
250
- e?.Code === "ResourceNotFoundException" ||
251
- e?.code === "ResourceNotFoundException" ||
252
- (typeof e?.message === "string" && e.message.includes("ResourceNotFoundException")));
248
+ return isAwsErrorCode(err, "ResourceNotFoundException");
253
249
  }
254
250
  function deepMerge(target, source) {
255
251
  const result = { ...target };