@mesh-tech/mesh-cli 0.20.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/bin/mesh.js +186 -15
- package/dist/bin/mesh.js.map +4 -4
- package/dist/build-info.json +2 -2
- package/dist/src/commands/app-check.d.ts +1 -1
- package/dist/src/commands/app-check.d.ts.map +1 -1
- package/dist/src/commands/app-check.js +13 -0
- package/dist/src/commands/app-check.js.map +1 -1
- package/dist/src/commands/local/hub-local.d.ts +35 -0
- package/dist/src/commands/local/hub-local.d.ts.map +1 -1
- package/dist/src/commands/local/hub-local.js +61 -1
- package/dist/src/commands/local/hub-local.js.map +1 -1
- package/dist/src/commands/local/index.d.ts.map +1 -1
- package/dist/src/commands/local/index.js +15 -1
- package/dist/src/commands/local/index.js.map +1 -1
- package/dist/src/commands/local/seed-hub-catalog.d.ts +33 -0
- package/dist/src/commands/local/seed-hub-catalog.d.ts.map +1 -0
- package/dist/src/commands/local/seed-hub-catalog.js +90 -0
- package/dist/src/commands/local/seed-hub-catalog.js.map +1 -0
- package/dist/src/commands/login.d.ts +12 -0
- package/dist/src/commands/login.d.ts.map +1 -1
- package/dist/src/commands/login.js +52 -7
- package/dist/src/commands/login.js.map +1 -1
- package/dist/src/commands/registry.d.ts +22 -3
- package/dist/src/commands/registry.d.ts.map +1 -1
- package/dist/src/commands/registry.js +36 -4
- package/dist/src/commands/registry.js.map +1 -1
- package/dist/src/commands/temporal.js +20 -6
- package/dist/src/commands/temporal.js.map +2 -2
- package/dist/src/utils/aws-auth.d.ts +19 -0
- package/dist/src/utils/aws-auth.d.ts.map +1 -1
- package/dist/src/utils/aws-auth.js +30 -9
- package/dist/src/utils/aws-auth.js.map +1 -1
- package/dist/src/utils/registry-identity.d.ts +35 -0
- package/dist/src/utils/registry-identity.d.ts.map +1 -1
- package/dist/src/utils/registry-identity.js +47 -1
- package/dist/src/utils/registry-identity.js.map +1 -1
- package/package.json +2 -2
- package/skills/core/SKILL.md +1 -1
- package/templates/api-auth/index.ts.hbs +3 -1
- package/templates/api-role-gating/index.ts.hbs +3 -1
package/README.md
CHANGED
|
@@ -64,7 +64,9 @@ trip).
|
|
|
64
64
|
**If your account does not hold the grant yet**, the command tells you so and
|
|
65
65
|
prints the access-request URL the platform returned (or names the admin to
|
|
66
66
|
ask). Contact your Mesh contact to have **registry access granted to your
|
|
67
|
-
user**, then run `mesh registry login` again
|
|
67
|
+
user**, then run `mesh registry login` again — it re-mints your sign-in so the
|
|
68
|
+
new role is in the token the broker sees. If it still says not authorized,
|
|
69
|
+
start a fresh sign-in: `mesh registry logout && mesh registry login`.
|
|
68
70
|
|
|
69
71
|
Everything after this point — installing `@mesh-tech/*` packages, scaffolding
|
|
70
72
|
an app, and the local docs server — depends on this step. `mesh init` (next)
|
|
@@ -241,8 +243,10 @@ everywhere else.
|
|
|
241
243
|
|
|
242
244
|
Your account and session are fine; you don't hold the registry grant yet. The
|
|
243
245
|
message names the request URL (or the admin to ask) — see
|
|
244
|
-
[step 2](#2-get-registry-access). Once granted, `mesh registry login` again
|
|
245
|
-
your sign-in
|
|
246
|
+
[step 2](#2-get-registry-access). Once granted, `mesh registry login` again —
|
|
247
|
+
it re-mints your sign-in so the new role reaches the broker (a role only rides
|
|
248
|
+
in tokens issued after it was granted). Still refused?
|
|
249
|
+
`mesh registry logout && mesh registry login` starts a fresh sign-in.
|
|
246
250
|
|
|
247
251
|
### `No registry access on this machine — @mesh-tech packages cannot be installed.`
|
|
248
252
|
|
package/dist/bin/mesh.js
CHANGED
|
@@ -3274,6 +3274,7 @@ __export(login_exports, {
|
|
|
3274
3274
|
discoverConfigForContext: () => discoverConfigForContext,
|
|
3275
3275
|
discoverConfigFromWellKnown: () => discoverConfigFromWellKnown,
|
|
3276
3276
|
ensureLogin: () => ensureLogin,
|
|
3277
|
+
forceRefreshToken: () => forceRefreshToken,
|
|
3277
3278
|
getContextConfig: () => getContextConfig,
|
|
3278
3279
|
getValidToken: () => getValidToken,
|
|
3279
3280
|
isRemoteEnvironment: () => isRemoteEnvironment,
|
|
@@ -3993,22 +3994,43 @@ async function getValidToken(context, opts = {}) {
|
|
|
3993
3994
|
if (tokenStillValid(creds.expiresAt, opts.marginMs ?? 0)) {
|
|
3994
3995
|
return creds.idToken;
|
|
3995
3996
|
}
|
|
3997
|
+
return remintToken(context, config, creds);
|
|
3998
|
+
}
|
|
3999
|
+
async function remintToken(context, config, creds) {
|
|
3996
4000
|
if (!creds.refreshToken) return null;
|
|
4001
|
+
let tokens;
|
|
4002
|
+
try {
|
|
4003
|
+
tokens = await refreshTokens(config.issuer, config.clientId, creds.refreshToken);
|
|
4004
|
+
} catch {
|
|
4005
|
+
return null;
|
|
4006
|
+
}
|
|
4007
|
+
let email = creds.email;
|
|
4008
|
+
try {
|
|
4009
|
+
email = decodeJwtPayload(tokens.id_token).email ?? creds.email;
|
|
4010
|
+
} catch {
|
|
4011
|
+
}
|
|
3997
4012
|
try {
|
|
3998
|
-
const tokens = await refreshTokens(config.issuer, config.clientId, creds.refreshToken);
|
|
3999
|
-
const idPayload = decodeJwtPayload(tokens.id_token);
|
|
4000
4013
|
writeCredentials(context, {
|
|
4001
4014
|
...creds,
|
|
4002
4015
|
idToken: tokens.id_token,
|
|
4003
4016
|
accessToken: tokens.access_token,
|
|
4004
4017
|
refreshToken: tokens.refresh_token ?? creds.refreshToken,
|
|
4005
4018
|
expiresAt: new Date(Date.now() + tokens.expires_in * 1e3).toISOString(),
|
|
4006
|
-
email
|
|
4019
|
+
email
|
|
4007
4020
|
});
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4021
|
+
} catch (err) {
|
|
4022
|
+
logWarn(
|
|
4023
|
+
`The refreshed ${context} session could not be cached (${err instanceof Error ? err.message : String(err)}) \u2014 this run continues on the new token, but the next one may need a fresh sign-in.`
|
|
4024
|
+
);
|
|
4011
4025
|
}
|
|
4026
|
+
return tokens.id_token;
|
|
4027
|
+
}
|
|
4028
|
+
async function forceRefreshToken(context) {
|
|
4029
|
+
const config = getContextConfig(context);
|
|
4030
|
+
if (!config) return null;
|
|
4031
|
+
const creds = readCredentials(context);
|
|
4032
|
+
if (!creds) return null;
|
|
4033
|
+
return remintToken(context, config, creds);
|
|
4012
4034
|
}
|
|
4013
4035
|
async function probeCredentials(context, roleArn) {
|
|
4014
4036
|
const cached = readCredentials(context);
|
|
@@ -4376,7 +4398,9 @@ __export(registry_identity_exports, {
|
|
|
4376
4398
|
isReservedRegistryContext: () => isReservedRegistryContext,
|
|
4377
4399
|
logRegistryIdentity: () => logRegistryIdentity,
|
|
4378
4400
|
readRegistrySession: () => readRegistrySession,
|
|
4401
|
+
refreshRegistrySession: () => refreshRegistrySession,
|
|
4379
4402
|
registrySessionCandidates: () => registrySessionCandidates,
|
|
4403
|
+
registryTokenRoles: () => registryTokenRoles,
|
|
4380
4404
|
resolveRegistryIdentity: () => resolveRegistryIdentity
|
|
4381
4405
|
});
|
|
4382
4406
|
function isReservedRegistryContext(context) {
|
|
@@ -4480,6 +4504,22 @@ async function ensureRegistrySession(opts) {
|
|
|
4480
4504
|
expiresAt: creds.expiresAt
|
|
4481
4505
|
};
|
|
4482
4506
|
}
|
|
4507
|
+
function registryTokenRoles(idToken) {
|
|
4508
|
+
if (!idToken) return [];
|
|
4509
|
+
try {
|
|
4510
|
+
return projectRoleKeys(decodeJwtPayload(idToken)).sort();
|
|
4511
|
+
} catch {
|
|
4512
|
+
return [];
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
async function refreshRegistrySession(context) {
|
|
4516
|
+
const before = registryTokenRoles(readCredentials(context)?.idToken);
|
|
4517
|
+
const idToken = await forceRefreshToken(context);
|
|
4518
|
+
if (!idToken) return { reminted: false, rolesChanged: false };
|
|
4519
|
+
const after = registryTokenRoles(idToken);
|
|
4520
|
+
const same = after.length === before.length && after.every((role, i) => role === before[i]);
|
|
4521
|
+
return { reminted: true, rolesChanged: !same };
|
|
4522
|
+
}
|
|
4483
4523
|
function clearRegistrySession() {
|
|
4484
4524
|
clearCredentials(REGISTRY_CREDENTIAL_KEY);
|
|
4485
4525
|
clearContextConfig(REGISTRY_CREDENTIAL_KEY);
|
|
@@ -4508,6 +4548,7 @@ var init_registry_identity = __esm({
|
|
|
4508
4548
|
"libs/mesh-cli/src/utils/registry-identity.ts"() {
|
|
4509
4549
|
"use strict";
|
|
4510
4550
|
init_login();
|
|
4551
|
+
init_aws_auth();
|
|
4511
4552
|
init_first_party_contexts();
|
|
4512
4553
|
init_registry_broker();
|
|
4513
4554
|
init_errors();
|
|
@@ -4985,16 +5026,20 @@ function selectRoleForCaller(idToken, options) {
|
|
|
4985
5026
|
const adminRoles = options.adminClaimRoles ?? ["mesh.platform:admin"];
|
|
4986
5027
|
const claims = decodeJwtPayload2(idToken);
|
|
4987
5028
|
if (!claims) return options.defaultRole;
|
|
5029
|
+
const claimedRoles = projectRoleKeys(claims);
|
|
5030
|
+
return adminRoles.some((target) => claimedRoles.includes(target)) ? options.adminRole : options.defaultRole;
|
|
5031
|
+
}
|
|
5032
|
+
function projectRoleKeys(claims) {
|
|
5033
|
+
const names = /* @__PURE__ */ new Set();
|
|
4988
5034
|
for (const [key, value] of Object.entries(claims)) {
|
|
4989
5035
|
if (!key.startsWith("urn:zitadel:iam:org:project:")) continue;
|
|
4990
5036
|
if (!key.endsWith(":roles")) continue;
|
|
4991
|
-
if (!value || typeof value !== "object") continue;
|
|
4992
|
-
const
|
|
4993
|
-
|
|
4994
|
-
if (claimedRoles.includes(target)) return options.adminRole;
|
|
5037
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
5038
|
+
for (const name of Object.keys(value)) {
|
|
5039
|
+
if (name) names.add(name);
|
|
4995
5040
|
}
|
|
4996
5041
|
}
|
|
4997
|
-
return
|
|
5042
|
+
return [...names];
|
|
4998
5043
|
}
|
|
4999
5044
|
async function resolveAwsCredentials(roleArn, appRoot, stack) {
|
|
5000
5045
|
const roleName = roleArn.split("/").pop() ?? roleArn;
|
|
@@ -10748,6 +10793,11 @@ function scanApp(root, appRel) {
|
|
|
10748
10793
|
for (const f of files) hit(code, rel(root, f), why);
|
|
10749
10794
|
};
|
|
10750
10795
|
anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC \u2014 grants belong in the Hub (Access \u2192 People), not in a config redeploy");
|
|
10796
|
+
anti(
|
|
10797
|
+
"POINTER_PROJECT_OVERRIDE",
|
|
10798
|
+
RE_POINTER_OVERRIDE,
|
|
10799
|
+
"AppAuthzPointer is given a `zitadel:` override \u2014 an app binds its project with AppEnvironment's zitadelAppProjectId so every consumer of env.zitadel agrees; the override is reserved for the Hub"
|
|
10800
|
+
);
|
|
10751
10801
|
anti(
|
|
10752
10802
|
"PASSWORD_STORE",
|
|
10753
10803
|
RE_PASSWORD,
|
|
@@ -10857,7 +10907,7 @@ function registerAppCommands(program2) {
|
|
|
10857
10907
|
}
|
|
10858
10908
|
});
|
|
10859
10909
|
}
|
|
10860
|
-
var APP_CHECK_RULES, APP_CHECK_CODES, SRC_EXT, SKIP_DIRS, NON_SERVICE_DIRS, RE_HTML, RE_OIDC, RE_COOKIE, RE_LOGGER, RE_EXPOSURE, RE_POINTER, RE_METADATA, RE_IAC_GRANT, RE_PASSWORD, RE_ALLOWLIST, RE_USERS_TABLE;
|
|
10910
|
+
var APP_CHECK_RULES, APP_CHECK_CODES, SRC_EXT, SKIP_DIRS, NON_SERVICE_DIRS, RE_HTML, RE_OIDC, RE_COOKIE, RE_LOGGER, RE_EXPOSURE, RE_POINTER, RE_METADATA, RE_IAC_GRANT, RE_POINTER_OVERRIDE, RE_PASSWORD, RE_ALLOWLIST, RE_USERS_TABLE;
|
|
10861
10911
|
var init_app_check = __esm({
|
|
10862
10912
|
"libs/mesh-cli/src/commands/app-check.ts"() {
|
|
10863
10913
|
"use strict";
|
|
@@ -10929,6 +10979,13 @@ var init_app_check = __esm({
|
|
|
10929
10979
|
remediation: "remove the per-user grant map; grant roles from the Hub (App \u2192 Access \u2192 People)",
|
|
10930
10980
|
scope: "app"
|
|
10931
10981
|
},
|
|
10982
|
+
POINTER_PROJECT_OVERRIDE: {
|
|
10983
|
+
gate: "H",
|
|
10984
|
+
title: "Pointer publishes the env's project",
|
|
10985
|
+
severity: "advisory",
|
|
10986
|
+
remediation: "drop `zitadel:` from AppAuthzPointer and pass `zitadelAppProjectId: identity.projectId` to AppEnvironment \u2014 the override is the Hub's",
|
|
10987
|
+
scope: "app"
|
|
10988
|
+
},
|
|
10932
10989
|
PASSWORD_STORE: {
|
|
10933
10990
|
gate: "H",
|
|
10934
10991
|
title: "No app-side credentials",
|
|
@@ -10976,6 +11033,7 @@ var init_app_check = __esm({
|
|
|
10976
11033
|
RE_POINTER = /AppAuthzPointer/;
|
|
10977
11034
|
RE_METADATA = /compileOpsHubMetadata|ops-hub-metadata|SpiceDBSchema/;
|
|
10978
11035
|
RE_IAC_GRANT = /userId: *"[0-9]{6,}"/;
|
|
11036
|
+
RE_POINTER_OVERRIDE = /\bzitadel: *\{\s*projectId\b/;
|
|
10979
11037
|
RE_PASSWORD = /(^|[^a-z])(bcrypt|argon2)|password_hash|passwordHash|hashPassword|temporaryPassword.*(send|mail)|(send|mail).*temporaryPassword/;
|
|
10980
11038
|
RE_ALLOWLIST = /ADMIN_EMAILS|ALLOWED_EMAILS|allowedEmails|adminEmails/;
|
|
10981
11039
|
RE_USERS_TABLE = /create table (if not exists )?"?(users|roles|user_roles|permissions)"?/i;
|
|
@@ -16159,6 +16217,7 @@ __export(registry_exports, {
|
|
|
16159
16217
|
resolvePublisherRoleArn: () => resolvePublisherRoleArn,
|
|
16160
16218
|
runRegistryLogin: () => runRegistryLogin,
|
|
16161
16219
|
runRegistryStatus: () => runRegistryStatus,
|
|
16220
|
+
shouldRemintAfter: () => shouldRemintAfter,
|
|
16162
16221
|
shouldRepairUserNpmrc: () => shouldRepairUserNpmrc
|
|
16163
16222
|
});
|
|
16164
16223
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
@@ -16422,6 +16481,9 @@ function describeBrokerFailure(failure) {
|
|
|
16422
16481
|
function registrySignInAllowed(opts, hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
16423
16482
|
return Boolean(opts.device) || hasTty;
|
|
16424
16483
|
}
|
|
16484
|
+
function shouldRemintAfter(failure) {
|
|
16485
|
+
return failure.kind === "not-authorized";
|
|
16486
|
+
}
|
|
16425
16487
|
async function tryBrokerLogin(opts) {
|
|
16426
16488
|
if (opts.broker === false) return { outcome: { kind: "skipped-flag", flag: "--no-broker" } };
|
|
16427
16489
|
if (opts.ci) return { outcome: { kind: "skipped-flag", flag: "--ci" } };
|
|
@@ -16461,6 +16523,13 @@ async function tryBrokerLogin(opts) {
|
|
|
16461
16523
|
result = await requestGrant(session.context, identity);
|
|
16462
16524
|
}
|
|
16463
16525
|
}
|
|
16526
|
+
if (!result.ok && shouldRemintAfter(result.failure)) {
|
|
16527
|
+
const { rolesChanged } = await refreshRegistrySession(session.context);
|
|
16528
|
+
if (rolesChanged) {
|
|
16529
|
+
logInfo("Your sign-in now carries a role it did not have \u2014 retrying\u2026");
|
|
16530
|
+
result = await requestGrant(session.context, identity);
|
|
16531
|
+
}
|
|
16532
|
+
}
|
|
16464
16533
|
if (result.ok) return { grant: result.grant };
|
|
16465
16534
|
const { message, hint, fatal } = describeBrokerFailure(result.failure);
|
|
16466
16535
|
if (fatal) {
|
|
@@ -16810,7 +16879,7 @@ var init_registry = __esm({
|
|
|
16810
16879
|
this.name = "RegistryNotAuthorizedError";
|
|
16811
16880
|
}
|
|
16812
16881
|
};
|
|
16813
|
-
NOT_AUTHORIZED_RERUN = "Once granted, run: mesh registry login (your sign-in
|
|
16882
|
+
NOT_AUTHORIZED_RERUN = "Once granted, run: mesh registry login (it re-mints your sign-in; still refused? mesh registry logout && mesh registry login)";
|
|
16814
16883
|
CONTEXT_IGNORED_NOTICE = "The registry is global \u2014 the context argument is no longer needed (ignored).";
|
|
16815
16884
|
}
|
|
16816
16885
|
});
|
|
@@ -17776,6 +17845,32 @@ async function ensureHubImages() {
|
|
|
17776
17845
|
}
|
|
17777
17846
|
return version;
|
|
17778
17847
|
}
|
|
17848
|
+
function tarballNameForImageVersion(version) {
|
|
17849
|
+
if (version.endsWith("-src")) return null;
|
|
17850
|
+
return `mesh-tech-hub-${version.replace(/-r\d+$/, "")}.tgz`;
|
|
17851
|
+
}
|
|
17852
|
+
function readHubCompiledAuthz(version) {
|
|
17853
|
+
const context = path40.join(cacheDir(), `context-${version}`);
|
|
17854
|
+
const file = path40.join(context, HUB_COMPILED_AUTHZ_REL);
|
|
17855
|
+
if (!fs33.existsSync(file)) {
|
|
17856
|
+
const tarball = tarballNameForImageVersion(version);
|
|
17857
|
+
if (!tarball || !fs33.existsSync(path40.join(cacheDir(), tarball))) return null;
|
|
17858
|
+
fs33.mkdirSync(context, { recursive: true });
|
|
17859
|
+
try {
|
|
17860
|
+
execFileSync26(
|
|
17861
|
+
"tar",
|
|
17862
|
+
["-xzf", path40.join(cacheDir(), tarball), "-C", context, "--strip-components", "1", `package/${HUB_COMPILED_AUTHZ_REL}`],
|
|
17863
|
+
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
17864
|
+
);
|
|
17865
|
+
} catch {
|
|
17866
|
+
return null;
|
|
17867
|
+
}
|
|
17868
|
+
if (!fs33.existsSync(file)) return null;
|
|
17869
|
+
}
|
|
17870
|
+
const parsed = JSON.parse(fs33.readFileSync(file, "utf-8"));
|
|
17871
|
+
if (typeof parsed.zed !== "string" || !parsed.metadata || typeof parsed.metadata !== "object") return null;
|
|
17872
|
+
return { zed: parsed.zed, metadata: parsed.metadata };
|
|
17873
|
+
}
|
|
17779
17874
|
function readWorkspaceCatalog(repoRoot2) {
|
|
17780
17875
|
const text = fs33.readFileSync(path40.join(repoRoot2, "pnpm-workspace.yaml"), "utf-8");
|
|
17781
17876
|
const marker = "\ncatalog:\n";
|
|
@@ -17790,7 +17885,7 @@ function readWorkspaceCatalog(repoRoot2) {
|
|
|
17790
17885
|
return catalog;
|
|
17791
17886
|
}
|
|
17792
17887
|
function normalizeHubManifests(contextDir, catalog) {
|
|
17793
|
-
const DEP_FIELDS = ["dependencies", "
|
|
17888
|
+
const DEP_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
17794
17889
|
const touched = [];
|
|
17795
17890
|
for (const entry of fs33.readdirSync(contextDir, { withFileTypes: true })) {
|
|
17796
17891
|
if (!entry.isDirectory()) continue;
|
|
@@ -17798,6 +17893,10 @@ function normalizeHubManifests(contextDir, catalog) {
|
|
|
17798
17893
|
if (!fs33.existsSync(manifest)) continue;
|
|
17799
17894
|
const pkg = JSON.parse(fs33.readFileSync(manifest, "utf-8"));
|
|
17800
17895
|
let changed = false;
|
|
17896
|
+
if (pkg.devDependencies) {
|
|
17897
|
+
delete pkg.devDependencies;
|
|
17898
|
+
changed = true;
|
|
17899
|
+
}
|
|
17801
17900
|
for (const field of DEP_FIELDS) {
|
|
17802
17901
|
const deps = pkg[field];
|
|
17803
17902
|
if (!deps) continue;
|
|
@@ -17873,7 +17972,7 @@ async function buildHubImagesFromSource(repoRoot2) {
|
|
|
17873
17972
|
}
|
|
17874
17973
|
return version;
|
|
17875
17974
|
}
|
|
17876
|
-
var execFileAsync2, HUB_PACKAGE, HUB_IMAGES, LOCAL_IMAGE_REV, HUB_AUTH_IMAGE;
|
|
17975
|
+
var execFileAsync2, HUB_PACKAGE, HUB_IMAGES, LOCAL_IMAGE_REV, HUB_AUTH_IMAGE, HUB_COMPILED_AUTHZ_REL;
|
|
17877
17976
|
var init_hub_local = __esm({
|
|
17878
17977
|
"libs/mesh-cli/src/commands/local/hub-local.ts"() {
|
|
17879
17978
|
"use strict";
|
|
@@ -17887,6 +17986,66 @@ var init_hub_local = __esm({
|
|
|
17887
17986
|
HUB_IMAGES = ["mesh-local-hub-api", "mesh-local-hub-ui"];
|
|
17888
17987
|
LOCAL_IMAGE_REV = "r2";
|
|
17889
17988
|
HUB_AUTH_IMAGE = "mesh-local-hub-auth:v7.7.1-r1";
|
|
17989
|
+
HUB_COMPILED_AUTHZ_REL = "api/dist/authz/compiled.json";
|
|
17990
|
+
}
|
|
17991
|
+
});
|
|
17992
|
+
|
|
17993
|
+
// libs/mesh-cli/src/commands/local/seed-hub-catalog.ts
|
|
17994
|
+
function withHubCatalogRefs(pointer, metadataRef) {
|
|
17995
|
+
return {
|
|
17996
|
+
spicedb: { instanceRefs: [] },
|
|
17997
|
+
...pointer,
|
|
17998
|
+
opsHubMetadataRef: metadataRef,
|
|
17999
|
+
mode: typeof pointer.mode === "string" ? pointer.mode : "policy-engine"
|
|
18000
|
+
};
|
|
18001
|
+
}
|
|
18002
|
+
async function publishHubAuthzCatalog(compiled, awsConfig) {
|
|
18003
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
18004
|
+
const ssm = new SSMClient5(awsConfig);
|
|
18005
|
+
await ssm.send(
|
|
18006
|
+
new PutParameterCommand({
|
|
18007
|
+
Name: HUB_OPS_HUB_METADATA_PARAM,
|
|
18008
|
+
Type: "String",
|
|
18009
|
+
// The blob is ~7.5 KB — past the 4 KB standard tier, within Advanced (8 KB),
|
|
18010
|
+
// the tier the platform's Export uses for the same param.
|
|
18011
|
+
Tier: "Advanced",
|
|
18012
|
+
Overwrite: true,
|
|
18013
|
+
Value: JSON.stringify(compiled.metadata),
|
|
18014
|
+
Description: "Hub role catalog \u2014 ops-hub-metadata (local analog of MeshHub's SpiceDBSchema export)"
|
|
18015
|
+
})
|
|
18016
|
+
);
|
|
18017
|
+
let current = {};
|
|
18018
|
+
try {
|
|
18019
|
+
const existing = await ssm.send(new GetParameterCommand2({ Name: HUB_AUTHZ_POINTER_PARAM }));
|
|
18020
|
+
current = JSON.parse(existing.Parameter?.Value ?? "{}");
|
|
18021
|
+
} catch {
|
|
18022
|
+
}
|
|
18023
|
+
await ssm.send(
|
|
18024
|
+
new PutParameterCommand({
|
|
18025
|
+
Name: HUB_AUTHZ_POINTER_PARAM,
|
|
18026
|
+
Type: "String",
|
|
18027
|
+
Overwrite: true,
|
|
18028
|
+
Value: JSON.stringify(withHubCatalogRefs(current, HUB_OPS_HUB_METADATA_PARAM)),
|
|
18029
|
+
Description: "Hub authz pointer (local analog of the platform Pulumi program)"
|
|
18030
|
+
})
|
|
18031
|
+
);
|
|
18032
|
+
return { bundles: catalogBundleNames(compiled.metadata) };
|
|
18033
|
+
}
|
|
18034
|
+
function catalogBundleNames(metadata) {
|
|
18035
|
+
const bundles = metadata.bundles;
|
|
18036
|
+
if (Array.isArray(bundles)) {
|
|
18037
|
+
return bundles.map((b) => b && typeof b === "object" && typeof b.name === "string" ? b.name : null).filter((n) => n !== null);
|
|
18038
|
+
}
|
|
18039
|
+
if (bundles && typeof bundles === "object") return Object.keys(bundles);
|
|
18040
|
+
return [];
|
|
18041
|
+
}
|
|
18042
|
+
var HUB_AUTHZ_POINTER_PARAM, HUB_OPS_HUB_METADATA_PARAM;
|
|
18043
|
+
var init_seed_hub_catalog = __esm({
|
|
18044
|
+
"libs/mesh-cli/src/commands/local/seed-hub-catalog.ts"() {
|
|
18045
|
+
"use strict";
|
|
18046
|
+
init_seed();
|
|
18047
|
+
HUB_AUTHZ_POINTER_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/apps/hub/stacks/local/authz`;
|
|
18048
|
+
HUB_OPS_HUB_METADATA_PARAM = `${HUB_AUTHZ_POINTER_PARAM}/ops-hub-metadata`;
|
|
17890
18049
|
}
|
|
17891
18050
|
});
|
|
17892
18051
|
|
|
@@ -18063,6 +18222,17 @@ Re-running start from here may recreate shared containers with this checkout's c
|
|
|
18063
18222
|
logInfo("Seeding Zitadel (Platform project, Mesh CLI app, Hub auth, test users)\u2026");
|
|
18064
18223
|
const zitadel = await seedZitadel(LOCAL_AWS_CONFIG);
|
|
18065
18224
|
hubAuth = zitadel.hubAuth;
|
|
18225
|
+
if (hubVersion) {
|
|
18226
|
+
const compiled = readHubCompiledAuthz(hubVersion);
|
|
18227
|
+
if (compiled) {
|
|
18228
|
+
const { bundles } = await publishHubAuthzCatalog(compiled, LOCAL_AWS_CONFIG);
|
|
18229
|
+
logSuccess(`Hub role catalog seeded: ${bundles.length} role(s) (${bundles.join(", ")})`);
|
|
18230
|
+
} else {
|
|
18231
|
+
logWarn(
|
|
18232
|
+
`Hub v${hubVersion} ships no compiled role catalog (api/dist/authz/compiled.json) \u2014 Users \u2192 Roles will be empty. Hub \u2265 2.1.0 publishes one.`
|
|
18233
|
+
);
|
|
18234
|
+
}
|
|
18235
|
+
}
|
|
18066
18236
|
try {
|
|
18067
18237
|
const reconciled = await reconcileRegistryFromZitadel();
|
|
18068
18238
|
if (reconciled.tenants.length > 0) {
|
|
@@ -18230,6 +18400,7 @@ var init_local = __esm({
|
|
|
18230
18400
|
init_auth_provision();
|
|
18231
18401
|
init_seed();
|
|
18232
18402
|
init_seed_zitadel();
|
|
18403
|
+
init_seed_hub_catalog();
|
|
18233
18404
|
WAIT_TIMEOUT_MS = 18e4;
|
|
18234
18405
|
WAIT_POLL_MS = 3e3;
|
|
18235
18406
|
}
|