@mesh-tech/mesh-cli 0.18.2 → 0.20.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.
- package/README.md +111 -56
- package/dist/bin/mesh.js +5158 -4302
- package/dist/bin/mesh.js.map +4 -4
- package/dist/build-info.json +2 -2
- package/dist/src/commands/create-app.d.ts +28 -0
- package/dist/src/commands/create-app.d.ts.map +1 -1
- package/dist/src/commands/create-app.js +45 -1
- package/dist/src/commands/create-app.js.map +1 -1
- package/dist/src/commands/dev-doctor.js +2 -2
- package/dist/src/commands/dev-doctor.js.map +1 -1
- package/dist/src/commands/dev.d.ts +7 -0
- package/dist/src/commands/dev.d.ts.map +1 -1
- package/dist/src/commands/dev.js +1 -0
- package/dist/src/commands/dev.js.map +1 -1
- package/dist/src/commands/hub/index.d.ts.map +1 -1
- package/dist/src/commands/hub/index.js +5 -0
- package/dist/src/commands/hub/index.js.map +1 -1
- package/dist/src/commands/init/wizard.d.ts +216 -0
- package/dist/src/commands/init/wizard.d.ts.map +1 -0
- package/dist/src/commands/init/wizard.js +471 -0
- package/dist/src/commands/init/wizard.js.map +1 -0
- package/dist/src/commands/init.d.ts +14 -6
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +41 -15
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/local/auth-provision.d.ts +4 -0
- package/dist/src/commands/local/auth-provision.d.ts.map +1 -1
- package/dist/src/commands/local/auth-provision.js +20 -0
- package/dist/src/commands/local/auth-provision.js.map +1 -1
- package/dist/src/commands/local/dev-local.d.ts +32 -0
- package/dist/src/commands/local/dev-local.d.ts.map +1 -1
- package/dist/src/commands/local/dev-local.js +88 -2
- package/dist/src/commands/local/dev-local.js.map +1 -1
- package/dist/src/commands/local/seed.d.ts +9 -0
- package/dist/src/commands/local/seed.d.ts.map +1 -1
- package/dist/src/commands/local/seed.js +49 -10
- package/dist/src/commands/local/seed.js.map +1 -1
- package/dist/src/commands/login.d.ts +76 -5
- package/dist/src/commands/login.d.ts.map +1 -1
- package/dist/src/commands/login.js +161 -60
- package/dist/src/commands/login.js.map +1 -1
- package/dist/src/commands/registry.d.ts +133 -10
- package/dist/src/commands/registry.d.ts.map +1 -1
- package/dist/src/commands/registry.js +238 -86
- package/dist/src/commands/registry.js.map +1 -1
- package/dist/src/commands/temporal.js +54 -24
- package/dist/src/commands/temporal.js.map +3 -3
- package/dist/src/docs/registry-docs.d.ts.map +1 -1
- package/dist/src/docs/registry-docs.js +8 -3
- package/dist/src/docs/registry-docs.js.map +1 -1
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +5 -1
- package/dist/src/program.js.map +1 -1
- package/dist/src/utils/auth-preflight.d.ts +51 -20
- package/dist/src/utils/auth-preflight.d.ts.map +1 -1
- package/dist/src/utils/auth-preflight.js +48 -21
- package/dist/src/utils/auth-preflight.js.map +1 -1
- package/dist/src/utils/mesh-json.d.ts +58 -0
- package/dist/src/utils/mesh-json.d.ts.map +1 -0
- package/dist/src/utils/mesh-json.js +85 -0
- package/dist/src/utils/mesh-json.js.map +1 -0
- package/dist/src/utils/registry-broker.d.ts +12 -29
- package/dist/src/utils/registry-broker.d.ts.map +1 -1
- package/dist/src/utils/registry-broker.js +12 -29
- package/dist/src/utils/registry-broker.js.map +1 -1
- package/dist/src/utils/registry-identity.d.ts +170 -0
- package/dist/src/utils/registry-identity.d.ts.map +1 -0
- package/dist/src/utils/registry-identity.js +273 -0
- package/dist/src/utils/registry-identity.js.map +1 -0
- package/package.json +3 -2
- package/skills/core/SKILL.md +8 -7
- package/templates/apps-repo/gitignore +4 -0
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
*
|
|
11
11
|
* In containers/SSH sessions, device code flow is used automatically.
|
|
12
12
|
*
|
|
13
|
+
* `mesh login` is for a DEPLOYED platform only — the tenant platform you
|
|
14
|
+
* deploy to, reach the Hub of, or tunnel into. Package-registry access is a
|
|
15
|
+
* separate, global identity owned by `mesh registry login`; a platform login
|
|
16
|
+
* never writes ~/.npmrc, and the reserved `registry` credential key is refused
|
|
17
|
+
* here so the two can never be confused.
|
|
18
|
+
*
|
|
13
19
|
* Usage:
|
|
14
|
-
* mesh login
|
|
15
|
-
* mesh login
|
|
16
|
-
* mesh login
|
|
20
|
+
* mesh login acme.dev — authenticate to the acme dev platform
|
|
21
|
+
* mesh login acme.dev --device — force device code flow
|
|
22
|
+
* mesh login acme.dev --status — show current auth state (Zitadel session if
|
|
17
23
|
* there is one, otherwise whatever the AWS
|
|
18
24
|
* credential chain resolves — SSO or key pair)
|
|
19
|
-
* mesh logout
|
|
25
|
+
* mesh logout acme.dev — clear cached credentials
|
|
20
26
|
*/
|
|
21
27
|
import { Command } from "commander";
|
|
22
28
|
import { type AwsIdentity } from "../utils/aws-auth.js";
|
|
@@ -66,7 +72,29 @@ interface CachedCredentials {
|
|
|
66
72
|
type ConfigFile = Record<string, ContextConfig>;
|
|
67
73
|
type CredentialsFile = Record<string, CachedCredentials>;
|
|
68
74
|
export declare function writeContextConfig(context: string, config: ContextConfig): void;
|
|
75
|
+
/**
|
|
76
|
+
* Forget a cached context config so the next use runs discovery again.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* clearContextConfig("registry"); // `mesh registry logout` — re-discovers the registry next time
|
|
80
|
+
*/
|
|
81
|
+
export declare function clearContextConfig(context: string): void;
|
|
69
82
|
declare function getContextConfig(context: string): ContextConfig | null;
|
|
83
|
+
/**
|
|
84
|
+
* Anonymous discovery against `https://cli.<domain>/.well-known/mesh.json`.
|
|
85
|
+
*
|
|
86
|
+
* `quiet: true` returns the payload WITHOUT caching it under `contextKey` and
|
|
87
|
+
* without the progress lines — for a caller that stores the record itself
|
|
88
|
+
* under a name that is not a platform context (the package registry's
|
|
89
|
+
* reserved key). Warnings still print, because a failed discovery is the
|
|
90
|
+
* caller's problem either way.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const cfg = await discoverConfigFromWellKnown("dev.platform.meshtech.io", "registry", { quiet: true });
|
|
94
|
+
*/
|
|
95
|
+
export declare function discoverConfigFromWellKnown(domain: string, contextKey?: string, opts?: {
|
|
96
|
+
quiet?: boolean;
|
|
97
|
+
}): Promise<ContextConfig | null>;
|
|
70
98
|
declare function readAllCredentials(): CredentialsFile;
|
|
71
99
|
declare function readCredentials(context: string): CachedCredentials | null;
|
|
72
100
|
/**
|
|
@@ -82,6 +110,7 @@ declare function readCredentials(context: string): CachedCredentials | null;
|
|
|
82
110
|
* Exported for unit testing.
|
|
83
111
|
*/
|
|
84
112
|
export declare function atomicWriteFileSync(path: string, data: string, mode: number): void;
|
|
113
|
+
declare function clearCredentials(context: string): void;
|
|
85
114
|
declare function decodeJwtPayload(token: string): Record<string, unknown>;
|
|
86
115
|
/**
|
|
87
116
|
* Run device-code login, re-issuing a fresh code (up to `maxCodes`) if the user
|
|
@@ -142,6 +171,29 @@ export declare function renderAwsFallbackStatus(context: string, reason: string,
|
|
|
142
171
|
exitCode: 0 | 1;
|
|
143
172
|
};
|
|
144
173
|
export declare function registerLoginCommand(program: Command): void;
|
|
174
|
+
/**
|
|
175
|
+
* The credential key the package registry's own session lives under. Not a
|
|
176
|
+
* platform: `mesh login` / `mesh logout` refuse it, and the post-login hints
|
|
177
|
+
* (VPN, tenants) never apply to it. `utils/registry-identity.ts` owns the
|
|
178
|
+
* registry side of this contract and re-exports the key.
|
|
179
|
+
*/
|
|
180
|
+
export declare const RESERVED_REGISTRY_CONTEXT = "registry";
|
|
181
|
+
/** Printed once when `--no-registry` is passed: the flag no longer changes anything. */
|
|
182
|
+
export declare const NO_REGISTRY_DEPRECATED = "--no-registry is deprecated and ignored: mesh login no longer touches the registry.";
|
|
183
|
+
/** Printed after a platform login on a machine with no package-registry access. */
|
|
184
|
+
export declare const REGISTRY_SEPARATE_HINT = "Package registry access is separate from platform sign-in \u2014 run: mesh registry login";
|
|
185
|
+
/**
|
|
186
|
+
* The "no configuration found" explanation for a context that neither SSM,
|
|
187
|
+
* the pinned first-party table, nor the well-known endpoint could resolve —
|
|
188
|
+
* every path tried, then the `config.json` snippet to add by hand.
|
|
189
|
+
*
|
|
190
|
+
* Shared by the `login` command and the `mesh init` wizard's platform step so
|
|
191
|
+
* a discovery failure reads the same wherever it happens.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* logError(renderNoConfigHelp("acme.dev"));
|
|
195
|
+
*/
|
|
196
|
+
export declare function renderNoConfigHelp(context: string): string;
|
|
145
197
|
/**
|
|
146
198
|
* Get a valid access token for the given context, refreshing if needed.
|
|
147
199
|
*
|
|
@@ -261,6 +313,25 @@ export declare function resolveRoleOrExplain(opts: {
|
|
|
261
313
|
} | {
|
|
262
314
|
error: string;
|
|
263
315
|
};
|
|
316
|
+
/**
|
|
317
|
+
* Ensure a valid Zitadel ID token is cached for the given context — refreshing
|
|
318
|
+
* or running the full login flow as needed. Returns null on failure.
|
|
319
|
+
*/
|
|
320
|
+
/**
|
|
321
|
+
* Run the sign-in itself — browser PKCE, or the device-code flow when asked
|
|
322
|
+
* for or when there is no browser to open (`isRemoteEnvironment`) — and cache
|
|
323
|
+
* the resulting session under `context`. Rejects when the user denies, the
|
|
324
|
+
* flow times out, or the issuer refuses.
|
|
325
|
+
*
|
|
326
|
+
* The one sign-in routine every caller shares: the `login` command, on-demand
|
|
327
|
+
* login from other commands, and the package registry's own session.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* await runLoginFlow("acme.dev", config, { device: false });
|
|
331
|
+
*/
|
|
332
|
+
export declare function runLoginFlow(context: string, config: ContextConfig, opts?: {
|
|
333
|
+
device?: boolean;
|
|
334
|
+
}): Promise<void>;
|
|
264
335
|
/**
|
|
265
336
|
* Ensure a valid login session for `context`, running the login flow (device
|
|
266
337
|
* code in remote/non-TTY, browser callback otherwise) if none is cached. Returns
|
|
@@ -287,5 +358,5 @@ export declare function readAllContextConfigs(): ConfigFile;
|
|
|
287
358
|
* sign-in, which is why this leg had no coverage above the unit tier.
|
|
288
359
|
*/
|
|
289
360
|
export declare function discoverConfigForContext(context: string): Promise<ContextConfig | null>;
|
|
290
|
-
export { readCredentials, readAllCredentials, getContextConfig, decodeJwtPayload, getValidToken };
|
|
361
|
+
export { readCredentials, readAllCredentials, getContextConfig, decodeJwtPayload, getValidToken, clearCredentials };
|
|
291
362
|
//# sourceMappingURL=login.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/login.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
*
|
|
11
11
|
* In containers/SSH sessions, device code flow is used automatically.
|
|
12
12
|
*
|
|
13
|
+
* `mesh login` is for a DEPLOYED platform only — the tenant platform you
|
|
14
|
+
* deploy to, reach the Hub of, or tunnel into. Package-registry access is a
|
|
15
|
+
* separate, global identity owned by `mesh registry login`; a platform login
|
|
16
|
+
* never writes ~/.npmrc, and the reserved `registry` credential key is refused
|
|
17
|
+
* here so the two can never be confused.
|
|
18
|
+
*
|
|
13
19
|
* Usage:
|
|
14
|
-
* mesh login
|
|
15
|
-
* mesh login
|
|
16
|
-
* mesh login
|
|
20
|
+
* mesh login acme.dev — authenticate to the acme dev platform
|
|
21
|
+
* mesh login acme.dev --device — force device code flow
|
|
22
|
+
* mesh login acme.dev --status — show current auth state (Zitadel session if
|
|
17
23
|
* there is one, otherwise whatever the AWS
|
|
18
24
|
* credential chain resolves — SSO or key pair)
|
|
19
|
-
* mesh logout
|
|
25
|
+
* mesh logout acme.dev — clear cached credentials
|
|
20
26
|
*/
|
|
21
27
|
import * as http from "http";
|
|
22
28
|
import * as crypto from "crypto";
|
|
@@ -24,6 +30,7 @@ import * as fs from "fs";
|
|
|
24
30
|
import * as path from "path";
|
|
25
31
|
import { execFileSync } from "child_process";
|
|
26
32
|
import { logError, logInfo, logSuccess, logWarn } from "../utils/log.js";
|
|
33
|
+
import { MeshCliError } from "../utils/errors.js";
|
|
27
34
|
import { assumeRoleCredentials, assumeRoleWithWebIdentity, describeAssumeFailure, probeAwsIdentity, renderCredentialProcessProfile, tokenIssuer, resolveStableMeshBin, selectRoleForCaller, toCredentialProcessJson, upsertManagedAwsConfigSection, } from "../utils/aws-auth.js";
|
|
28
35
|
import { firstPartyDomainFor } from "../utils/first-party-contexts.js";
|
|
29
36
|
import { tailscaleBackendState } from "./vpn/index.js";
|
|
@@ -52,6 +59,20 @@ export function writeContextConfig(context, config) {
|
|
|
52
59
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
53
60
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
54
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Forget a cached context config so the next use runs discovery again.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* clearContextConfig("registry"); // `mesh registry logout` — re-discovers the registry next time
|
|
67
|
+
*/
|
|
68
|
+
export function clearContextConfig(context) {
|
|
69
|
+
const existing = readConfig();
|
|
70
|
+
if (!(context in existing))
|
|
71
|
+
return;
|
|
72
|
+
delete existing[context];
|
|
73
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
74
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
75
|
+
}
|
|
55
76
|
function getContextConfig(context) {
|
|
56
77
|
const config = readConfig();
|
|
57
78
|
return config[context] ?? null;
|
|
@@ -151,9 +172,10 @@ async function discoverConfigFromSsm(context) {
|
|
|
151
172
|
* `dev.platform.meshtech.io` but must be cached under `mesh.dev`, or the very
|
|
152
173
|
* next command re-runs discovery and the alias buys nothing.
|
|
153
174
|
*/
|
|
154
|
-
async function discoverConfig(domain, contextKey = domain) {
|
|
175
|
+
async function discoverConfig(domain, contextKey = domain, opts = {}) {
|
|
155
176
|
const url = `https://cli.${domain}/.well-known/mesh.json`;
|
|
156
|
-
|
|
177
|
+
if (!opts.quiet)
|
|
178
|
+
logInfo(`Attempting discovery from ${url}...`);
|
|
157
179
|
try {
|
|
158
180
|
const controller = new AbortController();
|
|
159
181
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
@@ -177,8 +199,10 @@ async function discoverConfig(domain, contextKey = domain) {
|
|
|
177
199
|
config.vpnJoinBroker = data.vpnJoinBroker;
|
|
178
200
|
if (typeof data.registryBroker === "string")
|
|
179
201
|
config.registryBroker = data.registryBroker;
|
|
180
|
-
|
|
181
|
-
|
|
202
|
+
if (!opts.quiet) {
|
|
203
|
+
writeContextConfig(contextKey, config);
|
|
204
|
+
logSuccess(`Discovered platform configuration for ${contextKey}`);
|
|
205
|
+
}
|
|
182
206
|
return config;
|
|
183
207
|
}
|
|
184
208
|
catch (err) {
|
|
@@ -192,6 +216,21 @@ async function discoverConfig(domain, contextKey = domain) {
|
|
|
192
216
|
return null;
|
|
193
217
|
}
|
|
194
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Anonymous discovery against `https://cli.<domain>/.well-known/mesh.json`.
|
|
221
|
+
*
|
|
222
|
+
* `quiet: true` returns the payload WITHOUT caching it under `contextKey` and
|
|
223
|
+
* without the progress lines — for a caller that stores the record itself
|
|
224
|
+
* under a name that is not a platform context (the package registry's
|
|
225
|
+
* reserved key). Warnings still print, because a failed discovery is the
|
|
226
|
+
* caller's problem either way.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* const cfg = await discoverConfigFromWellKnown("dev.platform.meshtech.io", "registry", { quiet: true });
|
|
230
|
+
*/
|
|
231
|
+
export function discoverConfigFromWellKnown(domain, contextKey = domain, opts = {}) {
|
|
232
|
+
return discoverConfig(domain, contextKey, opts);
|
|
233
|
+
}
|
|
195
234
|
function readAllCredentials() {
|
|
196
235
|
if (!fs.existsSync(CREDENTIALS_FILE))
|
|
197
236
|
return {};
|
|
@@ -581,6 +620,9 @@ export function isRemoteEnvironment() {
|
|
|
581
620
|
* Non-blocking — never fails or delays the login flow.
|
|
582
621
|
*/
|
|
583
622
|
function hintVpnIfDisconnected(context) {
|
|
623
|
+
// The package registry is not a platform: no VPN sits behind it.
|
|
624
|
+
if (context === RESERVED_REGISTRY_CONTEXT)
|
|
625
|
+
return;
|
|
584
626
|
try {
|
|
585
627
|
const state = tailscaleBackendState();
|
|
586
628
|
if (state !== "Running") {
|
|
@@ -748,8 +790,9 @@ export function registerLoginCommand(program) {
|
|
|
748
790
|
.option("--credential-process", "Print AWS credential_process JSON (used by mesh dev's temp profile); refreshes the Zitadel token as needed. Requires --role or a cached defaultRole.")
|
|
749
791
|
.option("--role <arn>", "IAM role ARN to assume via web identity (used with --export). Saved as the default for this context so future --export runs can omit it. Resolution order: --role > MESH_AWS_ROLE > cached defaultRole.")
|
|
750
792
|
.option("--region <region>", "AWS region to include in the exported AWS_REGION. Defaults to AWS_REGION env or us-east-2.")
|
|
751
|
-
.option("--no-registry", "
|
|
793
|
+
.option("--no-registry", "Deprecated and ignored — mesh login no longer touches the package registry (that is `mesh registry login`)")
|
|
752
794
|
.action(async (context, opts) => {
|
|
795
|
+
refuseReservedRegistryContext(context, "login");
|
|
753
796
|
let config = getContextConfig(context);
|
|
754
797
|
if (!config) {
|
|
755
798
|
// One chain, shared with `ensureLogin` — see discoverConfigGuarded.
|
|
@@ -758,25 +801,7 @@ export function registerLoginCommand(program) {
|
|
|
758
801
|
// laptop could resolve a short context at all.
|
|
759
802
|
config = await discoverConfigGuarded(context);
|
|
760
803
|
if (!config) {
|
|
761
|
-
|
|
762
|
-
logError(`No configuration found for "${context}".\n\n` +
|
|
763
|
-
`Tried:\n` +
|
|
764
|
-
` 1. SSM: /mesh-platform/${context.split(".")[0]}/${context.split(".")[1]}/platform/zitadel\n` +
|
|
765
|
-
` (requires AWS SSO login + read access)\n` +
|
|
766
|
-
(pinned
|
|
767
|
-
? ` 2. HTTPS: https://cli.${pinned}/.well-known/mesh.json (pinned first-party domain)\n\n`
|
|
768
|
-
: ` 2. HTTPS: https://cli.${context}/.well-known/mesh.json\n` +
|
|
769
|
-
` (only attempted for a full domain, or a known first-party context)\n\n`) +
|
|
770
|
-
`To fix:\n` +
|
|
771
|
-
` - Use the platform's full domain: mesh login <env>.<tenant>.meshtech.io\n` +
|
|
772
|
-
` - Or, if you have AWS access, ensure SSO is active: aws sso login --profile <profile>\n` +
|
|
773
|
-
` - Or add config manually to ${CONFIG_FILE}:\n\n` +
|
|
774
|
-
` {\n` +
|
|
775
|
-
` "${context}": {\n` +
|
|
776
|
-
` "issuer": "https://identity.<env>.<your-platform-domain>",\n` +
|
|
777
|
-
` "clientId": "<cli-oidc-client-id>"\n` +
|
|
778
|
-
` }\n` +
|
|
779
|
-
` }\n`);
|
|
804
|
+
logError(renderNoConfigHelp(context));
|
|
780
805
|
process.exit(1);
|
|
781
806
|
}
|
|
782
807
|
}
|
|
@@ -792,45 +817,106 @@ export function registerLoginCommand(program) {
|
|
|
792
817
|
await credentialProcessAwsCredentials(context, config, opts);
|
|
793
818
|
return;
|
|
794
819
|
}
|
|
820
|
+
if (opts.registry === false) {
|
|
821
|
+
logWarn(NO_REGISTRY_DEPRECATED);
|
|
822
|
+
}
|
|
795
823
|
try {
|
|
796
|
-
|
|
797
|
-
if (useDevice) {
|
|
798
|
-
logInfo("Using device code flow");
|
|
799
|
-
await deviceCodeLogin(context, config);
|
|
800
|
-
}
|
|
801
|
-
else {
|
|
802
|
-
await login(context, config);
|
|
803
|
-
}
|
|
824
|
+
await runLoginFlow(context, config, opts);
|
|
804
825
|
}
|
|
805
826
|
catch (err) {
|
|
806
827
|
logError(`Login failed: ${err.message}`);
|
|
807
828
|
process.exit(1);
|
|
808
829
|
}
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
|
|
813
|
-
try {
|
|
814
|
-
logInfo("Refreshing CodeArtifact registry auth (skip with --no-registry)…");
|
|
815
|
-
const { runRegistryLogin } = await import("./registry.js");
|
|
816
|
-
await runRegistryLogin(context, {});
|
|
817
|
-
}
|
|
818
|
-
catch (err) {
|
|
819
|
-
logWarn(`Registry auth failed (login itself succeeded): ${err.message}` +
|
|
820
|
-
` — retry with: mesh registry login ${context}`
|
|
821
|
-
+ ` (add --profile mesh-dev only if you have an AWS account and the broker is unavailable)`);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
830
|
+
// Platform sign-in and package-registry access are two identities. A
|
|
831
|
+
// platform login never writes ~/.npmrc; it only points at the command
|
|
832
|
+
// that does, and only when this machine has no registry access yet.
|
|
833
|
+
await hintRegistryIfMissing();
|
|
824
834
|
});
|
|
825
835
|
program
|
|
826
836
|
.command("logout")
|
|
827
837
|
.description("Clear cached Zitadel credentials")
|
|
828
838
|
.argument("<context>", 'Platform context (e.g., "mesh.dev")')
|
|
829
839
|
.action((context) => {
|
|
840
|
+
refuseReservedRegistryContext(context, "logout");
|
|
830
841
|
clearCredentials(context);
|
|
831
842
|
logSuccess(`Logged out of ${context}. Credentials cleared.`);
|
|
832
843
|
});
|
|
833
844
|
}
|
|
845
|
+
/**
|
|
846
|
+
* The credential key the package registry's own session lives under. Not a
|
|
847
|
+
* platform: `mesh login` / `mesh logout` refuse it, and the post-login hints
|
|
848
|
+
* (VPN, tenants) never apply to it. `utils/registry-identity.ts` owns the
|
|
849
|
+
* registry side of this contract and re-exports the key.
|
|
850
|
+
*/
|
|
851
|
+
export const RESERVED_REGISTRY_CONTEXT = "registry";
|
|
852
|
+
/** Printed once when `--no-registry` is passed: the flag no longer changes anything. */
|
|
853
|
+
export const NO_REGISTRY_DEPRECATED = "--no-registry is deprecated and ignored: mesh login no longer touches the registry.";
|
|
854
|
+
/** Printed after a platform login on a machine with no package-registry access. */
|
|
855
|
+
export const REGISTRY_SEPARATE_HINT = "Package registry access is separate from platform sign-in — run: mesh registry login";
|
|
856
|
+
/**
|
|
857
|
+
* The reserved `registry` credential key is not a platform. `mesh login
|
|
858
|
+
* registry` would try to discover a platform named "registry" and cache a
|
|
859
|
+
* platform session on top of the package registry's; refuse it and name the
|
|
860
|
+
* command that owns that key.
|
|
861
|
+
*/
|
|
862
|
+
function refuseReservedRegistryContext(context, verb) {
|
|
863
|
+
if (context !== RESERVED_REGISTRY_CONTEXT)
|
|
864
|
+
return;
|
|
865
|
+
throw new MeshCliError(`"registry" is the package registry's own session, not a platform context.`, { remediation: { command: `mesh registry ${verb}` } });
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* After a successful platform login: one line pointing at `mesh registry
|
|
869
|
+
* login` when the machine has neither a registry session nor a working
|
|
870
|
+
* `~/.npmrc` token. Silent when either exists, and silent when the registry
|
|
871
|
+
* cannot be reached (a network fault is not a missing grant).
|
|
872
|
+
*/
|
|
873
|
+
async function hintRegistryIfMissing() {
|
|
874
|
+
try {
|
|
875
|
+
const { readRegistrySession } = await import("../utils/registry-identity.js");
|
|
876
|
+
if (readRegistrySession())
|
|
877
|
+
return;
|
|
878
|
+
const { probeRegistryToken } = await import("../utils/auth-preflight.js");
|
|
879
|
+
const probe = await probeRegistryToken();
|
|
880
|
+
if (probe.state === "missing" || probe.state === "expired") {
|
|
881
|
+
logInfo(REGISTRY_SEPARATE_HINT);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
catch {
|
|
885
|
+
// A hint must never fail a login that already succeeded.
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The "no configuration found" explanation for a context that neither SSM,
|
|
890
|
+
* the pinned first-party table, nor the well-known endpoint could resolve —
|
|
891
|
+
* every path tried, then the `config.json` snippet to add by hand.
|
|
892
|
+
*
|
|
893
|
+
* Shared by the `login` command and the `mesh init` wizard's platform step so
|
|
894
|
+
* a discovery failure reads the same wherever it happens.
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* logError(renderNoConfigHelp("acme.dev"));
|
|
898
|
+
*/
|
|
899
|
+
export function renderNoConfigHelp(context) {
|
|
900
|
+
const pinned = firstPartyDomainFor(context);
|
|
901
|
+
return (`No configuration found for "${context}".\n\n` +
|
|
902
|
+
`Tried:\n` +
|
|
903
|
+
` 1. SSM: /mesh-platform/${context.split(".")[0]}/${context.split(".")[1]}/platform/zitadel\n` +
|
|
904
|
+
` (requires AWS SSO login + read access)\n` +
|
|
905
|
+
(pinned
|
|
906
|
+
? ` 2. HTTPS: https://cli.${pinned}/.well-known/mesh.json (pinned first-party domain)\n\n`
|
|
907
|
+
: ` 2. HTTPS: https://cli.${context}/.well-known/mesh.json\n` +
|
|
908
|
+
` (only attempted for a full domain, or a known first-party context)\n\n`) +
|
|
909
|
+
`To fix:\n` +
|
|
910
|
+
` - Use the platform's full domain: mesh login <env>.<tenant>.meshtech.io\n` +
|
|
911
|
+
` - Or, if you have AWS access, ensure SSO is active: aws sso login --profile <profile>\n` +
|
|
912
|
+
` - Or add config manually to ${CONFIG_FILE}:\n\n` +
|
|
913
|
+
` {\n` +
|
|
914
|
+
` "${context}": {\n` +
|
|
915
|
+
` "issuer": "https://identity.<env>.<your-platform-domain>",\n` +
|
|
916
|
+
` "clientId": "<cli-oidc-client-id>"\n` +
|
|
917
|
+
` }\n` +
|
|
918
|
+
` }\n`);
|
|
919
|
+
}
|
|
834
920
|
// =============================================================================
|
|
835
921
|
// Exports (for use by other mesh commands)
|
|
836
922
|
// =============================================================================
|
|
@@ -1017,20 +1103,35 @@ export function resolveRoleOrExplain(opts, config) {
|
|
|
1017
1103
|
* Ensure a valid Zitadel ID token is cached for the given context — refreshing
|
|
1018
1104
|
* or running the full login flow as needed. Returns null on failure.
|
|
1019
1105
|
*/
|
|
1106
|
+
/**
|
|
1107
|
+
* Run the sign-in itself — browser PKCE, or the device-code flow when asked
|
|
1108
|
+
* for or when there is no browser to open (`isRemoteEnvironment`) — and cache
|
|
1109
|
+
* the resulting session under `context`. Rejects when the user denies, the
|
|
1110
|
+
* flow times out, or the issuer refuses.
|
|
1111
|
+
*
|
|
1112
|
+
* The one sign-in routine every caller shares: the `login` command, on-demand
|
|
1113
|
+
* login from other commands, and the package registry's own session.
|
|
1114
|
+
*
|
|
1115
|
+
* @example
|
|
1116
|
+
* await runLoginFlow("acme.dev", config, { device: false });
|
|
1117
|
+
*/
|
|
1118
|
+
export async function runLoginFlow(context, config, opts = {}) {
|
|
1119
|
+
const useDevice = opts.device || isRemoteEnvironment();
|
|
1120
|
+
if (useDevice) {
|
|
1121
|
+
logInfo("Using device code flow");
|
|
1122
|
+
await deviceCodeLogin(context, config);
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
await login(context, config);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1020
1128
|
async function ensureValidToken(context, config, opts) {
|
|
1021
1129
|
let token = await getValidToken(context);
|
|
1022
1130
|
if (token)
|
|
1023
1131
|
return token;
|
|
1024
1132
|
logInfo(`No valid Zitadel session for ${context} — running login flow`);
|
|
1025
1133
|
try {
|
|
1026
|
-
|
|
1027
|
-
if (useDevice) {
|
|
1028
|
-
logInfo("Using device code flow");
|
|
1029
|
-
await deviceCodeLogin(context, config);
|
|
1030
|
-
}
|
|
1031
|
-
else {
|
|
1032
|
-
await login(context, config);
|
|
1033
|
-
}
|
|
1134
|
+
await runLoginFlow(context, config, opts);
|
|
1034
1135
|
}
|
|
1035
1136
|
catch (err) {
|
|
1036
1137
|
logError(`Login failed: ${err.message}`);
|
|
@@ -1268,5 +1369,5 @@ export function readAllContextConfigs() {
|
|
|
1268
1369
|
export async function discoverConfigForContext(context) {
|
|
1269
1370
|
return getContextConfig(context) ?? (await discoverConfigGuarded(context));
|
|
1270
1371
|
}
|
|
1271
|
-
export { readCredentials, readAllCredentials, getContextConfig, decodeJwtPayload, getValidToken };
|
|
1372
|
+
export { readCredentials, readAllCredentials, getContextConfig, decodeJwtPayload, getValidToken, clearCredentials };
|
|
1272
1373
|
//# sourceMappingURL=login.js.map
|