@mesh-tech/mesh-cli 0.35.2 → 0.37.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 (107) hide show
  1. package/assets/docs-preview.css +1599 -0
  2. package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js +48 -0
  3. package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js.map +6 -0
  4. package/dist/bin/mesh.capture-history-A4IAVVIR.js +38 -0
  5. package/dist/bin/mesh.capture-history-A4IAVVIR.js.map +6 -0
  6. package/dist/bin/mesh.chunk-5A5OFSYR.js +187 -0
  7. package/dist/bin/mesh.chunk-5A5OFSYR.js.map +6 -0
  8. package/dist/bin/mesh.chunk-7DMAFUBQ.js +7715 -0
  9. package/dist/bin/mesh.chunk-7DMAFUBQ.js.map +6 -0
  10. package/dist/bin/mesh.chunk-A4DGMGHF.js +303 -0
  11. package/dist/bin/mesh.chunk-A4DGMGHF.js.map +6 -0
  12. package/dist/bin/mesh.chunk-CQ2IC4OP.js +217 -0
  13. package/dist/bin/mesh.chunk-CQ2IC4OP.js.map +6 -0
  14. package/dist/bin/mesh.chunk-F4654EPX.js +383 -0
  15. package/dist/bin/mesh.chunk-F4654EPX.js.map +6 -0
  16. package/dist/bin/mesh.chunk-IQOBTL42.js +60 -0
  17. package/dist/bin/mesh.chunk-IQOBTL42.js.map +6 -0
  18. package/dist/bin/mesh.chunk-J6DVWKRW.js +647 -0
  19. package/dist/bin/mesh.chunk-J6DVWKRW.js.map +6 -0
  20. package/dist/bin/mesh.chunk-JB7FWYSP.js +1751 -0
  21. package/dist/bin/mesh.chunk-JB7FWYSP.js.map +6 -0
  22. package/dist/bin/mesh.chunk-MCBMHWXP.js +837 -0
  23. package/dist/bin/mesh.chunk-MCBMHWXP.js.map +6 -0
  24. package/dist/bin/mesh.chunk-OW5EMVG5.js +507 -0
  25. package/dist/bin/mesh.chunk-OW5EMVG5.js.map +6 -0
  26. package/dist/bin/mesh.chunk-R5VVTXG4.js +10092 -0
  27. package/dist/bin/mesh.chunk-R5VVTXG4.js.map +6 -0
  28. package/dist/bin/mesh.chunk-VKV6Z3UI.js +1146 -0
  29. package/dist/bin/mesh.chunk-VKV6Z3UI.js.map +6 -0
  30. package/dist/bin/mesh.chunk-WFFDF346.js +59 -0
  31. package/dist/bin/mesh.chunk-WFFDF346.js.map +6 -0
  32. package/dist/bin/mesh.chunk-WOJOTUFZ.js +835 -0
  33. package/dist/bin/mesh.chunk-WOJOTUFZ.js.map +6 -0
  34. package/dist/bin/mesh.cli-reference-H3TOPIET.js +158 -0
  35. package/dist/bin/mesh.cli-reference-H3TOPIET.js.map +6 -0
  36. package/dist/bin/mesh.convert-KK6ZP256.js +274 -0
  37. package/dist/bin/mesh.convert-KK6ZP256.js.map +6 -0
  38. package/dist/bin/mesh.create-app-A5F45PWZ.js +39 -0
  39. package/dist/bin/mesh.create-app-A5F45PWZ.js.map +6 -0
  40. package/dist/bin/mesh.dev-token-server-GYDYPXYH.js +61 -0
  41. package/dist/bin/mesh.dev-token-server-GYDYPXYH.js.map +6 -0
  42. package/dist/bin/mesh.js +20 -26414
  43. package/dist/bin/mesh.js.map +3 -3
  44. package/dist/bin/mesh.login-JNHPVMCT.js +91 -0
  45. package/dist/bin/mesh.login-JNHPVMCT.js.map +6 -0
  46. package/dist/bin/mesh.platform-setup-EB64EFRN.js +37 -0
  47. package/dist/bin/mesh.platform-setup-EB64EFRN.js.map +6 -0
  48. package/dist/bin/mesh.preview-3IP34TDO.js +2410 -0
  49. package/dist/bin/mesh.preview-3IP34TDO.js.map +6 -0
  50. package/dist/bin/mesh.program-DWOKHNMQ.js +23 -0
  51. package/dist/bin/mesh.program-DWOKHNMQ.js.map +6 -0
  52. package/dist/bin/mesh.registry-OXF57OQW.js +47 -0
  53. package/dist/bin/mesh.registry-OXF57OQW.js.map +6 -0
  54. package/dist/bin/mesh.registry-identity-2HSGQGJC.js +42 -0
  55. package/dist/bin/mesh.registry-identity-2HSGQGJC.js.map +6 -0
  56. package/dist/bin/mesh.skills-MNDSMBIE.js +23 -0
  57. package/dist/bin/mesh.skills-MNDSMBIE.js.map +6 -0
  58. package/dist/bin/mesh.src-5D5DFSLX.js +1302 -0
  59. package/dist/bin/mesh.src-5D5DFSLX.js.map +6 -0
  60. package/dist/bin/mesh.src-G7TJBLNJ.js +40 -0
  61. package/dist/bin/mesh.src-G7TJBLNJ.js.map +6 -0
  62. package/dist/bin/mesh.stack-EFTFAOCV.js +64 -0
  63. package/dist/bin/mesh.stack-EFTFAOCV.js.map +6 -0
  64. package/dist/bin/mesh.start-ARIIY6CV.js +454 -0
  65. package/dist/bin/mesh.start-ARIIY6CV.js.map +6 -0
  66. package/dist/bin/mesh.wizard-ROAQAGSF.js +500 -0
  67. package/dist/bin/mesh.wizard-ROAQAGSF.js.map +6 -0
  68. package/dist/build-info.json +2 -2
  69. package/dist/src/commands/app-check.d.ts +2 -1
  70. package/dist/src/commands/app-check.js +32 -0
  71. package/dist/src/commands/create-app.d.ts +1 -0
  72. package/dist/src/commands/create-app.js +4 -0
  73. package/dist/src/commands/dev.d.ts +6 -0
  74. package/dist/src/commands/dev.js +152 -3
  75. package/dist/src/commands/docs.js +33 -0
  76. package/dist/src/commands/local/act-as-local.d.ts +5 -0
  77. package/dist/src/commands/local/act-as-local.js +41 -0
  78. package/dist/src/commands/local/auth-provision.d.ts +1 -0
  79. package/dist/src/commands/local/auth-provision.js +1 -0
  80. package/dist/src/commands/local/index.js +4 -0
  81. package/dist/src/commands/local/local-api-key.d.ts +44 -0
  82. package/dist/src/commands/local/local-api-key.js +115 -0
  83. package/dist/src/commands/local/local-spicedb.d.ts +95 -0
  84. package/dist/src/commands/local/local-spicedb.js +304 -0
  85. package/dist/src/commands/local/publish-docs.d.ts +81 -0
  86. package/dist/src/commands/local/publish-docs.js +328 -0
  87. package/dist/src/commands/local/seed-zitadel.js +2 -0
  88. package/dist/src/commands/temporal.js +191 -61
  89. package/dist/src/commands/temporal.js.map +3 -3
  90. package/dist/src/docs/convert.d.ts +35 -0
  91. package/dist/src/docs/convert.js +276 -0
  92. package/dist/src/docs/preview-source.d.ts +13 -0
  93. package/dist/src/docs/preview-source.js +58 -0
  94. package/dist/src/docs/preview.d.ts +32 -0
  95. package/dist/src/docs/preview.js +279 -0
  96. package/dist/src/utils/scaffold-versions.d.ts +1 -0
  97. package/dist/src/utils/scaffold-versions.js +1 -0
  98. package/fragments/agents/AGENTS.md.hbs +27 -27
  99. package/fragments/base/README.md.hbs +5 -2
  100. package/fragments/base/index.ts.hbs +22 -33
  101. package/fragments/base/package.json.hbs +5 -1
  102. package/fragments/service/api/openapi.json.hbs +1 -1
  103. package/fragments/service/docs/docs.yaml.hbs +10 -0
  104. package/fragments/service/docs/pages/getting-started.md.hbs +6 -9
  105. package/package.json +19 -3
  106. package/skills/mesh/SKILL.md +2 -2
  107. package/stack/docker-compose.hub.yml +6 -0
@@ -0,0 +1,835 @@
1
+ import {
2
+ PUBLISHED_BROKER_TIMEOUT_MS,
3
+ clearRegistrySession,
4
+ describeSessionExpiry,
5
+ ensureRegistrySession,
6
+ fetchRegistryGrant,
7
+ identityChanged,
8
+ logRegistryIdentity,
9
+ readRegistrySession,
10
+ realGrantDeps,
11
+ refreshRegistrySession,
12
+ registryAccountState,
13
+ resolveRegistryIdentity
14
+ } from "./mesh.chunk-A4DGMGHF.js";
15
+ import {
16
+ readCredentials
17
+ } from "./mesh.chunk-R5VVTXG4.js";
18
+ import {
19
+ logError,
20
+ logInfo,
21
+ logSuccess,
22
+ logWarn
23
+ } from "./mesh.chunk-WFFDF346.js";
24
+ import {
25
+ MeshCliError
26
+ } from "./mesh.chunk-IQOBTL42.js";
27
+ import {
28
+ SSO_SESSION_SNIPPET,
29
+ awsConfigHasProfile,
30
+ findUnscopedCodeArtifactRegistry,
31
+ homeNpmrcPath,
32
+ npmrcAuthKeyForEndpoint,
33
+ probeRegistryToken,
34
+ readAwsConfig,
35
+ stripUnscopedCodeArtifactRegistry,
36
+ upsertNpmrcLines
37
+ } from "./mesh.chunk-5A5OFSYR.js";
38
+
39
+ // packages/mesh-cli/src/commands/registry.ts
40
+ import { execFileSync as execFileSync2 } from "child_process";
41
+ import * as fs2 from "fs";
42
+ import * as path2 from "path";
43
+
44
+ // packages/mesh-cli/src/commands/registry-publish.ts
45
+ import { execFileSync, execSync } from "child_process";
46
+ import * as fs from "fs";
47
+ import * as path from "path";
48
+ function repoRoot() {
49
+ return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
50
+ }
51
+ function computeSnapshotVersion(root) {
52
+ const sha = execSync("git rev-parse --short HEAD", { cwd: root, encoding: "utf-8" }).trim();
53
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
54
+ return `0.0.0-dev.${stamp}.${sha}`;
55
+ }
56
+ function setPackageVersion(file, version) {
57
+ const content = fs.readFileSync(file, "utf-8");
58
+ const next = content.replace(/"version"(\s*:\s*)"[^"]*"/, `"version"$1"${version}"`);
59
+ fs.writeFileSync(file, next);
60
+ }
61
+ function dirtyPackageJsons(root) {
62
+ const out = execSync("git diff --name-only", { cwd: root, encoding: "utf-8" });
63
+ return new Set(
64
+ out.split("\n").map((s) => s.trim()).filter((s) => s.endsWith("package.json"))
65
+ );
66
+ }
67
+ function registerRegistryPublish(registry) {
68
+ registry.command("publish").description(
69
+ "Publish an in-development snapshot of @mesh-tech packages to CodeArtifact (dev dist-tag)"
70
+ ).argument("[context]", 'Platform context for Zitadel auth (e.g. "mesh.dev")').option(
71
+ "--snapshot",
72
+ "Publish a dev snapshot build (required \u2014 real releases go through changesets/CI)",
73
+ false
74
+ ).option("--tag <tag>", "dist-tag to publish the snapshot under", "dev").option(
75
+ "--only <pkgs>",
76
+ "Comma-separated substring filter, e.g. --only agent-sdk,app-kit (default: all publishable)"
77
+ ).option("--dry-run", "Build + resolve versions but do not upload", false).option("--role <arn>", "IAM role ARN for registry access").action(async (context, opts) => {
78
+ if (!opts.snapshot) {
79
+ logError(
80
+ "mesh registry publish currently supports only snapshot (dev) builds.\n\nRe-run with --snapshot. Real releases go through changesets + the\nchangeset-version CI workflow, not this command."
81
+ );
82
+ process.exit(1);
83
+ }
84
+ const root = repoRoot();
85
+ const awsCreds = resolveCredentials(context, opts.role);
86
+ const env = Object.keys(awsCreds).length > 0 ? { ...process.env, ...awsCreds } : void 0;
87
+ logInfo("Authenticating with CodeArtifact...");
88
+ if (!codeartifactLogin(env)) {
89
+ logError("CodeArtifact login failed. Run: mesh registry login <context>");
90
+ process.exit(1);
91
+ }
92
+ const listPath = path.join(root, "scripts/lib/publishable-packages.json");
93
+ const all = JSON.parse(fs.readFileSync(listPath, "utf-8"));
94
+ const patterns = (opts.only ?? "").split(",").map((s) => s.trim()).filter(Boolean);
95
+ const selected = patterns.length ? all.filter((p) => patterns.some((pat) => p.dir.includes(pat) || p.name.includes(pat))) : all;
96
+ if (selected.length === 0) {
97
+ logError(`No publishable package matched --only '${opts.only}'`);
98
+ process.exit(1);
99
+ }
100
+ const version = computeSnapshotVersion(root);
101
+ logInfo(`Snapshot version: ${version} (dist-tag '${opts.tag}')`);
102
+ const preDirty = dirtyPackageJsons(root);
103
+ const backups = [];
104
+ const stamp = (dir) => {
105
+ const file = path.join(root, dir, "package.json");
106
+ if (!fs.existsSync(file)) return;
107
+ backups.push({ file, content: fs.readFileSync(file, "utf-8") });
108
+ setPackageVersion(file, version);
109
+ };
110
+ try {
111
+ for (const p of selected) {
112
+ stamp(p.dir);
113
+ for (const sub of p.subPackages ?? []) {
114
+ stamp(path.join(p.dir, typeof sub === "string" ? sub : sub.dir));
115
+ }
116
+ }
117
+ const args = [
118
+ "scripts/publish-packages.sh",
119
+ "--tag",
120
+ opts.tag,
121
+ "--skip-latest",
122
+ // Pass the TS-resolved DIRS (not the raw patterns) so the shell's
123
+ // dir-substring --only filter selects exactly the set we stamped —
124
+ // a name-only pattern would otherwise stamp here but be skipped there.
125
+ ...patterns.length ? ["--only", selected.map((p) => p.dir).join(",")] : [],
126
+ ...opts.dryRun ? ["--dry-run"] : []
127
+ ];
128
+ logInfo(`Publishing ${selected.length} package(s)...`);
129
+ execFileSync("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
130
+ } finally {
131
+ for (const b of backups) fs.writeFileSync(b.file, b.content);
132
+ logInfo("Restored source package.json versions.");
133
+ const collateral = [...dirtyPackageJsons(root)].filter((f) => !preDirty.has(f));
134
+ if (collateral.length) {
135
+ try {
136
+ execFileSync("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
137
+ logInfo(`Reverted ${collateral.length} package.json file(s) re-vendored by build:publish.`);
138
+ } catch {
139
+ logError(
140
+ `Could not auto-revert build:publish side-effects \u2014 run \`git checkout --\` on:
141
+ ${collateral.join("\n ")}`
142
+ );
143
+ }
144
+ }
145
+ }
146
+ logSuccess(`Published snapshot ${version} under dist-tag '${opts.tag}'.`);
147
+ console.log(
148
+ "\nPin these EXACTLY in the consumer repo (prereleases are never auto-resolved by ^/~):"
149
+ );
150
+ for (const p of selected) console.log(` "${p.name}": "${version}",`);
151
+ console.log(
152
+ `
153
+ Or always grab the newest dev build:
154
+ pnpm add ${selected.map((p) => `${p.name}@${opts.tag}`).join(" ")}`
155
+ );
156
+ });
157
+ }
158
+
159
+ // packages/mesh-cli/src/commands/registry.ts
160
+ var CA_DOMAIN = "mesh-platform";
161
+ var CA_REPOSITORY = "mesh-packages";
162
+ var CA_NAMESPACE = "@mesh-tech";
163
+ var CA_REGION = "us-east-2";
164
+ var DEFAULT_REGISTRY_ROLE = null;
165
+ function getEndpoint(env) {
166
+ try {
167
+ const result = execFileSync2("aws", [
168
+ "codeartifact",
169
+ "get-repository-endpoint",
170
+ "--domain",
171
+ CA_DOMAIN,
172
+ "--repository",
173
+ CA_REPOSITORY,
174
+ "--format",
175
+ "npm",
176
+ "--region",
177
+ CA_REGION,
178
+ "--output",
179
+ "text",
180
+ "--query",
181
+ "repositoryEndpoint"
182
+ ], {
183
+ encoding: "utf-8",
184
+ env: env ? { ...process.env, ...env } : void 0,
185
+ stdio: ["pipe", "pipe", "pipe"]
186
+ });
187
+ return result.trim() || null;
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+ function codeartifactLogin(env) {
193
+ try {
194
+ execFileSync2("aws", [
195
+ "codeartifact",
196
+ "login",
197
+ "--tool",
198
+ "npm",
199
+ "--domain",
200
+ CA_DOMAIN,
201
+ "--repository",
202
+ CA_REPOSITORY,
203
+ "--namespace",
204
+ CA_NAMESPACE,
205
+ "--region",
206
+ CA_REGION
207
+ ], {
208
+ encoding: "utf-8",
209
+ env: env ? { ...process.env, ...env } : void 0,
210
+ stdio: ["pipe", "pipe", "pipe"]
211
+ });
212
+ return true;
213
+ } catch {
214
+ return false;
215
+ }
216
+ }
217
+ function parseTenantEnv(context) {
218
+ const parts = context.split(".");
219
+ if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
220
+ return { tenant: parts[0], env: parts[1] };
221
+ }
222
+ async function getRegistrySsmExport(tenant, env) {
223
+ try {
224
+ const { SSMClient, GetParameterCommand } = await import("@aws-sdk/client-ssm");
225
+ const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? CA_REGION;
226
+ const ssm = new SSMClient({ region });
227
+ const ssmPath = `/mesh-platform/${tenant}/${env}/registry`;
228
+ const response = await ssm.send(new GetParameterCommand({ Name: ssmPath }));
229
+ if (!response.Parameter?.Value) return null;
230
+ return JSON.parse(response.Parameter.Value);
231
+ } catch {
232
+ return null;
233
+ }
234
+ }
235
+ function resolvePublisherRoleArn({
236
+ roleArnFlag,
237
+ ssmArn
238
+ }) {
239
+ const resolved = roleArnFlag || ssmArn;
240
+ if (!resolved) {
241
+ throw new Error(
242
+ "No publisher role: pass --role or ensure the registry stack exported publisherRoleArn."
243
+ );
244
+ }
245
+ return resolved;
246
+ }
247
+ function assumeRole(roleArn) {
248
+ try {
249
+ const result = execFileSync2("aws", [
250
+ "sts",
251
+ "assume-role",
252
+ "--role-arn",
253
+ roleArn,
254
+ "--role-session-name",
255
+ "mesh-registry",
256
+ "--output",
257
+ "json"
258
+ ], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
259
+ const creds = JSON.parse(result).Credentials;
260
+ return {
261
+ AWS_ACCESS_KEY_ID: creds.AccessKeyId,
262
+ AWS_SECRET_ACCESS_KEY: creds.SecretAccessKey,
263
+ AWS_SESSION_TOKEN: creds.SessionToken
264
+ };
265
+ } catch {
266
+ return null;
267
+ }
268
+ }
269
+ function assumeRoleWithWebIdentity(roleArn, idToken, sessionName) {
270
+ try {
271
+ const result = execFileSync2("aws", [
272
+ "sts",
273
+ "assume-role-with-web-identity",
274
+ "--role-arn",
275
+ roleArn,
276
+ "--web-identity-token",
277
+ idToken,
278
+ "--role-session-name",
279
+ sessionName,
280
+ "--output",
281
+ "json"
282
+ ], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
283
+ const creds = JSON.parse(result).Credentials;
284
+ return {
285
+ AWS_ACCESS_KEY_ID: creds.AccessKeyId,
286
+ AWS_SECRET_ACCESS_KEY: creds.SecretAccessKey,
287
+ AWS_SESSION_TOKEN: creds.SessionToken
288
+ };
289
+ } catch {
290
+ return null;
291
+ }
292
+ }
293
+ function resolveCredentials(context, roleArn) {
294
+ if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SESSION_TOKEN) {
295
+ logInfo("Using existing AWS credentials from environment");
296
+ return {};
297
+ }
298
+ if (context) {
299
+ const meshCreds = readCredentials(context);
300
+ if (meshCreds && new Date(meshCreds.expiresAt) > /* @__PURE__ */ new Date()) {
301
+ const targetRole = roleArn || DEFAULT_REGISTRY_ROLE;
302
+ if (targetRole) {
303
+ const sessionName = (meshCreds.email ?? "mesh-registry").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
304
+ logInfo(`Assuming registry role via Zitadel (${meshCreds.email ?? context})`);
305
+ const creds = assumeRoleWithWebIdentity(targetRole, meshCreds.idToken, sessionName);
306
+ if (creds) {
307
+ logSuccess("Assumed registry role via Zitadel");
308
+ return creds;
309
+ }
310
+ logWarn("Zitadel JWT role assumption failed \u2014 falling back to AWS profile");
311
+ } else {
312
+ logInfo(`Zitadel auth available (${meshCreds.email}) but no registry role configured \u2014 using AWS profile`);
313
+ }
314
+ } else if (meshCreds) {
315
+ logWarn(`Zitadel token expired for ${context}. Run: mesh login ${context}`);
316
+ } else {
317
+ logWarn(`No Zitadel credentials for ${context}. Run: mesh login ${context}`);
318
+ }
319
+ }
320
+ if (roleArn) {
321
+ logInfo(`Assuming ${roleArn.split("/").pop()} via AWS SSO`);
322
+ const creds = assumeRole(roleArn);
323
+ if (creds) {
324
+ logSuccess("Assumed registry role via AWS SSO");
325
+ return creds;
326
+ }
327
+ logWarn("Role assumption failed \u2014 trying current credentials directly");
328
+ }
329
+ logInfo("Using current AWS profile");
330
+ return {};
331
+ }
332
+ function findProjectRoot() {
333
+ let dir = process.cwd();
334
+ while (true) {
335
+ if (fs2.existsSync(path2.join(dir, "pnpm-workspace.yaml")) || fs2.existsSync(path2.join(dir, "pnpm-lock.yaml")) || fs2.existsSync(path2.join(dir, "Pulumi.yaml")) || fs2.existsSync(path2.join(dir, ".npmrc"))) {
336
+ return dir;
337
+ }
338
+ const parent = path2.dirname(dir);
339
+ if (parent === dir) return null;
340
+ dir = parent;
341
+ }
342
+ }
343
+ function ensureProjectNpmrc(endpoint) {
344
+ const projectRoot = findProjectRoot() ?? process.cwd();
345
+ const npmrcPath = path2.join(projectRoot, ".npmrc");
346
+ const scopeLine = `${CA_NAMESPACE}:registry=${endpoint}`;
347
+ if (!fs2.existsSync(npmrcPath)) {
348
+ fs2.writeFileSync(npmrcPath, scopeLine + "\n", "utf-8");
349
+ return { created: true, updated: false, path: npmrcPath };
350
+ }
351
+ const content = fs2.readFileSync(npmrcPath, "utf-8");
352
+ const scopeRegex = new RegExp(`^${CA_NAMESPACE.replace("/", "\\/")}:registry=.+$`, "m");
353
+ if (scopeRegex.test(content)) {
354
+ if (content.includes(scopeLine)) {
355
+ return { created: false, updated: false, path: npmrcPath };
356
+ }
357
+ const updated = content.replace(scopeRegex, scopeLine);
358
+ fs2.writeFileSync(npmrcPath, updated, "utf-8");
359
+ return { created: false, updated: true, path: npmrcPath };
360
+ }
361
+ const separator = content.endsWith("\n") ? "" : "\n";
362
+ fs2.writeFileSync(npmrcPath, content + separator + scopeLine + "\n", "utf-8");
363
+ return { created: false, updated: true, path: npmrcPath };
364
+ }
365
+ function applyGrantToUserNpmrc(grant, npmrcPath = homeNpmrcPath()) {
366
+ let existing = "";
367
+ try {
368
+ existing = fs2.readFileSync(npmrcPath, "utf-8");
369
+ } catch {
370
+ }
371
+ const content = upsertNpmrcLines(existing, [
372
+ { key: `${grant.scope}:registry`, value: grant.endpoint },
373
+ { key: `${npmrcAuthKeyForEndpoint(grant.endpoint)}:_authToken`, value: grant.authorizationToken }
374
+ ]);
375
+ fs2.mkdirSync(path2.dirname(npmrcPath), { recursive: true });
376
+ fs2.writeFileSync(npmrcPath, content, { mode: 384 });
377
+ try {
378
+ fs2.chmodSync(npmrcPath, 384);
379
+ } catch {
380
+ }
381
+ return { path: npmrcPath };
382
+ }
383
+ function describeBrokerFailure(failure, account = {}) {
384
+ const unverified = account.emailVerified === false ? `
385
+ Your Mesh account${account.email ? ` (${account.email})` : ""} has never confirmed its email address, and an unconfirmed account cannot hold a registry grant.
386
+ This is not something a role fixes: ask your Mesh contact to verify the address on your account (a self-registered, passkey-only sign-up lands here), then run: mesh registry logout && mesh registry login` : "";
387
+ switch (failure.kind) {
388
+ case "no-broker":
389
+ return {
390
+ message: "The registry publishes no broker \u2014 using the AWS paths.",
391
+ fatal: false
392
+ };
393
+ case "no-session":
394
+ return {
395
+ message: "No registry session \u2014 using the AWS paths.",
396
+ hint: "mesh registry login",
397
+ fatal: false
398
+ };
399
+ case "unauthenticated":
400
+ return {
401
+ message: "The registry broker rejected your session (expired or wrong audience)." + unverified,
402
+ hint: "mesh registry logout && mesh registry login",
403
+ fatal: false
404
+ };
405
+ case "not-authorized":
406
+ return {
407
+ // The whole message lives in the body, not in a `remediation.command`:
408
+ // the answer here is "ask a human for a role", which is not a command
409
+ // anyone can run, and rendering a URL behind "→ run:" reads as one.
410
+ message: `Your Mesh account${failure.email ? ` (${failure.email})` : ""} is not authorized to read ${CA_NAMESPACE} packages.` + (failure.requiredRoles?.length ? `
411
+ Needs one of these Zitadel roles: ${failure.requiredRoles.join(", ")}` : "") + (failure.requestUrl ? `
412
+ Request access: ${failure.requestUrl}` : "\n Ask a Mesh platform admin to grant your account registry access.") + `
413
+ ${NOT_AUTHORIZED_RERUN}` + unverified,
414
+ fatal: true
415
+ };
416
+ case "unavailable":
417
+ return {
418
+ message: `Registry broker unavailable (${failure.detail}) \u2014 falling back to the AWS paths.`,
419
+ fatal: false
420
+ };
421
+ }
422
+ }
423
+ var RegistryNotAuthorizedError = class extends MeshCliError {
424
+ constructor(message) {
425
+ super(message);
426
+ this.name = "RegistryNotAuthorizedError";
427
+ }
428
+ };
429
+ function registrySignInAllowed(opts, hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
430
+ return Boolean(opts.device) || hasTty;
431
+ }
432
+ function shouldRemintAfter(failure) {
433
+ return failure.kind === "not-authorized";
434
+ }
435
+ var NOT_AUTHORIZED_RERUN = "Once granted, run: mesh registry login (it re-mints your sign-in; still refused? mesh registry logout && mesh registry login)";
436
+ var CONTEXT_IGNORED_NOTICE = "The registry is global \u2014 the context argument is no longer needed (ignored).";
437
+ async function tryBrokerLogin(opts) {
438
+ if (opts.broker === false) return { outcome: { kind: "skipped-flag", flag: "--no-broker" } };
439
+ if (opts.ci) return { outcome: { kind: "skipped-flag", flag: "--ci" } };
440
+ let identity;
441
+ try {
442
+ identity = await resolveRegistryIdentity();
443
+ } catch (err) {
444
+ const detail2 = err.message.split("\n")[0] ?? String(err);
445
+ logWarn(`Registry broker unavailable (${detail2}) \u2014 falling back to the AWS paths.`);
446
+ return { outcome: { kind: "unavailable", detail: detail2 } };
447
+ }
448
+ logRegistryIdentity(identity);
449
+ const session = await ensureRegistrySession({
450
+ device: opts.device,
451
+ interactive: registrySignInAllowed(opts)
452
+ });
453
+ if (!session.ok) {
454
+ if (session.reason === "login-failed") {
455
+ throw new MeshCliError(`Registry sign-in failed: ${session.detail}`, {
456
+ remediation: { command: opts.device ? "mesh registry login --device" : "mesh registry login" }
457
+ });
458
+ }
459
+ logWarn(`No registry session (${session.detail}) \u2014 falling back to the AWS paths.`);
460
+ return { outcome: { kind: "no-session", detail: session.detail } };
461
+ }
462
+ logInfo(`Registry session: ${session.email}`);
463
+ let result = await requestGrant(session.context, identity);
464
+ if (!result.ok && (result.failure.kind === "unauthenticated" || result.failure.kind === "unavailable")) {
465
+ let fresh = null;
466
+ try {
467
+ fresh = await resolveRegistryIdentity({ refresh: true });
468
+ } catch {
469
+ }
470
+ if (fresh && identityChanged(identity, fresh)) {
471
+ logInfo(`Registry identity changed since it was cached \u2014 retrying with ${fresh.registryBroker}`);
472
+ identity = fresh;
473
+ result = await requestGrant(session.context, identity);
474
+ }
475
+ }
476
+ if (!result.ok && shouldRemintAfter(result.failure)) {
477
+ const { rolesChanged } = await refreshRegistrySession(session.context);
478
+ if (rolesChanged) {
479
+ logInfo("Your sign-in now carries a role it did not have \u2014 retrying\u2026");
480
+ result = await requestGrant(session.context, identity);
481
+ }
482
+ }
483
+ if (result.ok) return { grant: result.grant };
484
+ const { message, hint, fatal } = describeBrokerFailure(
485
+ result.failure,
486
+ registryAccountState(readCredentials(session.context)?.idToken)
487
+ );
488
+ if (fatal) {
489
+ throw new RegistryNotAuthorizedError(message);
490
+ }
491
+ logWarn(message);
492
+ if (hint) logInfo(` Try: ${hint}`);
493
+ const detail = result.failure.kind === "unavailable" ? result.failure.detail : result.failure.kind;
494
+ return { outcome: { kind: "unavailable", detail } };
495
+ }
496
+ async function requestGrant(sessionContext, identity) {
497
+ logInfo(`Requesting a registry token from ${identity.registryBroker} (no AWS credentials needed)\u2026`);
498
+ return fetchRegistryGrant(sessionContext, identity.registryBroker, {
499
+ ...realGrantDeps(),
500
+ timeoutMs: PUBLISHED_BROKER_TIMEOUT_MS
501
+ });
502
+ }
503
+ function shouldRepairUserNpmrc(opts) {
504
+ return !opts.ci;
505
+ }
506
+ function repairUnscopedRegistry(npmrcPath = homeNpmrcPath()) {
507
+ let original;
508
+ try {
509
+ original = fs2.readFileSync(npmrcPath, "utf-8");
510
+ } catch {
511
+ return { removed: null };
512
+ }
513
+ const { content, removed } = stripUnscopedCodeArtifactRegistry(original);
514
+ if (!removed) return { removed: null };
515
+ const backupPath = `${npmrcPath}.bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
516
+ try {
517
+ fs2.writeFileSync(backupPath, original, "utf-8");
518
+ fs2.writeFileSync(npmrcPath, content, "utf-8");
519
+ } catch (err) {
520
+ return { removed: null, error: err instanceof Error ? err.message : String(err) };
521
+ }
522
+ return { removed, backupPath };
523
+ }
524
+ function renderRegistrySessionLine(session, now = Date.now()) {
525
+ if (!session) return "No registry session \u2014 run: mesh registry login";
526
+ return `Registry session: ${session.email} (expires in ${describeSessionExpiry(session.expiresAt, now)})`;
527
+ }
528
+ async function checkExistingToken() {
529
+ const npmrcPath = homeNpmrcPath();
530
+ if (!fs2.existsSync(npmrcPath)) return { valid: false, state: "missing", reason: "no ~/.npmrc" };
531
+ const hijackLine = findUnscopedCodeArtifactRegistry(
532
+ fs2.readFileSync(npmrcPath, "utf-8")
533
+ ) ?? void 0;
534
+ const probe = await probeRegistryToken({ npmrcPath });
535
+ if (probe.state === "missing") {
536
+ return { valid: false, state: probe.state, reason: "no CodeArtifact token in ~/.npmrc", hijackLine };
537
+ }
538
+ if (probe.state === "expired") {
539
+ return {
540
+ valid: false,
541
+ state: probe.state,
542
+ reason: probe.detail ?? "the CodeArtifact token is expired",
543
+ hijackLine
544
+ };
545
+ }
546
+ if (probe.state === "unreachable") {
547
+ return {
548
+ valid: false,
549
+ state: probe.state,
550
+ reason: `could not reach the registry to verify the token (${probe.detail ?? "unknown error"})`,
551
+ hijackLine
552
+ };
553
+ }
554
+ if (hijackLine) {
555
+ return { valid: false, state: probe.state, reason: "unscoped CodeArtifact registry", hijackLine };
556
+ }
557
+ return { valid: true, state: probe.state, endpoint: getEndpoint() ?? void 0 };
558
+ }
559
+ async function runRegistryStatus(opts) {
560
+ const { valid, state, endpoint, reason, hijackLine } = await (opts?.check ?? checkExistingToken)();
561
+ if (hijackLine) {
562
+ logWarn(
563
+ "~/.npmrc sets CodeArtifact as your DEFAULT registry \u2014 every public npm install goes through it and will 401 when the 12h token expires:"
564
+ );
565
+ logInfo(` ${hijackLine.trim()}`);
566
+ logInfo("Fix: mesh registry login # removes it (backup taken) and keeps @mesh-tech scoped");
567
+ }
568
+ const unverifiable = state === "unreachable" && !hijackLine;
569
+ if (!valid && !unverifiable) {
570
+ logWarn(`Registry auth is not usable${reason ? ` \u2014 ${reason}` : ""}. Run: mesh registry login`);
571
+ return 1;
572
+ }
573
+ if (unverifiable) {
574
+ logWarn(`Could not verify the CodeArtifact token in ~/.npmrc \u2014 ${reason}.`);
575
+ logInfo("Nothing to fix from here; retry when the registry is reachable.");
576
+ } else {
577
+ logSuccess("CodeArtifact token in ~/.npmrc is valid");
578
+ if (endpoint) {
579
+ logInfo(`Endpoint: ${endpoint}`);
580
+ }
581
+ }
582
+ logInfo(renderRegistrySessionLine(opts?.session ?? readRegistrySession()));
583
+ const projectRoot = findProjectRoot();
584
+ if (projectRoot) {
585
+ const npmrcPath = path2.join(projectRoot, ".npmrc");
586
+ if (fs2.existsSync(npmrcPath)) {
587
+ const content = fs2.readFileSync(npmrcPath, "utf-8");
588
+ if (content.includes(`${CA_NAMESPACE}:registry=`)) {
589
+ logSuccess(`Project .npmrc configured (${npmrcPath})`);
590
+ } else {
591
+ logWarn(`Project .npmrc exists but missing ${CA_NAMESPACE} scope (${npmrcPath})`);
592
+ logInfo("Run: mesh registry login");
593
+ }
594
+ } else {
595
+ logWarn(`No .npmrc in project root (${projectRoot})`);
596
+ logInfo("Run: mesh registry login");
597
+ }
598
+ }
599
+ return 0;
600
+ }
601
+ function ensureSsoProfile(profile) {
602
+ const awsConfig = readAwsConfig();
603
+ if (awsConfig && !awsConfigHasProfile(awsConfig, profile)) {
604
+ throw new MeshCliError(
605
+ `AWS profile '${profile}' is not defined in ~/.aws/config \u2014 \`aws sso login --profile ${profile}\` cannot work until it exists.
606
+
607
+ If you only need to INSTALL @mesh-tech packages, drop --profile: \`mesh registry login\` uses the
608
+ Zitadel-gated broker and needs no AWS account at all.
609
+
610
+ If you DEPLOY and genuinely need this profile \u2014 one-time setup: \`aws configure sso --profile ${profile}\`,
611
+ or add the session + profile by hand:
612
+ ${SSO_SESSION_SNIPPET}
613
+ [profile ${profile}]
614
+ sso_session = mesh
615
+ sso_account_id = <account id>
616
+ sso_role_name = <role>
617
+ region = us-east-2`,
618
+ { remediation: { command: `aws configure sso --profile ${profile}` } }
619
+ );
620
+ }
621
+ const probe = () => execFileSync2("aws", ["sts", "get-caller-identity", "--profile", profile], {
622
+ stdio: ["ignore", "pipe", "pipe"],
623
+ timeout: 2e4
624
+ });
625
+ try {
626
+ probe();
627
+ logInfo(`AWS SSO session for profile '${profile}' is live`);
628
+ return;
629
+ } catch {
630
+ logInfo(`AWS SSO session for profile '${profile}' is stale \u2014 opening browser login\u2026`);
631
+ }
632
+ execFileSync2("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
633
+ try {
634
+ probe();
635
+ } catch (err) {
636
+ throw new MeshCliError(
637
+ `AWS SSO login for profile '${profile}' completed but the session still fails an identity check.`,
638
+ {
639
+ remediation: { command: `aws sts get-caller-identity --profile ${profile} # see the underlying error` },
640
+ cause: err
641
+ }
642
+ );
643
+ }
644
+ logSuccess(`AWS SSO session refreshed (profile '${profile}')`);
645
+ }
646
+ async function runRegistryLogin(context, opts) {
647
+ let roleArn = opts.role;
648
+ if (opts.publish) {
649
+ let ssmArn;
650
+ if (!opts.role) {
651
+ if (!context) {
652
+ throw new MeshCliError(
653
+ '`--publish` requires a platform context, e.g. "mesh registry login mesh.dev --publish".'
654
+ );
655
+ }
656
+ const parsed = parseTenantEnv(context);
657
+ if (!parsed) {
658
+ throw new MeshCliError(
659
+ `Could not parse tenant/env from context "${context}" (expected "<tenant>.<env>").`
660
+ );
661
+ }
662
+ const registryExport = await getRegistrySsmExport(parsed.tenant, parsed.env);
663
+ ssmArn = registryExport?.publisherRoleArn;
664
+ }
665
+ try {
666
+ roleArn = resolvePublisherRoleArn({ roleArnFlag: opts.role, ssmArn });
667
+ } catch (err) {
668
+ throw new MeshCliError(err.message);
669
+ }
670
+ logInfo(`Publisher role resolved: ${roleArn}`);
671
+ }
672
+ let outcome;
673
+ if (opts.publish) {
674
+ outcome = { kind: "skipped-flag", flag: "--publish" };
675
+ } else if (opts.profile) {
676
+ outcome = { kind: "skipped-profile", profile: opts.profile };
677
+ } else {
678
+ if (context && opts.broker !== false && !opts.ci) logInfo(CONTEXT_IGNORED_NOTICE);
679
+ const attempt = await tryBrokerLogin(opts);
680
+ if ("grant" in attempt) {
681
+ const { path: npmrcPath } = applyGrantToUserNpmrc(attempt.grant);
682
+ logSuccess(
683
+ `Registry token issued by the Mesh broker \u2192 ${npmrcPath}` + (attempt.grant.expiresAt ? ` (expires ${attempt.grant.expiresAt})` : "")
684
+ );
685
+ if (shouldRepairUserNpmrc(opts)) {
686
+ const repair = repairUnscopedRegistry();
687
+ if (repair.removed) {
688
+ logWarn(
689
+ "Removed an unscoped CodeArtifact `registry=` from ~/.npmrc \u2014 it routed ALL npm traffic (public packages included) through CodeArtifact."
690
+ );
691
+ logInfo(` removed: ${repair.removed.trim()}`);
692
+ if (repair.backupPath) logInfo(` backup: ${repair.backupPath}`);
693
+ }
694
+ }
695
+ if (!opts.ci && !opts.skipNpmrc) {
696
+ const result = ensureProjectNpmrc(attempt.grant.endpoint);
697
+ if (result.created) {
698
+ logSuccess(`Created ${result.path} with ${CA_NAMESPACE} registry scope`);
699
+ } else if (result.updated) {
700
+ logSuccess(`Updated ${CA_NAMESPACE} registry in ${result.path}`);
701
+ } else {
702
+ logInfo(`${result.path} already configured`);
703
+ }
704
+ }
705
+ logSuccess(`Ready \u2014 run pnpm install to fetch ${CA_NAMESPACE} packages`);
706
+ return;
707
+ }
708
+ outcome = attempt.outcome;
709
+ }
710
+ let env;
711
+ if (opts.profile) {
712
+ ensureSsoProfile(opts.profile);
713
+ env = { ...process.env, AWS_PROFILE: opts.profile };
714
+ for (const key of ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]) {
715
+ delete env[key];
716
+ }
717
+ } else {
718
+ const awsCreds = resolveCredentials(context, roleArn);
719
+ env = Object.keys(awsCreds).length > 0 ? { ...process.env, ...awsCreds } : void 0;
720
+ }
721
+ logInfo("Authenticating with CodeArtifact...");
722
+ if (!codeartifactLogin(env)) {
723
+ throw new MeshCliError(describeAwsChainFailure(outcome));
724
+ }
725
+ logSuccess("CodeArtifact token refreshed (valid for 12 hours)");
726
+ if (shouldRepairUserNpmrc(opts)) {
727
+ const repair = repairUnscopedRegistry();
728
+ if (repair.removed) {
729
+ logWarn(
730
+ `Removed an unscoped CodeArtifact \`registry=\` from ~/.npmrc \u2014 it routed ALL npm traffic (public packages included) through CodeArtifact, so every install broke when the 12h token expired. ${CA_NAMESPACE} stays scoped; public packages now go to npmjs.`
731
+ );
732
+ logInfo(` removed: ${repair.removed.trim()}`);
733
+ if (repair.backupPath) logInfo(` backup: ${repair.backupPath}`);
734
+ } else if (repair.error) {
735
+ logWarn(`Could not repair ~/.npmrc (${repair.error}) \u2014 remove any unscoped \`registry=\` line by hand.`);
736
+ }
737
+ }
738
+ const endpoint = getEndpoint(env);
739
+ if (!endpoint) {
740
+ logWarn("Could not retrieve registry endpoint \u2014 .npmrc scope not configured");
741
+ logInfo("You may need to manually add to .npmrc:");
742
+ logInfo(` ${CA_NAMESPACE}:registry=https://mesh-platform-<ACCOUNT>.d.codeartifact.${CA_REGION}.amazonaws.com/npm/${CA_REPOSITORY}/`);
743
+ return;
744
+ }
745
+ if (!opts.ci && !opts.skipNpmrc) {
746
+ const result = ensureProjectNpmrc(endpoint);
747
+ if (result.created) {
748
+ logSuccess(`Created ${result.path} with ${CA_NAMESPACE} registry scope`);
749
+ } else if (result.updated) {
750
+ logSuccess(`Updated ${CA_NAMESPACE} registry in ${result.path}`);
751
+ } else {
752
+ logInfo(`${result.path} already configured`);
753
+ }
754
+ }
755
+ logSuccess(`Ready \u2014 run pnpm install to fetch ${CA_NAMESPACE} packages`);
756
+ }
757
+ function describeAwsChainFailure(outcome) {
758
+ switch (outcome.kind) {
759
+ case "skipped-profile":
760
+ return `AWS profile '${outcome.profile}' cannot read the Mesh registry (aws codeartifact login failed).
761
+
762
+ --profile is for people who DEPLOY with an AWS account that is allowed to read the registry
763
+ (Trabian's mesh-dev profile, or a tenant deployer role). Any other profile fails here.
764
+
765
+ To install packages you do not need AWS at all \u2014 drop the flag:
766
+ mesh registry login`;
767
+ case "unavailable":
768
+ return `Registry broker unavailable (${outcome.detail}) and this machine has no AWS path either.
769
+
770
+ Retry in a minute: mesh registry login
771
+ If you deploy and have an AWS account: mesh registry login --profile mesh-dev`;
772
+ case "no-session":
773
+ return `No registry session on this machine (${outcome.detail}) and the AWS chain failed too.
774
+
775
+ Sign in once \u2014 on a headless box: mesh registry login --device
776
+ In CI with AWS creds: mesh registry login --ci`;
777
+ case "skipped-flag":
778
+ return `CodeArtifact login failed with the AWS credential chain (${outcome.flag} skips the registry broker).
779
+
780
+ This path needs AWS credentials that can read the registry \u2014 ambient keys, a live SSO
781
+ session, or --profile <deployer-profile>.
782
+
783
+ To install packages you do not need AWS at all \u2014 drop the flag:
784
+ mesh registry login`;
785
+ }
786
+ }
787
+ function registerRegistryCommands(program) {
788
+ const registry = program.command("registry").description(
789
+ "PACKAGE access: the sign-in that makes @mesh-tech/* installable. One global identity \u2014 no AWS account, no platform, no `<tenant>.<env>` context. (`mesh login <tenant>.<env>` is the other one; that is for deploying to or operating a platform.)"
790
+ );
791
+ registry.command("login").description(
792
+ "Sign in to the Mesh package registry (browser, or device code with --device) and configure ~/.npmrc + the project .npmrc. Needs no platform context and no AWS account."
793
+ ).argument(
794
+ "[context]",
795
+ "Platform context \u2014 only meaningful with --publish (publisher role from that context's SSM export) or on the explicit AWS chain; ignored on the default read path, which is global"
796
+ ).option("--device", "Sign in with the device-code flow (headless box, SSH, no browser)").option("--role <arn>", "IAM role ARN for registry access").option("--ci", "CI mode \u2014 AWS chain only (ambient credentials), refresh token only, skip project .npmrc setup").option("--skip-npmrc", "Skip project .npmrc setup").option("--profile <name>", "AWS SSO profile of an account allowed to READ the registry (deployers only \u2014 e.g. mesh-dev); skips the broker, auto-runs `aws sso login` when the session is stale").option(
797
+ "--no-broker",
798
+ "Skip the Zitadel-gated registry broker and go straight to the AWS credential chain"
799
+ ).option(
800
+ "--publish",
801
+ "Assume the registry's publisher role for CodeArtifact publish access (opt-in privileged path \u2014 resolves publisherRoleArn from the registry's SSM export unless --role is given)"
802
+ ).action(async (context, opts) => {
803
+ await runRegistryLogin(context, opts);
804
+ });
805
+ registry.command("logout").description("Forget the package-registry session (platform logins under `mesh login` are untouched)").action(() => {
806
+ clearRegistrySession();
807
+ logSuccess("Registry session cleared. ~/.npmrc is untouched \u2014 its token expires on its own within 12 hours.");
808
+ });
809
+ registry.command("status").description("Check current registry authentication status \u2014 the ~/.npmrc token and the registry session").action(async () => {
810
+ const code = await runRegistryStatus();
811
+ if (code !== 0) process.exit(code);
812
+ });
813
+ registerRegistryPublish(registry);
814
+ }
815
+
816
+ export {
817
+ codeartifactLogin,
818
+ resolvePublisherRoleArn,
819
+ resolveCredentials,
820
+ applyGrantToUserNpmrc,
821
+ describeBrokerFailure,
822
+ RegistryNotAuthorizedError,
823
+ registrySignInAllowed,
824
+ shouldRemintAfter,
825
+ NOT_AUTHORIZED_RERUN,
826
+ CONTEXT_IGNORED_NOTICE,
827
+ shouldRepairUserNpmrc,
828
+ repairUnscopedRegistry,
829
+ renderRegistrySessionLine,
830
+ checkExistingToken,
831
+ runRegistryStatus,
832
+ runRegistryLogin,
833
+ describeAwsChainFailure,
834
+ registerRegistryCommands
835
+ };