@mesh-tech/mesh-cli 0.13.0 → 0.15.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/dist/bin/mesh.js +940 -277
- package/dist/bin/mesh.js.map +4 -4
- package/dist/build-info.json +2 -2
- package/dist/src/commands/create-app.d.ts +4 -0
- package/dist/src/commands/create-app.d.ts.map +1 -1
- package/dist/src/commands/create-app.js +13 -6
- package/dist/src/commands/create-app.js.map +1 -1
- package/dist/src/commands/dev.d.ts +11 -0
- package/dist/src/commands/dev.d.ts.map +1 -1
- package/dist/src/commands/dev.js +70 -1
- 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 +65 -4
- package/dist/src/commands/hub/index.js.map +1 -1
- package/dist/src/commands/local/auth-provision.d.ts +34 -0
- package/dist/src/commands/local/auth-provision.d.ts.map +1 -1
- package/dist/src/commands/local/auth-provision.js +78 -1
- package/dist/src/commands/local/auth-provision.js.map +1 -1
- package/dist/src/commands/local/hub-local.d.ts +38 -0
- package/dist/src/commands/local/hub-local.d.ts.map +1 -1
- package/dist/src/commands/local/hub-local.js +137 -0
- 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 +31 -3
- package/dist/src/commands/local/index.js.map +1 -1
- package/dist/src/commands/local/seed-zitadel.d.ts +10 -3
- package/dist/src/commands/local/seed-zitadel.d.ts.map +1 -1
- package/dist/src/commands/local/seed-zitadel.js +259 -78
- package/dist/src/commands/local/seed-zitadel.js.map +1 -1
- package/dist/src/commands/local/stack.d.ts +30 -0
- package/dist/src/commands/local/stack.d.ts.map +1 -1
- package/dist/src/commands/local/stack.js +48 -0
- package/dist/src/commands/local/stack.js.map +1 -1
- package/dist/src/commands/secrets/index.d.ts +1 -0
- package/dist/src/commands/secrets/index.d.ts.map +1 -1
- package/dist/src/commands/secrets/index.js +12 -0
- package/dist/src/commands/secrets/index.js.map +1 -1
- package/dist/src/commands/secrets/reindex.d.ts +48 -0
- package/dist/src/commands/secrets/reindex.d.ts.map +1 -0
- package/dist/src/commands/secrets/reindex.js +157 -0
- package/dist/src/commands/secrets/reindex.js.map +1 -0
- package/dist/src/commands/secrets/set.d.ts +63 -0
- package/dist/src/commands/secrets/set.d.ts.map +1 -1
- package/dist/src/commands/secrets/set.js +78 -3
- package/dist/src/commands/secrets/set.js.map +1 -1
- package/fragments/base/index.ts.hbs +9 -0
- package/fragments/base/package.json.hbs +1 -1
- package/fragments/service/api/package.json.hbs +4 -0
- package/fragments/service/api/src/index.ts.hbs +1 -4
- package/fragments/temporal/worker/package.json.hbs +1 -0
- package/fragments/temporal/worker/src/activities.ts.hbs +5 -1
- package/fragments/temporal/worker/src/workflows.ts.hbs +4 -1
- package/package.json +3 -2
- package/skills/core/SKILL.md +4 -2
- package/stack/docker-compose.hub.yml +5 -0
- package/stack/docker-compose.yml +35 -0
package/dist/bin/mesh.js
CHANGED
|
@@ -580,11 +580,11 @@ function createVcsFolderReader(opts) {
|
|
|
580
580
|
}
|
|
581
581
|
return p;
|
|
582
582
|
};
|
|
583
|
-
const readPath = async (repo,
|
|
583
|
+
const readPath = async (repo, path42) => {
|
|
584
584
|
const dir = await cloneRepo(repo);
|
|
585
|
-
const ref = await resolveFolderRef(dir,
|
|
586
|
-
if (!ref) throw new Error(`path "${
|
|
587
|
-
const files = await readFolderAtRef(dir, ref,
|
|
585
|
+
const ref = await resolveFolderRef(dir, path42);
|
|
586
|
+
if (!ref) throw new Error(`path "${path42}" not found on any ref of ${repo}`);
|
|
587
|
+
const files = await readFolderAtRef(dir, ref, path42);
|
|
588
588
|
return { ref, files };
|
|
589
589
|
};
|
|
590
590
|
const cleanup = async () => {
|
|
@@ -1530,7 +1530,7 @@ function execTailscaleCmd(args) {
|
|
|
1530
1530
|
}
|
|
1531
1531
|
attempts.push({ binary, args });
|
|
1532
1532
|
}
|
|
1533
|
-
return new Promise((
|
|
1533
|
+
return new Promise((resolve16, reject) => {
|
|
1534
1534
|
let index = 0;
|
|
1535
1535
|
function tryNext() {
|
|
1536
1536
|
if (index >= attempts.length) {
|
|
@@ -1542,7 +1542,7 @@ function execTailscaleCmd(args) {
|
|
|
1542
1542
|
if (error) {
|
|
1543
1543
|
tryNext();
|
|
1544
1544
|
} else {
|
|
1545
|
-
|
|
1545
|
+
resolve16(stdout);
|
|
1546
1546
|
}
|
|
1547
1547
|
});
|
|
1548
1548
|
}
|
|
@@ -1859,8 +1859,8 @@ function resolveHubPlatformName(platform) {
|
|
|
1859
1859
|
return platform?.name ?? "mesh";
|
|
1860
1860
|
}
|
|
1861
1861
|
async function ssmGetParameter(name) {
|
|
1862
|
-
const { SSMClient:
|
|
1863
|
-
const ssm = new
|
|
1862
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
1863
|
+
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
|
|
1864
1864
|
const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
|
|
1865
1865
|
return resp.Parameter?.Value;
|
|
1866
1866
|
}
|
|
@@ -1901,8 +1901,8 @@ var init_kubeconfig = __esm({
|
|
|
1901
1901
|
// libs/mesh-cli/src/utils/temporal-auth.ts
|
|
1902
1902
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1903
1903
|
async function resolveTemporalAuth(tenant, env, platformName = tenant) {
|
|
1904
|
-
const { SSMClient:
|
|
1905
|
-
const ssm = new
|
|
1904
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
1905
|
+
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
|
|
1906
1906
|
const results = {};
|
|
1907
1907
|
async function trySSM(name) {
|
|
1908
1908
|
try {
|
|
@@ -2034,14 +2034,14 @@ var init_temporal_auth = __esm({
|
|
|
2034
2034
|
// libs/mesh-cli/src/utils/reachability.ts
|
|
2035
2035
|
import { createConnection } from "node:net";
|
|
2036
2036
|
function probeTcpReachable(host, port, timeoutMs = 1500) {
|
|
2037
|
-
return new Promise((
|
|
2037
|
+
return new Promise((resolve16) => {
|
|
2038
2038
|
let settled = false;
|
|
2039
2039
|
const socket = createConnection({ host, port });
|
|
2040
2040
|
const finish = (ok) => {
|
|
2041
2041
|
if (settled) return;
|
|
2042
2042
|
settled = true;
|
|
2043
2043
|
socket.destroy();
|
|
2044
|
-
|
|
2044
|
+
resolve16(ok);
|
|
2045
2045
|
};
|
|
2046
2046
|
socket.setTimeout(timeoutMs);
|
|
2047
2047
|
socket.once("connect", () => finish(true));
|
|
@@ -2050,7 +2050,7 @@ function probeTcpReachable(host, port, timeoutMs = 1500) {
|
|
|
2050
2050
|
});
|
|
2051
2051
|
}
|
|
2052
2052
|
function probeConnectionHolds(host, port, timeoutMs = 800, holdMs = 25) {
|
|
2053
|
-
return new Promise((
|
|
2053
|
+
return new Promise((resolve16) => {
|
|
2054
2054
|
let settled = false;
|
|
2055
2055
|
let holdTimer;
|
|
2056
2056
|
const socket = createConnection({ host, port });
|
|
@@ -2059,7 +2059,7 @@ function probeConnectionHolds(host, port, timeoutMs = 800, holdMs = 25) {
|
|
|
2059
2059
|
settled = true;
|
|
2060
2060
|
if (holdTimer) clearTimeout(holdTimer);
|
|
2061
2061
|
socket.destroy();
|
|
2062
|
-
|
|
2062
|
+
resolve16(ok);
|
|
2063
2063
|
};
|
|
2064
2064
|
socket.setTimeout(timeoutMs);
|
|
2065
2065
|
socket.once("connect", () => {
|
|
@@ -2191,16 +2191,16 @@ async function waitForPort(host, port, timeoutMs, intervalMs = 500) {
|
|
|
2191
2191
|
const attemptTimeout = Math.max(250, Math.min(1e3, remaining));
|
|
2192
2192
|
if (await tryConnect(host, port, attemptTimeout)) return true;
|
|
2193
2193
|
if (Date.now() + intervalMs >= deadline) return false;
|
|
2194
|
-
await new Promise((
|
|
2194
|
+
await new Promise((resolve16) => setTimeout(resolve16, intervalMs));
|
|
2195
2195
|
}
|
|
2196
2196
|
}
|
|
2197
2197
|
function tryConnect(host, port, timeoutMs) {
|
|
2198
|
-
return new Promise((
|
|
2198
|
+
return new Promise((resolve16) => {
|
|
2199
2199
|
const socket = net.connect({ host, port });
|
|
2200
2200
|
const done = (ok) => {
|
|
2201
2201
|
socket.removeAllListeners();
|
|
2202
2202
|
socket.destroy();
|
|
2203
|
-
|
|
2203
|
+
resolve16(ok);
|
|
2204
2204
|
};
|
|
2205
2205
|
socket.setTimeout(timeoutMs);
|
|
2206
2206
|
socket.once("connect", () => done(true));
|
|
@@ -2277,7 +2277,7 @@ async function ensureTemporalNamespace(namespace, description) {
|
|
|
2277
2277
|
});
|
|
2278
2278
|
break;
|
|
2279
2279
|
} catch {
|
|
2280
|
-
await new Promise((
|
|
2280
|
+
await new Promise((resolve16) => setTimeout(resolve16, 1e3));
|
|
2281
2281
|
}
|
|
2282
2282
|
}
|
|
2283
2283
|
logSuccess(`Temporal namespace '${namespace}' is active`);
|
|
@@ -2297,8 +2297,8 @@ function buildFabricCheckPayload(targetBytes = 8e3) {
|
|
|
2297
2297
|
return JSON.stringify({ ...base, pad: "x".repeat(Math.max(0, targetBytes - overhead)) });
|
|
2298
2298
|
}
|
|
2299
2299
|
async function verifyFabric() {
|
|
2300
|
-
const { SSMClient:
|
|
2301
|
-
const ssm = new
|
|
2300
|
+
const { SSMClient: SSMClient5, PutParameterCommand, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
|
|
2301
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2302
2302
|
const payload = buildFabricCheckPayload();
|
|
2303
2303
|
const mainParam = FABRIC_CHECK_PATH;
|
|
2304
2304
|
const childParams = [`${FABRIC_CHECK_PATH}/vpc`, `${FABRIC_CHECK_PATH}/eks`];
|
|
@@ -2350,8 +2350,8 @@ async function verifyFabric() {
|
|
|
2350
2350
|
}
|
|
2351
2351
|
}
|
|
2352
2352
|
async function registerTenantEnv(tenant, opts = {}) {
|
|
2353
|
-
const { SSMClient:
|
|
2354
|
-
const ssm = new
|
|
2353
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
2354
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2355
2355
|
await ssm.send(
|
|
2356
2356
|
new PutParameterCommand({
|
|
2357
2357
|
Name: `/mesh-platform/${tenant}`,
|
|
@@ -2379,8 +2379,8 @@ async function registerTenantEnv(tenant, opts = {}) {
|
|
|
2379
2379
|
);
|
|
2380
2380
|
}
|
|
2381
2381
|
async function seedLocalPlatform() {
|
|
2382
|
-
const { SSMClient:
|
|
2383
|
-
const ssm = new
|
|
2382
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
2383
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2384
2384
|
await ssm.send(
|
|
2385
2385
|
new PutParameterCommand({
|
|
2386
2386
|
Name: APP_TENANTS_PARAM,
|
|
@@ -2434,14 +2434,14 @@ var init_seed = __esm({
|
|
|
2434
2434
|
// libs/mesh-cli/src/commands/local/helpers.ts
|
|
2435
2435
|
import * as net2 from "net";
|
|
2436
2436
|
async function upsertLocalSecret(secretId, value, client) {
|
|
2437
|
-
const { SecretsManagerClient:
|
|
2438
|
-
const sm = client ?? new
|
|
2437
|
+
const { SecretsManagerClient: SecretsManagerClient9, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
|
|
2438
|
+
const sm = client ?? new SecretsManagerClient9(LOCAL_AWS_CONFIG);
|
|
2439
2439
|
const secretString = JSON.stringify(value);
|
|
2440
2440
|
try {
|
|
2441
|
-
await sm.send(new
|
|
2441
|
+
await sm.send(new CreateSecretCommand4({ Name: secretId, SecretString: secretString }));
|
|
2442
2442
|
} catch (err) {
|
|
2443
2443
|
if (err?.name === "ResourceExistsException") {
|
|
2444
|
-
await sm.send(new
|
|
2444
|
+
await sm.send(new PutSecretValueCommand4({ SecretId: secretId, SecretString: secretString }));
|
|
2445
2445
|
} else {
|
|
2446
2446
|
throw err;
|
|
2447
2447
|
}
|
|
@@ -2449,16 +2449,16 @@ async function upsertLocalSecret(secretId, value, client) {
|
|
|
2449
2449
|
}
|
|
2450
2450
|
function probeTcp(port, opts = {}) {
|
|
2451
2451
|
const { host = "127.0.0.1", timeoutMs = 2e3 } = opts;
|
|
2452
|
-
return new Promise((
|
|
2452
|
+
return new Promise((resolve16) => {
|
|
2453
2453
|
const socket = net2.connect({ host, port, timeout: timeoutMs });
|
|
2454
2454
|
socket.once("connect", () => {
|
|
2455
2455
|
socket.destroy();
|
|
2456
|
-
|
|
2456
|
+
resolve16(true);
|
|
2457
2457
|
});
|
|
2458
|
-
socket.once("error", () =>
|
|
2458
|
+
socket.once("error", () => resolve16(false));
|
|
2459
2459
|
socket.once("timeout", () => {
|
|
2460
2460
|
socket.destroy();
|
|
2461
|
-
|
|
2461
|
+
resolve16(false);
|
|
2462
2462
|
});
|
|
2463
2463
|
});
|
|
2464
2464
|
}
|
|
@@ -2496,7 +2496,8 @@ __export(stack_exports, {
|
|
|
2496
2496
|
stackDir: () => stackDir,
|
|
2497
2497
|
stackOwnedElsewhere: () => stackOwnedElsewhere,
|
|
2498
2498
|
stackServices: () => stackServices,
|
|
2499
|
-
summarizeComposeFailure: () => summarizeComposeFailure
|
|
2499
|
+
summarizeComposeFailure: () => summarizeComposeFailure,
|
|
2500
|
+
writeAppServiceProbes: () => writeAppServiceProbes
|
|
2500
2501
|
});
|
|
2501
2502
|
import { execFileSync as execFileSync4, spawn, spawnSync } from "child_process";
|
|
2502
2503
|
import * as fs7 from "fs";
|
|
@@ -2514,6 +2515,26 @@ function localProbesDir() {
|
|
|
2514
2515
|
fs7.mkdirSync(dir, { recursive: true });
|
|
2515
2516
|
return dir;
|
|
2516
2517
|
}
|
|
2518
|
+
function writeAppServiceProbes(args) {
|
|
2519
|
+
const entries = Object.entries(args.services);
|
|
2520
|
+
if (entries.length === 0) return void 0;
|
|
2521
|
+
const healthPath = args.healthPath ?? "/health";
|
|
2522
|
+
const targets = entries.map(([name, port]) => ({
|
|
2523
|
+
targets: [`http://host.docker.internal:${port}${healthPath}`],
|
|
2524
|
+
labels: {
|
|
2525
|
+
type: "service",
|
|
2526
|
+
tenant: args.tenant,
|
|
2527
|
+
env: args.env,
|
|
2528
|
+
app: args.app,
|
|
2529
|
+
service: name,
|
|
2530
|
+
target: name
|
|
2531
|
+
}
|
|
2532
|
+
}));
|
|
2533
|
+
const file = path8.join(localProbesDir(), `${args.tenant}-${args.app}-services.json`);
|
|
2534
|
+
fs7.writeFileSync(file, `${JSON.stringify(targets, null, 2)}
|
|
2535
|
+
`);
|
|
2536
|
+
return file;
|
|
2537
|
+
}
|
|
2517
2538
|
function hubPort() {
|
|
2518
2539
|
const raw = process.env.MESH_HUB_PORT?.trim();
|
|
2519
2540
|
if (!raw) return DEFAULT_HUB_PORT;
|
|
@@ -2690,7 +2711,7 @@ async function composeStreamed(args, opts = {}) {
|
|
|
2690
2711
|
let captured = "";
|
|
2691
2712
|
let exitCode;
|
|
2692
2713
|
try {
|
|
2693
|
-
exitCode = await new Promise((
|
|
2714
|
+
exitCode = await new Promise((resolve16, reject) => {
|
|
2694
2715
|
const child = spawn("docker", fullArgs, {
|
|
2695
2716
|
cwd: dir,
|
|
2696
2717
|
env,
|
|
@@ -2706,11 +2727,11 @@ async function composeStreamed(args, opts = {}) {
|
|
|
2706
2727
|
child.stdout.on("data", consume);
|
|
2707
2728
|
child.stderr.on("data", consume);
|
|
2708
2729
|
child.on("error", reject);
|
|
2709
|
-
child.on("close", (code) =>
|
|
2730
|
+
child.on("close", (code) => resolve16(code ?? 1));
|
|
2710
2731
|
});
|
|
2711
2732
|
} finally {
|
|
2712
2733
|
heartbeat.stop();
|
|
2713
|
-
await new Promise((
|
|
2734
|
+
await new Promise((resolve16) => logStream.end(resolve16));
|
|
2714
2735
|
}
|
|
2715
2736
|
if (exitCode !== 0) {
|
|
2716
2737
|
throw new MeshCliError(
|
|
@@ -2798,6 +2819,13 @@ var init_stack = __esm({
|
|
|
2798
2819
|
probe: { kind: "http", url: "http://localhost:8080/debug/healthz" },
|
|
2799
2820
|
hint: "admin@local.mesh / LocalDev1!"
|
|
2800
2821
|
},
|
|
2822
|
+
{
|
|
2823
|
+
service: "mailpit",
|
|
2824
|
+
label: "Mailbox (local mail)",
|
|
2825
|
+
url: "http://localhost:8025",
|
|
2826
|
+
probe: { kind: "http", url: "http://localhost:8025/readyz" },
|
|
2827
|
+
hint: "every activation + password-reset mail Zitadel sends locally lands here"
|
|
2828
|
+
},
|
|
2801
2829
|
{
|
|
2802
2830
|
service: "database",
|
|
2803
2831
|
label: "Postgres",
|
|
@@ -3158,12 +3186,12 @@ async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
|
|
|
3158
3186
|
const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
|
|
3159
3187
|
const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE ? process.env.MESH_AWS_PROFILE ?? profile : void 0;
|
|
3160
3188
|
try {
|
|
3161
|
-
const { SecretsManagerClient:
|
|
3189
|
+
const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
|
|
3162
3190
|
if (fallbackProfile) process.env.AWS_PROFILE = fallbackProfile;
|
|
3163
|
-
const sm = new
|
|
3191
|
+
const sm = new SecretsManagerClient9({ region });
|
|
3164
3192
|
let res;
|
|
3165
3193
|
try {
|
|
3166
|
-
res = await sm.send(new
|
|
3194
|
+
res = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
|
|
3167
3195
|
} finally {
|
|
3168
3196
|
if (fallbackProfile) delete process.env.AWS_PROFILE;
|
|
3169
3197
|
}
|
|
@@ -3277,8 +3305,8 @@ async function seedLocalMock(args) {
|
|
|
3277
3305
|
`${secretName}/.config`,
|
|
3278
3306
|
Object.fromEntries(Object.entries(value).filter(([key]) => !SECRET_KEY_RE.test(key)))
|
|
3279
3307
|
);
|
|
3280
|
-
const { SSMClient:
|
|
3281
|
-
const ssm = new
|
|
3308
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3309
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3282
3310
|
const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/external-services/${decl.external}`;
|
|
3283
3311
|
await ssm.send(
|
|
3284
3312
|
new PutParameterCommand({
|
|
@@ -3382,16 +3410,48 @@ var init_mocks = __esm({
|
|
|
3382
3410
|
});
|
|
3383
3411
|
|
|
3384
3412
|
// libs/api-registry/src/hub-roles.ts
|
|
3385
|
-
|
|
3413
|
+
function restrictedRoleBase(roleKey) {
|
|
3414
|
+
const sep5 = roleKey.indexOf(HUB_ROLE_KEY_SEPARATOR);
|
|
3415
|
+
const base = sep5 === -1 ? roleKey : roleKey.slice(0, sep5);
|
|
3416
|
+
return RESTRICTED.has(base) ? base : null;
|
|
3417
|
+
}
|
|
3418
|
+
function hubRoleKeyTenant(roleKey) {
|
|
3419
|
+
const sep5 = roleKey.indexOf(HUB_ROLE_KEY_SEPARATOR);
|
|
3420
|
+
if (sep5 === -1) return null;
|
|
3421
|
+
const scope = roleKey.slice(sep5 + 1);
|
|
3422
|
+
const appSep = scope.indexOf(HUB_ROLE_APP_SEPARATOR);
|
|
3423
|
+
return appSep === -1 ? scope : scope.slice(0, appSep);
|
|
3424
|
+
}
|
|
3425
|
+
function hubOperatorRoleKeys(tenants) {
|
|
3426
|
+
const cleaned = [...new Set(tenants.filter((t) => t !== ""))].sort();
|
|
3427
|
+
for (const tenant of cleaned) {
|
|
3428
|
+
for (const sep5 of [HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR]) {
|
|
3429
|
+
if (tenant.includes(sep5)) {
|
|
3430
|
+
throw new Error(
|
|
3431
|
+
`hubOperatorRoleKeys: tenant name "${tenant}" contains the role-key separator "${sep5}" \u2014 it would parse as a different tenant's or app's grant`
|
|
3432
|
+
);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
return [
|
|
3437
|
+
...HUB_BASE_ROLES,
|
|
3438
|
+
...cleaned.flatMap(
|
|
3439
|
+
(tenant) => HUB_RESTRICTED_ROLES.map((role) => `${role}${HUB_ROLE_KEY_SEPARATOR}${tenant}`)
|
|
3440
|
+
)
|
|
3441
|
+
];
|
|
3442
|
+
}
|
|
3443
|
+
var HUB_STAFF_ROLES, HUB_RESTRICTED_ROLES, HUB_BASE_ROLES, HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR, RESTRICTED;
|
|
3386
3444
|
var init_hub_roles = __esm({
|
|
3387
3445
|
"libs/api-registry/src/hub-roles.ts"() {
|
|
3388
3446
|
"use strict";
|
|
3389
|
-
HUB_STAFF_ROLES = ["ops", "admin"
|
|
3390
|
-
HUB_RESTRICTED_ROLES = ["auditor", "vendor"];
|
|
3447
|
+
HUB_STAFF_ROLES = ["ops", "admin"];
|
|
3448
|
+
HUB_RESTRICTED_ROLES = ["developer", "auditor", "vendor"];
|
|
3391
3449
|
HUB_BASE_ROLES = [
|
|
3392
3450
|
...HUB_STAFF_ROLES,
|
|
3393
3451
|
...HUB_RESTRICTED_ROLES
|
|
3394
3452
|
];
|
|
3453
|
+
HUB_ROLE_KEY_SEPARATOR = ":";
|
|
3454
|
+
HUB_ROLE_APP_SEPARATOR = "/";
|
|
3395
3455
|
RESTRICTED = new Set(HUB_RESTRICTED_ROLES);
|
|
3396
3456
|
}
|
|
3397
3457
|
});
|
|
@@ -3506,7 +3566,9 @@ async function api(pat, method, apiPath, body, orgId) {
|
|
|
3506
3566
|
const text = await res.text();
|
|
3507
3567
|
const data = text ? JSON.parse(text) : {};
|
|
3508
3568
|
if (!res.ok) {
|
|
3509
|
-
const err = new Error(
|
|
3569
|
+
const err = new Error(
|
|
3570
|
+
`Zitadel ${method} ${apiPath} \u2192 ${res.status}: ${data?.message ?? text}`
|
|
3571
|
+
);
|
|
3510
3572
|
err.status = res.status;
|
|
3511
3573
|
err.zitadelCode = data?.code;
|
|
3512
3574
|
throw err;
|
|
@@ -3552,7 +3614,12 @@ async function ensureCliApp(pat, projectId) {
|
|
|
3552
3614
|
logInfo(`Zitadel app '${CLI_APP_NAME}' already exists`);
|
|
3553
3615
|
return existing;
|
|
3554
3616
|
}
|
|
3555
|
-
const created = await api(
|
|
3617
|
+
const created = await api(
|
|
3618
|
+
pat,
|
|
3619
|
+
"POST",
|
|
3620
|
+
`/management/v1/projects/${projectId}/apps/oidc`,
|
|
3621
|
+
buildCliAppPayload()
|
|
3622
|
+
);
|
|
3556
3623
|
logSuccess(`Created Zitadel application '${CLI_APP_NAME}' (PKCE + device code)`);
|
|
3557
3624
|
return created.clientId;
|
|
3558
3625
|
}
|
|
@@ -3594,12 +3661,16 @@ function readHubAuth() {
|
|
|
3594
3661
|
return null;
|
|
3595
3662
|
}
|
|
3596
3663
|
}
|
|
3664
|
+
function hubRoleDisplayName(roleKey) {
|
|
3665
|
+
const tenant = hubRoleKeyTenant(roleKey);
|
|
3666
|
+
return tenant === null ? roleKey : `${restrictedRoleBase(roleKey) ?? roleKey} (${tenant})`;
|
|
3667
|
+
}
|
|
3597
3668
|
async function ensureHubRoles(pat, projectId) {
|
|
3598
3669
|
for (const role of HUB_ROLES) {
|
|
3599
3670
|
try {
|
|
3600
3671
|
await api(pat, "POST", `/management/v1/projects/${projectId}/roles`, {
|
|
3601
3672
|
roleKey: role,
|
|
3602
|
-
displayName: role
|
|
3673
|
+
displayName: hubRoleDisplayName(role)
|
|
3603
3674
|
});
|
|
3604
3675
|
logSuccess(`Created Hub role '${role}'`);
|
|
3605
3676
|
} catch (err) {
|
|
@@ -3628,7 +3699,10 @@ async function reconcileHubRedirectUris(pat, projectId, app) {
|
|
|
3628
3699
|
redirectUris,
|
|
3629
3700
|
postLogoutRedirectUris,
|
|
3630
3701
|
responseTypes: cfg.responseTypes ?? ["OIDC_RESPONSE_TYPE_CODE"],
|
|
3631
|
-
grantTypes: cfg.grantTypes ?? [
|
|
3702
|
+
grantTypes: cfg.grantTypes ?? [
|
|
3703
|
+
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE",
|
|
3704
|
+
"OIDC_GRANT_TYPE_REFRESH_TOKEN"
|
|
3705
|
+
],
|
|
3632
3706
|
appType: "OIDC_APP_TYPE_WEB",
|
|
3633
3707
|
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
|
|
3634
3708
|
accessTokenType: cfg.accessTokenType ?? "OIDC_TOKEN_TYPE_JWT",
|
|
@@ -3637,7 +3711,9 @@ async function reconcileHubRedirectUris(pat, projectId, app) {
|
|
|
3637
3711
|
idTokenUserinfoAssertion: cfg.idTokenUserinfoAssertion ?? true,
|
|
3638
3712
|
devMode: cfg.devMode ?? true
|
|
3639
3713
|
});
|
|
3640
|
-
logSuccess(
|
|
3714
|
+
logSuccess(
|
|
3715
|
+
`Registered Hub redirect URI http://localhost:${hubPort()}/oauth2/callback (MESH_HUB_PORT)`
|
|
3716
|
+
);
|
|
3641
3717
|
}
|
|
3642
3718
|
async function ensureHubApp(pat, projectId) {
|
|
3643
3719
|
const existing = await searchZitadelApp(pat, projectId, HUB_APP_NAME);
|
|
@@ -3713,15 +3789,42 @@ async function seedHubAuth(pat) {
|
|
|
3713
3789
|
const persistedCookie = persisted?.cookieSecret && [16, 24, 32].includes(persisted.cookieSecret.length) ? persisted.cookieSecret : void 0;
|
|
3714
3790
|
const config = {
|
|
3715
3791
|
clientId: app.clientId,
|
|
3792
|
+
projectId,
|
|
3716
3793
|
clientSecret: app.clientSecret ?? persisted?.clientSecret ?? "",
|
|
3717
3794
|
cookieSecret: persistedCookie ?? (await import("crypto")).randomBytes(16).toString("hex")
|
|
3718
3795
|
};
|
|
3719
3796
|
fs9.writeFileSync(hubAuthPath(), JSON.stringify(config, null, 2), { mode: 384 });
|
|
3720
3797
|
return config;
|
|
3721
3798
|
}
|
|
3799
|
+
async function publishHubAuthzPointer(projectId, orgId, awsConfig) {
|
|
3800
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3801
|
+
const ssm = new SSMClient5(awsConfig);
|
|
3802
|
+
const name = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/apps/hub/stacks/local/authz`;
|
|
3803
|
+
let current = {};
|
|
3804
|
+
try {
|
|
3805
|
+
const existing = await ssm.send(new GetParameterCommand2({ Name: name }));
|
|
3806
|
+
current = JSON.parse(existing.Parameter?.Value ?? "{}");
|
|
3807
|
+
} catch {
|
|
3808
|
+
}
|
|
3809
|
+
const value = {
|
|
3810
|
+
spicedb: { instanceRefs: [] },
|
|
3811
|
+
...current,
|
|
3812
|
+
zitadel: { projectId, orgId, issuer: ZITADEL_ISSUER }
|
|
3813
|
+
};
|
|
3814
|
+
await ssm.send(
|
|
3815
|
+
new PutParameterCommand({
|
|
3816
|
+
Name: name,
|
|
3817
|
+
Type: "String",
|
|
3818
|
+
Overwrite: true,
|
|
3819
|
+
Value: JSON.stringify(value),
|
|
3820
|
+
Description: "Hub authz pointer (local analog of the platform Pulumi program)"
|
|
3821
|
+
})
|
|
3822
|
+
);
|
|
3823
|
+
logSuccess(`Hub authz pointer published \u2192 project ${projectId} in org ${orgId}`);
|
|
3824
|
+
}
|
|
3722
3825
|
async function writeRegistryParams(cliClientId, awsConfig) {
|
|
3723
|
-
const { SSMClient:
|
|
3724
|
-
const ssm = new
|
|
3826
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3827
|
+
const ssm = new SSMClient5(awsConfig);
|
|
3725
3828
|
await ssm.send(
|
|
3726
3829
|
new PutParameterCommand({
|
|
3727
3830
|
Name: ZITADEL_SSM_PARAM,
|
|
@@ -3743,6 +3846,77 @@ async function writeRegistryParams(cliClientId, awsConfig) {
|
|
|
3743
3846
|
);
|
|
3744
3847
|
}
|
|
3745
3848
|
}
|
|
3849
|
+
async function ensureOpsHubAdmin(pat, awsConfig) {
|
|
3850
|
+
const {
|
|
3851
|
+
SecretsManagerClient: SecretsManagerClient9,
|
|
3852
|
+
GetSecretValueCommand: GetSecretValueCommand9,
|
|
3853
|
+
CreateSecretCommand: CreateSecretCommand4,
|
|
3854
|
+
PutSecretValueCommand: PutSecretValueCommand4
|
|
3855
|
+
} = await import("@aws-sdk/client-secrets-manager");
|
|
3856
|
+
const sm = new SecretsManagerClient9(awsConfig);
|
|
3857
|
+
const existing = await sm.send(new GetSecretValueCommand9({ SecretId: OPS_HUB_SECRET_ID })).catch(() => null);
|
|
3858
|
+
if (existing?.SecretString) {
|
|
3859
|
+
logSuccess("Hub admin key already provisioned (Zitadel writes enabled)");
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
const userName = "hub-opshub";
|
|
3863
|
+
const found = await api(pat, "POST", "/management/v1/users/_search", {
|
|
3864
|
+
queries: [{ userNameQuery: { userName, method: "TEXT_QUERY_METHOD_EQUALS" } }]
|
|
3865
|
+
});
|
|
3866
|
+
let userId = found?.result?.[0]?.id;
|
|
3867
|
+
if (!userId) {
|
|
3868
|
+
const created = await api(pat, "POST", "/management/v1/users/machine", {
|
|
3869
|
+
userName,
|
|
3870
|
+
name: "Hub Ops Hub admin",
|
|
3871
|
+
description: "Local Hub Zitadel admin plane (seeded by mesh start)"
|
|
3872
|
+
});
|
|
3873
|
+
userId = created?.userId;
|
|
3874
|
+
}
|
|
3875
|
+
if (!userId) throw new MeshCliError("could not create the Hub's Zitadel admin user");
|
|
3876
|
+
await api(pat, "POST", "/admin/v1/members", { userId, roles: ["IAM_OWNER"] }).catch(
|
|
3877
|
+
() => void 0
|
|
3878
|
+
);
|
|
3879
|
+
const key = await api(pat, "POST", `/management/v1/users/${userId}/keys`, {
|
|
3880
|
+
type: "KEY_TYPE_JSON"
|
|
3881
|
+
});
|
|
3882
|
+
if (!key?.keyDetails)
|
|
3883
|
+
throw new MeshCliError("Zitadel did not return a machine key for the Hub admin user");
|
|
3884
|
+
const raw = JSON.parse(Buffer.from(key.keyDetails, "base64").toString("utf8"));
|
|
3885
|
+
const secretString = JSON.stringify({ keyId: raw.keyId, key: raw.key, userId: raw.userId });
|
|
3886
|
+
await sm.send(new CreateSecretCommand4({ Name: OPS_HUB_SECRET_ID, SecretString: secretString })).catch(async () => {
|
|
3887
|
+
await sm.send(
|
|
3888
|
+
new PutSecretValueCommand4({ SecretId: OPS_HUB_SECRET_ID, SecretString: secretString })
|
|
3889
|
+
);
|
|
3890
|
+
});
|
|
3891
|
+
logSuccess(`Hub admin key provisioned \u2192 ${OPS_HUB_SECRET_ID} (Zitadel writes enabled)`);
|
|
3892
|
+
}
|
|
3893
|
+
async function ensureLocalSmtp(pat) {
|
|
3894
|
+
try {
|
|
3895
|
+
const existing = await api(pat, "POST", "/admin/v1/smtp/_search", {}).catch(() => null);
|
|
3896
|
+
const configs = existing?.result ?? [];
|
|
3897
|
+
const match = configs.find((c) => c.host === LOCAL_SMTP.host);
|
|
3898
|
+
if (!match) {
|
|
3899
|
+
const created = await api(pat, "POST", "/admin/v1/smtp", {
|
|
3900
|
+
senderAddress: LOCAL_SMTP.from,
|
|
3901
|
+
senderName: LOCAL_SMTP.fromName,
|
|
3902
|
+
tls: false,
|
|
3903
|
+
host: LOCAL_SMTP.host,
|
|
3904
|
+
user: "",
|
|
3905
|
+
password: ""
|
|
3906
|
+
});
|
|
3907
|
+
if (created?.id) await api(pat, "POST", `/admin/v1/smtp/${created.id}/_activate`, {});
|
|
3908
|
+
logSuccess(`Local mailbox wired to Zitadel \u2192 ${LOCAL_SMTP.host} (view at http://localhost:8025)`);
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
if (match.state !== "SMTP_CONFIG_ACTIVE" && match.id) {
|
|
3912
|
+
await api(pat, "POST", `/admin/v1/smtp/${match.id}/_activate`, {});
|
|
3913
|
+
}
|
|
3914
|
+
} catch (err) {
|
|
3915
|
+
logWarn(
|
|
3916
|
+
`Could not wire the local mailbox to Zitadel (${err.message}). Activation and password-reset mail will not be delivered locally.`
|
|
3917
|
+
);
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3746
3920
|
async function seedZitadel(awsConfig) {
|
|
3747
3921
|
const pat = readSeederPat();
|
|
3748
3922
|
const org = await api(pat, "POST", "/admin/v1/orgs/_search", {
|
|
@@ -3754,21 +3928,26 @@ async function seedZitadel(awsConfig) {
|
|
|
3754
3928
|
{ remediation: { command: "mesh stop --destroy && mesh start" } }
|
|
3755
3929
|
);
|
|
3756
3930
|
}
|
|
3757
|
-
logSuccess(
|
|
3931
|
+
logSuccess(
|
|
3932
|
+
`Platform org '${PLATFORM_ORG}' ready (platform tenant root \u2014 holds platform-service auth config)`
|
|
3933
|
+
);
|
|
3758
3934
|
try {
|
|
3759
3935
|
await api(pat, "PUT", "/v2/features/instance", { loginV2: { required: false } });
|
|
3760
3936
|
} catch {
|
|
3761
3937
|
}
|
|
3938
|
+
await ensureLocalSmtp(pat);
|
|
3762
3939
|
const projectId = await ensureCliProject(pat);
|
|
3763
3940
|
const cliClientId = await ensureCliApp(pat, projectId);
|
|
3764
3941
|
await ensureTestUsers(pat);
|
|
3765
3942
|
const hubAuth = await seedHubAuth(pat);
|
|
3943
|
+
await publishHubAuthzPointer(hubAuth.projectId, org.result[0].id, awsConfig);
|
|
3766
3944
|
await writeRegistryParams(cliClientId, awsConfig);
|
|
3945
|
+
await ensureOpsHubAdmin(pat, awsConfig);
|
|
3767
3946
|
writeContextConfig(LOGIN_CONTEXT, { issuer: ZITADEL_ISSUER, clientId: cliClientId });
|
|
3768
3947
|
logSuccess(`Login context '${LOGIN_CONTEXT}' configured \u2192 try: mesh login ${LOGIN_CONTEXT}`);
|
|
3769
3948
|
return { projectId, cliClientId, hubAuth };
|
|
3770
3949
|
}
|
|
3771
|
-
var ZITADEL_ISSUER, LOGIN_CONTEXT, PLATFORM_ORG, CLI_PROJECT_NAME, CLI_APP_NAME, CLI_REDIRECT_URI, HUB_PROJECT_NAME, HUB_APP_NAME, hubRedirectUri, HUB_DEFAULT_REDIRECT_URI, HUB_ROLES, ZITADEL_SSM_PARAM, TEST_USERS_SSM_PREFIX, TEST_USERS, isAlreadyExists, ensureCliProject, ensureHubProject;
|
|
3950
|
+
var ZITADEL_ISSUER, LOGIN_CONTEXT, PLATFORM_ORG, CLI_PROJECT_NAME, CLI_APP_NAME, CLI_REDIRECT_URI, HUB_PROJECT_NAME, HUB_APP_NAME, hubRedirectUri, HUB_DEFAULT_REDIRECT_URI, HUB_ROLES, ZITADEL_SSM_PARAM, TEST_USERS_SSM_PREFIX, TEST_USERS, isAlreadyExists, ensureCliProject, ensureHubProject, OPS_HUB_SECRET_ID, LOCAL_SMTP;
|
|
3772
3951
|
var init_seed_zitadel = __esm({
|
|
3773
3952
|
"libs/mesh-cli/src/commands/local/seed-zitadel.ts"() {
|
|
3774
3953
|
"use strict";
|
|
@@ -3788,16 +3967,37 @@ var init_seed_zitadel = __esm({
|
|
|
3788
3967
|
HUB_APP_NAME = "ui";
|
|
3789
3968
|
hubRedirectUri = () => `http://localhost:${hubPort()}/oauth2/callback`;
|
|
3790
3969
|
HUB_DEFAULT_REDIRECT_URI = `http://localhost:${DEFAULT_HUB_PORT}/oauth2/callback`;
|
|
3791
|
-
HUB_ROLES =
|
|
3970
|
+
HUB_ROLES = hubOperatorRoleKeys([LOCAL_TENANT]);
|
|
3792
3971
|
ZITADEL_SSM_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/platform/zitadel`;
|
|
3793
3972
|
TEST_USERS_SSM_PREFIX = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/temporal/test-users`;
|
|
3794
3973
|
TEST_USERS = [
|
|
3795
|
-
{
|
|
3796
|
-
|
|
3974
|
+
{
|
|
3975
|
+
name: "dev",
|
|
3976
|
+
email: "dev@local.mesh",
|
|
3977
|
+
firstName: "Dev",
|
|
3978
|
+
lastName: "User",
|
|
3979
|
+
password: "LocalDev1!"
|
|
3980
|
+
},
|
|
3981
|
+
{
|
|
3982
|
+
name: "ops",
|
|
3983
|
+
email: "ops@local.mesh",
|
|
3984
|
+
firstName: "Ops",
|
|
3985
|
+
lastName: "User",
|
|
3986
|
+
password: "LocalDev1!"
|
|
3987
|
+
}
|
|
3797
3988
|
];
|
|
3798
3989
|
isAlreadyExists = (err) => err?.status === 409 || err?.zitadelCode === 6 || /already exists/i.test(err?.message ?? "");
|
|
3799
|
-
ensureCliProject = (pat) => ensureZitadelProject(pat, CLI_PROJECT_NAME, {
|
|
3990
|
+
ensureCliProject = (pat) => ensureZitadelProject(pat, CLI_PROJECT_NAME, {
|
|
3991
|
+
describe: "platform client feature",
|
|
3992
|
+
logExisting: true
|
|
3993
|
+
});
|
|
3800
3994
|
ensureHubProject = (pat) => ensureZitadelProject(pat, HUB_PROJECT_NAME, { describe: "Hub platform app" });
|
|
3995
|
+
OPS_HUB_SECRET_ID = "mesh/local/dev/zitadel/ops-hub";
|
|
3996
|
+
LOCAL_SMTP = {
|
|
3997
|
+
host: "mailpit:1025",
|
|
3998
|
+
from: "no-reply@local.mesh",
|
|
3999
|
+
fromName: "Mesh (local)"
|
|
4000
|
+
};
|
|
3801
4001
|
}
|
|
3802
4002
|
});
|
|
3803
4003
|
|
|
@@ -3911,9 +4111,9 @@ async function ensureM2mCaller(pat, tenant, app, orgId, projectId, roles) {
|
|
|
3911
4111
|
return false;
|
|
3912
4112
|
}
|
|
3913
4113
|
async function authSecretExists(tenant, app, service) {
|
|
3914
|
-
const { SecretsManagerClient:
|
|
3915
|
-
const sm = new
|
|
3916
|
-
return sm.send(new
|
|
4114
|
+
const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
|
|
4115
|
+
const sm = new SecretsManagerClient9(LOCAL_AWS_CONFIG);
|
|
4116
|
+
return sm.send(new GetSecretValueCommand9({ SecretId: authSecretPath(tenant, app, service) })).then(
|
|
3917
4117
|
() => true,
|
|
3918
4118
|
() => false
|
|
3919
4119
|
);
|
|
@@ -3924,8 +4124,8 @@ async function writeAuthSecret(tenant, app, service, value) {
|
|
|
3924
4124
|
logSuccess(`Stored service credentials \u2192 ${name}`);
|
|
3925
4125
|
}
|
|
3926
4126
|
async function registerLocalApp(args) {
|
|
3927
|
-
const { SSMClient:
|
|
3928
|
-
const ssm = new
|
|
4127
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
4128
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3929
4129
|
const put2 = (name, value, description) => ssm.send(
|
|
3930
4130
|
new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: JSON.stringify(value), Description: description })
|
|
3931
4131
|
);
|
|
@@ -3967,7 +4167,18 @@ async function registerLocalApp(args) {
|
|
|
3967
4167
|
}
|
|
3968
4168
|
await put2(
|
|
3969
4169
|
`${base}/meta`,
|
|
3970
|
-
{
|
|
4170
|
+
{
|
|
4171
|
+
runtime: "mesh-dev-local",
|
|
4172
|
+
services: args.services,
|
|
4173
|
+
ports: args.ports ?? {},
|
|
4174
|
+
// The same two fields a deployed stack stamps, so the Hub's app list and
|
|
4175
|
+
// its activity timeline read a local run exactly as they read a deploy —
|
|
4176
|
+
// which version of this app is running here, and since when. Without them
|
|
4177
|
+
// a local app shows a blank version and never appears on the timeline,
|
|
4178
|
+
// and "deployments aren't tracked" is indistinguishable from "no deploys".
|
|
4179
|
+
...args.version ? { version: args.version } : {},
|
|
4180
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4181
|
+
},
|
|
3971
4182
|
"Local run metadata (mesh dev --local)"
|
|
3972
4183
|
);
|
|
3973
4184
|
logSuccess(`Registered app in the local registry \u2192 ${base}`);
|
|
@@ -3975,8 +4186,8 @@ async function registerLocalApp(args) {
|
|
|
3975
4186
|
}
|
|
3976
4187
|
async function reconcileRegistryFromZitadel() {
|
|
3977
4188
|
const pat = readSeederPat();
|
|
3978
|
-
const { SSMClient:
|
|
3979
|
-
const ssm = new
|
|
4189
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
4190
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3980
4191
|
const orgs = await api(pat, "POST", "/admin/v1/orgs/_search", { query: { limit: 200 } });
|
|
3981
4192
|
const tenants = [];
|
|
3982
4193
|
let apps = 0;
|
|
@@ -4041,6 +4252,57 @@ async function reconcileRegistryFromZitadel() {
|
|
|
4041
4252
|
}
|
|
4042
4253
|
return { tenants, apps };
|
|
4043
4254
|
}
|
|
4255
|
+
async function ensureSignInApp(args) {
|
|
4256
|
+
const pat = readSeederPat();
|
|
4257
|
+
const orgId = await ensureOrg(pat, args.tenant);
|
|
4258
|
+
const projectId = await ensureProject(pat, orgId, args.app);
|
|
4259
|
+
const name = `${args.service}-web`;
|
|
4260
|
+
const base = args.baseUrl.replace(/\/+$/, "");
|
|
4261
|
+
const redirectUris = [`${base}/oauth2/callback`];
|
|
4262
|
+
const postLogoutRedirectUris = [base, `${base}/`];
|
|
4263
|
+
const config = {
|
|
4264
|
+
redirectUris,
|
|
4265
|
+
postLogoutRedirectUris,
|
|
4266
|
+
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
|
|
4267
|
+
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"],
|
|
4268
|
+
appType: "OIDC_APP_TYPE_WEB",
|
|
4269
|
+
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
|
|
4270
|
+
// Roles must ride in the token: the app authorizes on them, and the Hub
|
|
4271
|
+
// shows which roles a person holds by reading the same grants.
|
|
4272
|
+
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
|
|
4273
|
+
accessTokenRoleAssertion: true,
|
|
4274
|
+
idTokenRoleAssertion: true,
|
|
4275
|
+
idTokenUserinfoAssertion: true,
|
|
4276
|
+
// http:// callbacks are only permitted in dev mode.
|
|
4277
|
+
devMode: true
|
|
4278
|
+
};
|
|
4279
|
+
const existing = await searchZitadelApp(pat, projectId, name, orgId);
|
|
4280
|
+
if (existing) {
|
|
4281
|
+
await api(
|
|
4282
|
+
pat,
|
|
4283
|
+
"PUT",
|
|
4284
|
+
`/management/v1/projects/${projectId}/apps/${existing.id}/oidc_config`,
|
|
4285
|
+
config,
|
|
4286
|
+
orgId
|
|
4287
|
+
);
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
const created = await api(
|
|
4291
|
+
pat,
|
|
4292
|
+
"POST",
|
|
4293
|
+
`/management/v1/projects/${projectId}/apps/oidc`,
|
|
4294
|
+
{ name, ...config },
|
|
4295
|
+
orgId
|
|
4296
|
+
);
|
|
4297
|
+
await writeAuthSecret(args.tenant, args.app, name, {
|
|
4298
|
+
clientId: created.clientId,
|
|
4299
|
+
clientSecret: created.clientSecret,
|
|
4300
|
+
issuer: "http://localhost:8080",
|
|
4301
|
+
orgId,
|
|
4302
|
+
projectId
|
|
4303
|
+
});
|
|
4304
|
+
logSuccess(`Created Zitadel application '${name}' (browser sign-in \u2192 ${base}/oauth2/callback)`);
|
|
4305
|
+
}
|
|
4044
4306
|
async function ensureAppTenantAuth(args) {
|
|
4045
4307
|
const pat = readSeederPat();
|
|
4046
4308
|
const orgId = await ensureOrg(pat, args.tenant);
|
|
@@ -4652,14 +4914,14 @@ function isConfigStale(configMtimeMs, startedAtIso) {
|
|
|
4652
4914
|
return configMtimeMs > Date.parse(startedAtIso);
|
|
4653
4915
|
}
|
|
4654
4916
|
function canConnect(host, port, timeoutMs = 800) {
|
|
4655
|
-
return new Promise((
|
|
4917
|
+
return new Promise((resolve16) => {
|
|
4656
4918
|
const socket = new net3.Socket();
|
|
4657
4919
|
let settled = false;
|
|
4658
4920
|
const done = (ok) => {
|
|
4659
4921
|
if (settled) return;
|
|
4660
4922
|
settled = true;
|
|
4661
4923
|
socket.destroy();
|
|
4662
|
-
|
|
4924
|
+
resolve16(ok);
|
|
4663
4925
|
};
|
|
4664
4926
|
socket.setTimeout(timeoutMs);
|
|
4665
4927
|
socket.once("connect", () => done(true));
|
|
@@ -5118,7 +5380,7 @@ async function startTokenServer(port, context) {
|
|
|
5118
5380
|
})();
|
|
5119
5381
|
});
|
|
5120
5382
|
server.on("error", (e) => logWarn(`token-server error: ${e.message}`));
|
|
5121
|
-
await new Promise((
|
|
5383
|
+
await new Promise((resolve16) => server.listen(port, "127.0.0.1", resolve16));
|
|
5122
5384
|
logInfo(`dev-user token-server on http://127.0.0.1:${port} (context ${context})`);
|
|
5123
5385
|
await new Promise(() => {
|
|
5124
5386
|
});
|
|
@@ -5141,6 +5403,26 @@ import * as os6 from "os";
|
|
|
5141
5403
|
import * as path15 from "path";
|
|
5142
5404
|
import { Option } from "commander";
|
|
5143
5405
|
import { SecretsManagerClient as SecretsManagerClient2, GetSecretValueCommand as GetSecretValueCommand2 } from "@aws-sdk/client-secrets-manager";
|
|
5406
|
+
function registerServiceProbes(devOutput, tenant, probeFiles) {
|
|
5407
|
+
const services = {};
|
|
5408
|
+
for (const [name, service] of Object.entries(devOutput.services)) {
|
|
5409
|
+
if (name.startsWith("mock-")) continue;
|
|
5410
|
+
services[name] = service.port;
|
|
5411
|
+
}
|
|
5412
|
+
try {
|
|
5413
|
+
const file = writeAppServiceProbes({
|
|
5414
|
+
tenant,
|
|
5415
|
+
env: devOutput.platform?.env ?? "dev",
|
|
5416
|
+
app: devOutput.app ?? "",
|
|
5417
|
+
services
|
|
5418
|
+
});
|
|
5419
|
+
if (file) probeFiles.push(file);
|
|
5420
|
+
} catch (err) {
|
|
5421
|
+
logWarn(
|
|
5422
|
+
`Could not register uptime probes for this session (${err instanceof Error ? err.message : err}) \u2014 the Hub will show no uptime for these services.`
|
|
5423
|
+
);
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5144
5426
|
function deriveSessionName(projectName, wt) {
|
|
5145
5427
|
return wt.isPrimary ? `${projectName}-dev` : `${projectName}-${wt.slug}`;
|
|
5146
5428
|
}
|
|
@@ -5228,10 +5510,10 @@ function removeSessionState(sessionName) {
|
|
|
5228
5510
|
}
|
|
5229
5511
|
}
|
|
5230
5512
|
async function isPortFree(port) {
|
|
5231
|
-
const bindSucceeds = (host) => new Promise((
|
|
5513
|
+
const bindSucceeds = (host) => new Promise((resolve16) => {
|
|
5232
5514
|
const server = net4.createServer();
|
|
5233
|
-
server.once("error", () =>
|
|
5234
|
-
const onListening = () => server.close(() =>
|
|
5515
|
+
server.once("error", () => resolve16(false));
|
|
5516
|
+
const onListening = () => server.close(() => resolve16(true));
|
|
5235
5517
|
if (host === void 0) server.listen(port, onListening);
|
|
5236
5518
|
else server.listen(port, host, onListening);
|
|
5237
5519
|
});
|
|
@@ -5239,24 +5521,24 @@ async function isPortFree(port) {
|
|
|
5239
5521
|
return bindSucceeds("127.0.0.1");
|
|
5240
5522
|
}
|
|
5241
5523
|
async function findFreePort() {
|
|
5242
|
-
return new Promise((
|
|
5524
|
+
return new Promise((resolve16, reject) => {
|
|
5243
5525
|
const server = net4.createServer();
|
|
5244
5526
|
server.once("error", reject);
|
|
5245
5527
|
server.listen(0, "127.0.0.1", () => {
|
|
5246
5528
|
const { port } = server.address();
|
|
5247
|
-
server.close(() =>
|
|
5529
|
+
server.close(() => resolve16(port));
|
|
5248
5530
|
});
|
|
5249
5531
|
});
|
|
5250
5532
|
}
|
|
5251
5533
|
function isPortListening(port) {
|
|
5252
|
-
return new Promise((
|
|
5534
|
+
return new Promise((resolve16) => {
|
|
5253
5535
|
const s = new net4.Socket();
|
|
5254
5536
|
let done = false;
|
|
5255
5537
|
const fin = (ok) => {
|
|
5256
5538
|
if (done) return;
|
|
5257
5539
|
done = true;
|
|
5258
5540
|
s.destroy();
|
|
5259
|
-
|
|
5541
|
+
resolve16(ok);
|
|
5260
5542
|
};
|
|
5261
5543
|
s.setTimeout(300);
|
|
5262
5544
|
s.once("connect", () => fin(true));
|
|
@@ -6789,6 +7071,8 @@ function registerDevCommand(program2) {
|
|
|
6789
7071
|
}
|
|
6790
7072
|
return;
|
|
6791
7073
|
}
|
|
7074
|
+
let signInServices = [];
|
|
7075
|
+
let appVersion;
|
|
6792
7076
|
try {
|
|
6793
7077
|
const tenant = localTenant;
|
|
6794
7078
|
const app = rawDevOutput2.app ?? projectName;
|
|
@@ -6796,6 +7080,7 @@ function registerDevCommand(program2) {
|
|
|
6796
7080
|
const services = Object.keys(rawDevOutput2.services);
|
|
6797
7081
|
const authServices = services.filter((name) => !name.startsWith("mock-"));
|
|
6798
7082
|
let authRoles = [];
|
|
7083
|
+
signInServices = [];
|
|
6799
7084
|
try {
|
|
6800
7085
|
const appPkg = JSON.parse(
|
|
6801
7086
|
fs13.readFileSync(path15.join(appRoot, "package.json"), "utf-8")
|
|
@@ -6803,6 +7088,10 @@ function registerDevCommand(program2) {
|
|
|
6803
7088
|
if (Array.isArray(appPkg?.mesh?.auth?.roles)) {
|
|
6804
7089
|
authRoles = appPkg.mesh.auth.roles.filter((r) => typeof r === "string");
|
|
6805
7090
|
}
|
|
7091
|
+
if (Array.isArray(appPkg?.mesh?.auth?.signIn)) {
|
|
7092
|
+
signInServices = appPkg.mesh.auth.signIn.filter((r) => typeof r === "string");
|
|
7093
|
+
}
|
|
7094
|
+
if (typeof appPkg?.version === "string") appVersion = appPkg.version;
|
|
6806
7095
|
} catch {
|
|
6807
7096
|
}
|
|
6808
7097
|
await ensureAppTenantAuth({ tenant, app, services: authServices, roles: authRoles });
|
|
@@ -6813,10 +7102,30 @@ function registerDevCommand(program2) {
|
|
|
6813
7102
|
);
|
|
6814
7103
|
}
|
|
6815
7104
|
const devOutput2 = await allocatePorts(rawDevOutput2, worktree);
|
|
7105
|
+
for (const service of signInServices) {
|
|
7106
|
+
const port = devOutput2.services[service]?.port;
|
|
7107
|
+
if (!port) {
|
|
7108
|
+
logWarn(`mesh.auth.signIn names '${service}', which this app does not run \u2014 no sign-in app registered.`);
|
|
7109
|
+
continue;
|
|
7110
|
+
}
|
|
7111
|
+
try {
|
|
7112
|
+
await ensureSignInApp({
|
|
7113
|
+
tenant: localTenant,
|
|
7114
|
+
app: devOutput2.app ?? projectName,
|
|
7115
|
+
service,
|
|
7116
|
+
baseUrl: `http://localhost:${port}`
|
|
7117
|
+
});
|
|
7118
|
+
} catch (err) {
|
|
7119
|
+
logWarn(
|
|
7120
|
+
`Could not register the browser sign-in for '${service}' (${err instanceof Error ? err.message : err}) \u2014 the Hub's Access \u2192 Sign-in tab will report this app has no login.`
|
|
7121
|
+
);
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
6816
7124
|
try {
|
|
6817
7125
|
await registerLocalApp({
|
|
6818
7126
|
tenant: localTenant,
|
|
6819
7127
|
app: devOutput2.app ?? projectName,
|
|
7128
|
+
version: appVersion,
|
|
6820
7129
|
services: Object.keys(devOutput2.services),
|
|
6821
7130
|
ports: Object.fromEntries(
|
|
6822
7131
|
Object.entries(devOutput2.services).map(([name, svc]) => [name, svc.port])
|
|
@@ -6893,6 +7202,7 @@ function registerDevCommand(program2) {
|
|
|
6893
7202
|
);
|
|
6894
7203
|
logInfo(`Docker runner: ${composePath}`);
|
|
6895
7204
|
dockerDevUp(sessionName);
|
|
7205
|
+
registerServiceProbes(devOutput2, localTenant, externalProbeFiles);
|
|
6896
7206
|
saveSessionState(sessionName, {
|
|
6897
7207
|
appRoot,
|
|
6898
7208
|
stack: "local",
|
|
@@ -6924,6 +7234,7 @@ function registerDevCommand(program2) {
|
|
|
6924
7234
|
{ transport: "vpn-direct" }
|
|
6925
7235
|
// local mode: no tunnels
|
|
6926
7236
|
);
|
|
7237
|
+
registerServiceProbes(finalDevOutput2, localTenant, externalProbeFiles);
|
|
6927
7238
|
saveSessionState(sessionName, {
|
|
6928
7239
|
appRoot,
|
|
6929
7240
|
stack: "local",
|
|
@@ -7297,8 +7608,8 @@ function registerDevCommand(program2) {
|
|
|
7297
7608
|
});
|
|
7298
7609
|
dev.command("test-user [name]").description("Get Temporal test user credentials (from Pulumi-managed test users)").option("--tenant <tenant>", "Tenant name", "mesh").option("--env <env>", "Environment", "dev").option("--region <region>", "AWS region", "us-east-2").action(
|
|
7299
7610
|
async (name, opts) => {
|
|
7300
|
-
const { SSMClient:
|
|
7301
|
-
const ssm = new
|
|
7611
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3 } = await import("@aws-sdk/client-ssm");
|
|
7612
|
+
const ssm = new SSMClient5({ region: opts.region });
|
|
7302
7613
|
const basePath = `/mesh-platform/${opts.tenant}/${opts.env}/temporal/test-users`;
|
|
7303
7614
|
if (!name) {
|
|
7304
7615
|
try {
|
|
@@ -7395,6 +7706,7 @@ var init_dev = __esm({
|
|
|
7395
7706
|
init_seed();
|
|
7396
7707
|
init_mocks();
|
|
7397
7708
|
init_dev_local();
|
|
7709
|
+
init_stack();
|
|
7398
7710
|
init_docker_runner();
|
|
7399
7711
|
init_errors();
|
|
7400
7712
|
init_stack_flag();
|
|
@@ -7626,7 +7938,7 @@ function discoverRunnerPids(tenant, exclude) {
|
|
|
7626
7938
|
}
|
|
7627
7939
|
}
|
|
7628
7940
|
function startControlServer(payload) {
|
|
7629
|
-
return new Promise((
|
|
7941
|
+
return new Promise((resolve16, reject) => {
|
|
7630
7942
|
const server = net6.createServer((socket) => {
|
|
7631
7943
|
socket.on("error", () => {
|
|
7632
7944
|
});
|
|
@@ -7639,12 +7951,12 @@ function startControlServer(payload) {
|
|
|
7639
7951
|
});
|
|
7640
7952
|
server.on("error", reject);
|
|
7641
7953
|
server.listen(0, "127.0.0.1", () => {
|
|
7642
|
-
|
|
7954
|
+
resolve16({ server, port: server.address().port });
|
|
7643
7955
|
});
|
|
7644
7956
|
});
|
|
7645
7957
|
}
|
|
7646
7958
|
function queryControl(port, timeoutMs = 700) {
|
|
7647
|
-
return new Promise((
|
|
7959
|
+
return new Promise((resolve16) => {
|
|
7648
7960
|
const s = new net6.Socket();
|
|
7649
7961
|
let buf = "";
|
|
7650
7962
|
let done = false;
|
|
@@ -7652,7 +7964,7 @@ function queryControl(port, timeoutMs = 700) {
|
|
|
7652
7964
|
if (done) return;
|
|
7653
7965
|
done = true;
|
|
7654
7966
|
s.destroy();
|
|
7655
|
-
|
|
7967
|
+
resolve16(v);
|
|
7656
7968
|
};
|
|
7657
7969
|
s.setTimeout(timeoutMs);
|
|
7658
7970
|
s.once("timeout", () => fin(null));
|
|
@@ -7877,14 +8189,14 @@ function findRunningDaemon(tenant) {
|
|
|
7877
8189
|
return null;
|
|
7878
8190
|
}
|
|
7879
8191
|
function portAccepts(port, timeoutMs = 400) {
|
|
7880
|
-
return new Promise((
|
|
8192
|
+
return new Promise((resolve16) => {
|
|
7881
8193
|
const s = new net7.Socket();
|
|
7882
8194
|
let done = false;
|
|
7883
8195
|
const fin = (v) => {
|
|
7884
8196
|
if (done) return;
|
|
7885
8197
|
done = true;
|
|
7886
8198
|
s.destroy();
|
|
7887
|
-
|
|
8199
|
+
resolve16(v);
|
|
7888
8200
|
};
|
|
7889
8201
|
s.setTimeout(timeoutMs);
|
|
7890
8202
|
s.once("connect", () => fin(true));
|
|
@@ -8422,24 +8734,24 @@ function deriveLoginServer2(context) {
|
|
|
8422
8734
|
return `https://${parts.join(".")}`;
|
|
8423
8735
|
}
|
|
8424
8736
|
function findFreePort2() {
|
|
8425
|
-
return new Promise((
|
|
8737
|
+
return new Promise((resolve16, reject) => {
|
|
8426
8738
|
const s = net8.createServer();
|
|
8427
8739
|
s.on("error", reject);
|
|
8428
8740
|
s.listen(0, "127.0.0.1", () => {
|
|
8429
8741
|
const port = s.address().port;
|
|
8430
|
-
s.close(() =>
|
|
8742
|
+
s.close(() => resolve16(port));
|
|
8431
8743
|
});
|
|
8432
8744
|
});
|
|
8433
8745
|
}
|
|
8434
8746
|
function canConnect2(port) {
|
|
8435
|
-
return new Promise((
|
|
8747
|
+
return new Promise((resolve16) => {
|
|
8436
8748
|
const s = new net8.Socket();
|
|
8437
8749
|
let settled = false;
|
|
8438
8750
|
const done = (ok) => {
|
|
8439
8751
|
if (settled) return;
|
|
8440
8752
|
settled = true;
|
|
8441
8753
|
s.destroy();
|
|
8442
|
-
|
|
8754
|
+
resolve16(ok);
|
|
8443
8755
|
};
|
|
8444
8756
|
s.setTimeout(500);
|
|
8445
8757
|
s.once("connect", () => done(true));
|
|
@@ -8798,13 +9110,13 @@ function parseContextTenantEnv(context) {
|
|
|
8798
9110
|
return { tenant: tenant || "mesh", env: env || "dev" };
|
|
8799
9111
|
}
|
|
8800
9112
|
function findFreePort3() {
|
|
8801
|
-
return new Promise((
|
|
9113
|
+
return new Promise((resolve16, reject) => {
|
|
8802
9114
|
const srv = net9.createServer();
|
|
8803
9115
|
srv.on("error", reject);
|
|
8804
9116
|
srv.listen(0, "127.0.0.1", () => {
|
|
8805
9117
|
const addr = srv.address();
|
|
8806
9118
|
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
8807
|
-
srv.close(() =>
|
|
9119
|
+
srv.close(() => resolve16(port));
|
|
8808
9120
|
});
|
|
8809
9121
|
});
|
|
8810
9122
|
}
|
|
@@ -9291,9 +9603,9 @@ function parseTenantEnv(context) {
|
|
|
9291
9603
|
}
|
|
9292
9604
|
async function getRegistrySsmExport(tenant, env) {
|
|
9293
9605
|
try {
|
|
9294
|
-
const { SSMClient:
|
|
9606
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
9295
9607
|
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? CA_REGION;
|
|
9296
|
-
const ssm = new
|
|
9608
|
+
const ssm = new SSMClient5({ region });
|
|
9297
9609
|
const ssmPath = `/mesh-platform/${tenant}/${env}/registry`;
|
|
9298
9610
|
const response = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
|
|
9299
9611
|
if (!response.Parameter?.Value) return null;
|
|
@@ -9883,9 +10195,9 @@ async function discoverConfigFromSsm(context) {
|
|
|
9883
10195
|
const ssmPath = `/mesh-platform/${tenant}/${env}/platform/zitadel`;
|
|
9884
10196
|
logInfo(`Attempting SSM discovery from ${ssmPath}...`);
|
|
9885
10197
|
try {
|
|
9886
|
-
const { SSMClient:
|
|
10198
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
9887
10199
|
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
|
|
9888
|
-
const ssm = new
|
|
10200
|
+
const ssm = new SSMClient5({ region });
|
|
9889
10201
|
const resp = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
|
|
9890
10202
|
const raw = resp.Parameter?.Value;
|
|
9891
10203
|
if (!raw) {
|
|
@@ -9978,11 +10290,11 @@ function readAllCredentials() {
|
|
|
9978
10290
|
function readCredentials(context) {
|
|
9979
10291
|
return readAllCredentials()[context] ?? null;
|
|
9980
10292
|
}
|
|
9981
|
-
function atomicWriteFileSync(
|
|
9982
|
-
const tmpPath = `${
|
|
10293
|
+
function atomicWriteFileSync(path42, data, mode) {
|
|
10294
|
+
const tmpPath = `${path42}.${process.pid}.${atomicWriteCounter++}.tmp`;
|
|
9983
10295
|
try {
|
|
9984
10296
|
fs19.writeFileSync(tmpPath, data, { mode });
|
|
9985
|
-
fs19.renameSync(tmpPath,
|
|
10297
|
+
fs19.renameSync(tmpPath, path42);
|
|
9986
10298
|
} catch (err) {
|
|
9987
10299
|
try {
|
|
9988
10300
|
fs19.unlinkSync(tmpPath);
|
|
@@ -10055,7 +10367,7 @@ async function refreshTokens(issuer, clientId, refreshToken) {
|
|
|
10055
10367
|
return resp.json();
|
|
10056
10368
|
}
|
|
10057
10369
|
function login(context, config) {
|
|
10058
|
-
return new Promise((
|
|
10370
|
+
return new Promise((resolve16, reject) => {
|
|
10059
10371
|
const codeVerifier = generateCodeVerifier();
|
|
10060
10372
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
10061
10373
|
const state = base64url(crypto3.randomBytes(16));
|
|
@@ -10139,7 +10451,7 @@ function login(context, config) {
|
|
|
10139
10451
|
}
|
|
10140
10452
|
hintVpnIfDisconnected(context);
|
|
10141
10453
|
teardown();
|
|
10142
|
-
|
|
10454
|
+
resolve16();
|
|
10143
10455
|
} catch (err) {
|
|
10144
10456
|
teardown();
|
|
10145
10457
|
reject(err);
|
|
@@ -11734,6 +12046,7 @@ __export(create_app_exports, {
|
|
|
11734
12046
|
ensureWorkspaceGlobs: () => ensureWorkspaceGlobs,
|
|
11735
12047
|
isInsidePlatformMonorepo: () => isInsidePlatformMonorepo,
|
|
11736
12048
|
registerCreateAppCommand: () => registerCreateAppCommand,
|
|
12049
|
+
resolveAppDir: () => resolveAppDir,
|
|
11737
12050
|
shouldBootstrapAppsRepo: () => shouldBootstrapAppsRepo,
|
|
11738
12051
|
workspaceGlobForApp: () => workspaceGlobForApp
|
|
11739
12052
|
});
|
|
@@ -11918,7 +12231,7 @@ function parsePrimitives(input2) {
|
|
|
11918
12231
|
}
|
|
11919
12232
|
function resolveAppDir(cwd, tenant, name, test) {
|
|
11920
12233
|
const baseDir = test ? "tests/tenants" : "tenants";
|
|
11921
|
-
const possiblePaths = [
|
|
12234
|
+
const possiblePaths = test ? [path24.join(cwd, baseDir, tenant, "apps")] : [
|
|
11922
12235
|
path24.join(cwd, baseDir, tenant, "apps"),
|
|
11923
12236
|
path24.join(cwd, "..", tenant, "apps"),
|
|
11924
12237
|
path24.join(cwd, "apps")
|
|
@@ -12431,26 +12744,26 @@ async function startTunnelBackground(instanceId, rdsHost, rdsPort, localPort) {
|
|
|
12431
12744
|
throw new Error(`Tunnel failed to start after ${maxAttempts} seconds`);
|
|
12432
12745
|
}
|
|
12433
12746
|
function checkPort(port) {
|
|
12434
|
-
return new Promise((
|
|
12747
|
+
return new Promise((resolve16) => {
|
|
12435
12748
|
const socket = new net10.Socket();
|
|
12436
12749
|
socket.setTimeout(500);
|
|
12437
12750
|
socket.on("connect", () => {
|
|
12438
12751
|
socket.destroy();
|
|
12439
|
-
|
|
12752
|
+
resolve16(true);
|
|
12440
12753
|
});
|
|
12441
12754
|
socket.on("timeout", () => {
|
|
12442
12755
|
socket.destroy();
|
|
12443
|
-
|
|
12756
|
+
resolve16(false);
|
|
12444
12757
|
});
|
|
12445
12758
|
socket.on("error", () => {
|
|
12446
12759
|
socket.destroy();
|
|
12447
|
-
|
|
12760
|
+
resolve16(false);
|
|
12448
12761
|
});
|
|
12449
12762
|
socket.connect(port, "localhost");
|
|
12450
12763
|
});
|
|
12451
12764
|
}
|
|
12452
12765
|
function sleep2(ms) {
|
|
12453
|
-
return new Promise((
|
|
12766
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
12454
12767
|
}
|
|
12455
12768
|
async function psqlCommand(options) {
|
|
12456
12769
|
let platformTenant;
|
|
@@ -12476,13 +12789,13 @@ async function psqlCommand(options) {
|
|
|
12476
12789
|
logInfo(`App credentials: tenant=${appTenant}, stage=${appStage}, app=${appName}`);
|
|
12477
12790
|
}
|
|
12478
12791
|
const psqlCheck = spawn5("which", ["psql"]);
|
|
12479
|
-
await new Promise((
|
|
12792
|
+
await new Promise((resolve16, reject) => {
|
|
12480
12793
|
psqlCheck.on("exit", (code) => {
|
|
12481
12794
|
if (code !== 0) {
|
|
12482
12795
|
logError("psql not found. Install with: brew install postgresql");
|
|
12483
12796
|
reject(new Error("psql not found"));
|
|
12484
12797
|
} else {
|
|
12485
|
-
|
|
12798
|
+
resolve16();
|
|
12486
12799
|
}
|
|
12487
12800
|
});
|
|
12488
12801
|
});
|
|
@@ -12569,26 +12882,26 @@ function findSstOutputs() {
|
|
|
12569
12882
|
return null;
|
|
12570
12883
|
}
|
|
12571
12884
|
function checkPort2(port) {
|
|
12572
|
-
return new Promise((
|
|
12885
|
+
return new Promise((resolve16) => {
|
|
12573
12886
|
const socket = new net11.Socket();
|
|
12574
12887
|
socket.setTimeout(500);
|
|
12575
12888
|
socket.on("connect", () => {
|
|
12576
12889
|
socket.destroy();
|
|
12577
|
-
|
|
12890
|
+
resolve16(true);
|
|
12578
12891
|
});
|
|
12579
12892
|
socket.on("timeout", () => {
|
|
12580
12893
|
socket.destroy();
|
|
12581
|
-
|
|
12894
|
+
resolve16(false);
|
|
12582
12895
|
});
|
|
12583
12896
|
socket.on("error", () => {
|
|
12584
12897
|
socket.destroy();
|
|
12585
|
-
|
|
12898
|
+
resolve16(false);
|
|
12586
12899
|
});
|
|
12587
12900
|
socket.connect(port, "localhost");
|
|
12588
12901
|
});
|
|
12589
12902
|
}
|
|
12590
12903
|
function sleep3(ms) {
|
|
12591
|
-
return new Promise((
|
|
12904
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
12592
12905
|
}
|
|
12593
12906
|
async function startTunnelBackground2(instanceId, rdsEndpoint, rdsPort, localPort) {
|
|
12594
12907
|
logInfo("Starting database tunnel...");
|
|
@@ -13308,7 +13621,7 @@ var init_discover = __esm({
|
|
|
13308
13621
|
|
|
13309
13622
|
// libs/mesh-cli/src/docs/assemble.ts
|
|
13310
13623
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
13311
|
-
import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as
|
|
13624
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync5, writeFileSync as writeFileSync16 } from "node:fs";
|
|
13312
13625
|
import path27 from "node:path";
|
|
13313
13626
|
import { parse as parseYaml4 } from "yaml";
|
|
13314
13627
|
function splitFrontMatter(markdown) {
|
|
@@ -13621,7 +13934,7 @@ function assemblePortal(options) {
|
|
|
13621
13934
|
`;
|
|
13622
13935
|
const outFile = path27.join(outDir, page.filePath);
|
|
13623
13936
|
mkdirSync15(path27.dirname(outFile), { recursive: true });
|
|
13624
|
-
|
|
13937
|
+
writeFileSync16(
|
|
13625
13938
|
outFile,
|
|
13626
13939
|
joinFrontMatter(outData, rewritten.body.trimEnd() + sourceNote)
|
|
13627
13940
|
);
|
|
@@ -13928,8 +14241,8 @@ function formatDefault(value, paths = currentMachinePaths()) {
|
|
|
13928
14241
|
if (typeof value === "boolean" || typeof value === "number") return String(value);
|
|
13929
14242
|
return normalizeMachinePaths(JSON.stringify(value), paths);
|
|
13930
14243
|
}
|
|
13931
|
-
function slugifyCommandPath(
|
|
13932
|
-
return
|
|
14244
|
+
function slugifyCommandPath(path42) {
|
|
14245
|
+
return path42.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
13933
14246
|
}
|
|
13934
14247
|
function extractOptions(command) {
|
|
13935
14248
|
const options = command.options ?? [];
|
|
@@ -13963,17 +14276,17 @@ function isUndocumented(command) {
|
|
|
13963
14276
|
function extractCommand(command, parentPath, depth) {
|
|
13964
14277
|
const internals = command;
|
|
13965
14278
|
const name = internals._name ?? "";
|
|
13966
|
-
const
|
|
14279
|
+
const path42 = parentPath ? `${parentPath} ${name}` : name;
|
|
13967
14280
|
return {
|
|
13968
|
-
path:
|
|
14281
|
+
path: path42,
|
|
13969
14282
|
name,
|
|
13970
14283
|
aliases: [...internals._aliases ?? []],
|
|
13971
14284
|
description: internals._description ?? "",
|
|
13972
14285
|
args: extractArgs(command),
|
|
13973
14286
|
options: extractOptions(command),
|
|
13974
|
-
slug: slugifyCommandPath(
|
|
14287
|
+
slug: slugifyCommandPath(path42),
|
|
13975
14288
|
depth,
|
|
13976
|
-
subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child,
|
|
14289
|
+
subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, path42, depth + 1))
|
|
13977
14290
|
};
|
|
13978
14291
|
}
|
|
13979
14292
|
function extractCliReference(program2) {
|
|
@@ -14069,7 +14382,7 @@ import {
|
|
|
14069
14382
|
mkdirSync as mkdirSync16,
|
|
14070
14383
|
mkdtempSync as mkdtempSync3,
|
|
14071
14384
|
readFileSync as readFileSync24,
|
|
14072
|
-
writeFileSync as
|
|
14385
|
+
writeFileSync as writeFileSync17
|
|
14073
14386
|
} from "node:fs";
|
|
14074
14387
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
14075
14388
|
import path28 from "node:path";
|
|
@@ -14085,13 +14398,13 @@ function runAssemble(args) {
|
|
|
14085
14398
|
const errors = [...discovery.errors];
|
|
14086
14399
|
if (args.writeFiles !== false) {
|
|
14087
14400
|
const manifestPath = args.outDir.includes(tmpdir6()) ? path28.join(args.outDir, PUBLISH_MANIFEST) : path28.join(args.outDir, "..", PUBLISH_MANIFEST);
|
|
14088
|
-
|
|
14401
|
+
writeFileSync17(
|
|
14089
14402
|
manifestPath,
|
|
14090
14403
|
`${JSON.stringify(assembled.publishManifest, null, 2)}
|
|
14091
14404
|
`
|
|
14092
14405
|
);
|
|
14093
14406
|
if (args.configOut) {
|
|
14094
|
-
|
|
14407
|
+
writeFileSync17(
|
|
14095
14408
|
args.configOut,
|
|
14096
14409
|
renderZudokuConfig({
|
|
14097
14410
|
navigation: assembled.navigation,
|
|
@@ -14106,7 +14419,7 @@ function runAssemble(args) {
|
|
|
14106
14419
|
VERSION_JSON_DIR
|
|
14107
14420
|
);
|
|
14108
14421
|
mkdirSync16(versionDir, { recursive: true });
|
|
14109
|
-
|
|
14422
|
+
writeFileSync17(
|
|
14110
14423
|
path28.join(versionDir, VERSION_JSON),
|
|
14111
14424
|
renderVersionJson({
|
|
14112
14425
|
baseline: currentBaseline(args.repoRoot),
|
|
@@ -14161,7 +14474,7 @@ async function checkPortal(args) {
|
|
|
14161
14474
|
}
|
|
14162
14475
|
if (args.manifestOut) {
|
|
14163
14476
|
mkdirSync16(path28.dirname(args.manifestOut), { recursive: true });
|
|
14164
|
-
|
|
14477
|
+
writeFileSync17(
|
|
14165
14478
|
args.manifestOut,
|
|
14166
14479
|
`${JSON.stringify(result.assembled.publishManifest, null, 2)}
|
|
14167
14480
|
`
|
|
@@ -14201,14 +14514,14 @@ ${summary}`);
|
|
|
14201
14514
|
function runZudoku(args) {
|
|
14202
14515
|
const cliArgs = args.mode === "dev" ? ["dev", "--port", String(args.port ?? 3e3)] : ["build"];
|
|
14203
14516
|
logInfo(`Running \`zudoku ${cliArgs.join(" ")}\` in ${args.appDir}`);
|
|
14204
|
-
return new Promise((
|
|
14517
|
+
return new Promise((resolve16, reject) => {
|
|
14205
14518
|
const child = spawn7("pnpm", ["exec", "zudoku", ...cliArgs], {
|
|
14206
14519
|
cwd: args.appDir,
|
|
14207
14520
|
stdio: "inherit",
|
|
14208
14521
|
env: process.env
|
|
14209
14522
|
});
|
|
14210
14523
|
child.on("error", reject);
|
|
14211
|
-
child.on("close", (code) =>
|
|
14524
|
+
child.on("close", (code) => resolve16(code ?? 1));
|
|
14212
14525
|
});
|
|
14213
14526
|
}
|
|
14214
14527
|
async function runPortalCommand(opts, deps) {
|
|
@@ -14419,17 +14732,17 @@ async function serveDocsSite(args) {
|
|
|
14419
14732
|
res.writeHead(500).end("internal error");
|
|
14420
14733
|
});
|
|
14421
14734
|
});
|
|
14422
|
-
await new Promise((
|
|
14735
|
+
await new Promise((resolve16, reject) => {
|
|
14423
14736
|
server.once("error", reject);
|
|
14424
|
-
server.listen(args.port, "127.0.0.1", () =>
|
|
14737
|
+
server.listen(args.port, "127.0.0.1", () => resolve16());
|
|
14425
14738
|
});
|
|
14426
14739
|
const address = server.address();
|
|
14427
14740
|
const port = typeof address === "object" && address ? address.port : args.port;
|
|
14428
14741
|
return {
|
|
14429
14742
|
url: `http://127.0.0.1:${port}`,
|
|
14430
14743
|
port,
|
|
14431
|
-
close: () => new Promise((
|
|
14432
|
-
server.close((error) => error ? reject(error) :
|
|
14744
|
+
close: () => new Promise((resolve16, reject) => {
|
|
14745
|
+
server.close((error) => error ? reject(error) : resolve16());
|
|
14433
14746
|
})
|
|
14434
14747
|
};
|
|
14435
14748
|
}
|
|
@@ -14461,7 +14774,7 @@ var init_serve = __esm({
|
|
|
14461
14774
|
|
|
14462
14775
|
// libs/mesh-cli/src/docs/registry-docs.ts
|
|
14463
14776
|
import { execFileSync as execFileSync22 } from "node:child_process";
|
|
14464
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync26, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as
|
|
14777
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync26, renameSync as renameSync3, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
|
|
14465
14778
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
14466
14779
|
import path30 from "node:path";
|
|
14467
14780
|
function readDocsRegistryAuth(npmrcPath2 = homeNpmrcPath()) {
|
|
@@ -14547,7 +14860,7 @@ async function fetchDocsArtifact(auth, version, cacheRoot = docsCacheRoot(), fet
|
|
|
14547
14860
|
}
|
|
14548
14861
|
mkdirSync17(cacheRoot, { recursive: true });
|
|
14549
14862
|
const tgzPath = path30.join(cacheRoot, `.${version}.tgz`);
|
|
14550
|
-
|
|
14863
|
+
writeFileSync18(tgzPath, Buffer.from(await tgzResponse.arrayBuffer()));
|
|
14551
14864
|
const staging = path30.join(cacheRoot, `.staging-${version}`);
|
|
14552
14865
|
rmSync6(staging, { recursive: true, force: true });
|
|
14553
14866
|
mkdirSync17(staging, { recursive: true });
|
|
@@ -14591,7 +14904,7 @@ __export(start_exports, {
|
|
|
14591
14904
|
tmuxServeArgs: () => tmuxServeArgs
|
|
14592
14905
|
});
|
|
14593
14906
|
import { execFileSync as execFileSync23 } from "node:child_process";
|
|
14594
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync24, writeFileSync as
|
|
14907
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync24, writeFileSync as writeFileSync19 } from "node:fs";
|
|
14595
14908
|
import path31 from "node:path";
|
|
14596
14909
|
function registryAuthOrThrow() {
|
|
14597
14910
|
const auth = readDocsRegistryAuth();
|
|
@@ -14682,7 +14995,7 @@ Or run in the foreground instead: mesh docs start --foreground`
|
|
|
14682
14995
|
} catch (error) {
|
|
14683
14996
|
lastError = error instanceof Error ? error.message : String(error);
|
|
14684
14997
|
}
|
|
14685
|
-
await new Promise((
|
|
14998
|
+
await new Promise((resolve16) => setTimeout(resolve16, 500));
|
|
14686
14999
|
}
|
|
14687
15000
|
throw new Error(
|
|
14688
15001
|
`the docs server did not answer at ${url} within 60s (${lastError || "no response"}).
|
|
@@ -14719,8 +15032,8 @@ async function runDocsServeStatic(args) {
|
|
|
14719
15032
|
}
|
|
14720
15033
|
const server = await serveDocsSite({ root: args.root, port: requestedPort });
|
|
14721
15034
|
logSuccess(`Mesh docs serving at ${server.url} (from ${args.root})`);
|
|
14722
|
-
await new Promise((
|
|
14723
|
-
const stop = () => void server.close().finally(() =>
|
|
15035
|
+
await new Promise((resolve16) => {
|
|
15036
|
+
const stop = () => void server.close().finally(() => resolve16());
|
|
14724
15037
|
process.once("SIGINT", stop);
|
|
14725
15038
|
process.once("SIGTERM", stop);
|
|
14726
15039
|
});
|
|
@@ -14774,8 +15087,8 @@ async function runDocsStart(args) {
|
|
|
14774
15087
|
stdio: "inherit",
|
|
14775
15088
|
env: process.env
|
|
14776
15089
|
});
|
|
14777
|
-
await new Promise((
|
|
14778
|
-
child.on("close", () =>
|
|
15090
|
+
await new Promise((resolve16) => {
|
|
15091
|
+
child.on("close", () => resolve16());
|
|
14779
15092
|
process.once("SIGINT", () => child.kill("SIGINT"));
|
|
14780
15093
|
process.once("SIGTERM", () => child.kill("SIGTERM"));
|
|
14781
15094
|
});
|
|
@@ -14790,8 +15103,8 @@ async function runDocsStart(args) {
|
|
|
14790
15103
|
});
|
|
14791
15104
|
reportAssembly(result);
|
|
14792
15105
|
const { spawn: spawn10 } = await import("node:child_process");
|
|
14793
|
-
|
|
14794
|
-
const code = await new Promise((
|
|
15106
|
+
writeFileSync19(buildLog, "");
|
|
15107
|
+
const code = await new Promise((resolve16) => {
|
|
14795
15108
|
const child = spawn10("pnpm", ["exec", "zudoku", "build"], {
|
|
14796
15109
|
cwd: appDir,
|
|
14797
15110
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14799,8 +15112,8 @@ async function runDocsStart(args) {
|
|
|
14799
15112
|
});
|
|
14800
15113
|
child.stdout?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
|
|
14801
15114
|
child.stderr?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
|
|
14802
|
-
child.on("error", () =>
|
|
14803
|
-
child.on("close", (exitCode) =>
|
|
15115
|
+
child.on("error", () => resolve16(1));
|
|
15116
|
+
child.on("close", (exitCode) => resolve16(exitCode ?? 1));
|
|
14804
15117
|
});
|
|
14805
15118
|
if (code !== 0) throw new Error(`zudoku build exited with code ${code} \u2014 see ${buildLog}`);
|
|
14806
15119
|
serveRoot = path31.join(appDir, "dist");
|
|
@@ -14820,9 +15133,9 @@ async function runDocsStart(args) {
|
|
|
14820
15133
|
const server = await serveDocsSite({ root: serveRoot, port: requestedPort });
|
|
14821
15134
|
logSuccess(`Mesh docs (${label}) serving at ${server.url}`);
|
|
14822
15135
|
logInfo("Press Ctrl-C to stop");
|
|
14823
|
-
await new Promise((
|
|
15136
|
+
await new Promise((resolve16) => {
|
|
14824
15137
|
const stop = () => {
|
|
14825
|
-
void server.close().finally(() =>
|
|
15138
|
+
void server.close().finally(() => resolve16());
|
|
14826
15139
|
};
|
|
14827
15140
|
process.once("SIGINT", stop);
|
|
14828
15141
|
process.once("SIGTERM", stop);
|
|
@@ -15174,10 +15487,10 @@ function assembleHubEnv(session, sessionEnv, opts) {
|
|
|
15174
15487
|
{ remediation: { command: "mesh dev --kill && mesh dev # relaunch to refresh session state" } }
|
|
15175
15488
|
);
|
|
15176
15489
|
}
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15490
|
+
const isLocalPlatform = platform.tenant === "local";
|
|
15491
|
+
if (isLocalPlatform) {
|
|
15492
|
+
notes.push(
|
|
15493
|
+
"Local platform session \u2014 the Hub runs from this checkout against `mesh start`. Its containerized Hub keeps :9000, so pass --port/--api-port to run both."
|
|
15181
15494
|
);
|
|
15182
15495
|
}
|
|
15183
15496
|
const hubTenant = platform.name ?? "mesh";
|
|
@@ -15205,7 +15518,11 @@ function assembleHubEnv(session, sessionEnv, opts) {
|
|
|
15205
15518
|
"Dev session predates the per-session token-server (MESH-2039) \u2014 starting a dedicated one so tokens stay fresh past ~1h."
|
|
15206
15519
|
);
|
|
15207
15520
|
}
|
|
15208
|
-
const temporalAddress = sessionEnv.TEMPORAL_ADDRESS ?? (session.state.devOutput.tunnels["temporal"] ? tunnelClientAddress(session.state.devOutput.tunnels["temporal"]) :
|
|
15521
|
+
const temporalAddress = sessionEnv.TEMPORAL_ADDRESS ?? (session.state.devOutput.tunnels["temporal"] ? tunnelClientAddress(session.state.devOutput.tunnels["temporal"]) : (
|
|
15522
|
+
// The local platform runs Temporal as a published container, so there is
|
|
15523
|
+
// no tunnel to record and none to demand — the address is fixed.
|
|
15524
|
+
isLocalPlatform ? "localhost:7233" : void 0
|
|
15525
|
+
));
|
|
15209
15526
|
if (!temporalAddress) {
|
|
15210
15527
|
throw new MeshCliError(
|
|
15211
15528
|
`Dev session '${session.name}' exposes no Temporal tunnel \u2014 the Hub can't reach the stack's Temporal.`,
|
|
@@ -15218,7 +15535,8 @@ function assembleHubEnv(session, sessionEnv, opts) {
|
|
|
15218
15535
|
TEMPORAL_ADDRESS: temporalAddress,
|
|
15219
15536
|
HUB_TENANT: hubTenant,
|
|
15220
15537
|
HUB_SCOPE_ENV: scopeEnv,
|
|
15221
|
-
HUB_SCOPE_TENANTS: scopeTenants.join(",")
|
|
15538
|
+
HUB_SCOPE_TENANTS: scopeTenants.join(","),
|
|
15539
|
+
...isLocalPlatform ? localPlatformEnv2(sessionEnv) : {}
|
|
15222
15540
|
};
|
|
15223
15541
|
const uiEnv = {
|
|
15224
15542
|
...forwarded,
|
|
@@ -15267,24 +15585,24 @@ function resolvePlatformDir(explicit, cwd, env = process.env) {
|
|
|
15267
15585
|
);
|
|
15268
15586
|
}
|
|
15269
15587
|
async function findFreePort4() {
|
|
15270
|
-
return new Promise((
|
|
15588
|
+
return new Promise((resolve16, reject) => {
|
|
15271
15589
|
const server = net12.createServer();
|
|
15272
15590
|
server.listen(0, "127.0.0.1", () => {
|
|
15273
15591
|
const address = server.address();
|
|
15274
15592
|
server.close(
|
|
15275
|
-
() => typeof address === "object" && address ?
|
|
15593
|
+
() => typeof address === "object" && address ? resolve16(address.port) : reject(new Error("no port"))
|
|
15276
15594
|
);
|
|
15277
15595
|
});
|
|
15278
15596
|
server.on("error", reject);
|
|
15279
15597
|
});
|
|
15280
15598
|
}
|
|
15281
15599
|
function isPortListening2(port) {
|
|
15282
|
-
return new Promise((
|
|
15600
|
+
return new Promise((resolve16) => {
|
|
15283
15601
|
const socket = net12.connect({ host: "127.0.0.1", port, timeout: 400 });
|
|
15284
15602
|
const done = (ok) => {
|
|
15285
15603
|
socket.removeAllListeners();
|
|
15286
15604
|
socket.destroy();
|
|
15287
|
-
|
|
15605
|
+
resolve16(ok);
|
|
15288
15606
|
};
|
|
15289
15607
|
socket.once("connect", () => done(true));
|
|
15290
15608
|
socket.once("timeout", () => done(false));
|
|
@@ -15405,7 +15723,7 @@ async function hubDevAction(opts) {
|
|
|
15405
15723
|
execFileSync24("tmux", ["send-keys", "-t", `${HUB_SESSION}:${window}`, buildLaunchCommand(envFile, dir, cmd), "Enter"]);
|
|
15406
15724
|
};
|
|
15407
15725
|
launch("api", apiDir, assembled.apiEnv, "pnpm dev", false);
|
|
15408
|
-
launch("ui", uiDir, assembled.uiEnv, `pnpm dev --
|
|
15726
|
+
launch("ui", uiDir, assembled.uiEnv, `pnpm dev --port ${uiPort} --strictPort`, true);
|
|
15409
15727
|
logInfo("Waiting for the Hub to come up\u2026");
|
|
15410
15728
|
const apiUp = await waitForPort("127.0.0.1", apiPort, 9e4);
|
|
15411
15729
|
const uiUp = apiUp && await waitForPort("127.0.0.1", uiPort, 9e4);
|
|
@@ -15428,7 +15746,7 @@ function registerHubCommands(program2) {
|
|
|
15428
15746
|
"Launch the current-code Hub (api + ui) against a running `mesh dev` session \u2014 env auto-assembled, zero hand-set vars"
|
|
15429
15747
|
).option("--session <name>", "dev session to observe (default: auto-detect from cwd)").option("--tenants <list>", "comma-separated HUB_SCOPE_TENANTS override (default: the session's tenant)").option("--port <port>", `Hub UI host port (default: $MESH_HUB_DEV_PORT or ${DEFAULT_HUB_UI_PORT})`).option("--api-port <port>", `Hub API port (default: ${DEFAULT_HUB_API_PORT})`).option("--platform-dir <dir>", "mesh-platform checkout to run the Hub from (default: $MESH_PLATFORM_DIR or walk up from cwd)").option("--print-env", "print the assembled env (secrets redacted) and exit without launching").option("--kill", "tear down the running Hub session").action(hubDevAction);
|
|
15430
15748
|
}
|
|
15431
|
-
var HUB_SESSION, DEFAULT_HUB_UI_PORT, DEFAULT_HUB_API_PORT, FORWARD_PREFIXES, SENSITIVE_RE;
|
|
15749
|
+
var HUB_SESSION, DEFAULT_HUB_UI_PORT, DEFAULT_HUB_API_PORT, FORWARD_PREFIXES, localPlatformEnv2, SENSITIVE_RE;
|
|
15432
15750
|
var init_hub = __esm({
|
|
15433
15751
|
"libs/mesh-cli/src/commands/hub/index.ts"() {
|
|
15434
15752
|
"use strict";
|
|
@@ -15442,6 +15760,35 @@ var init_hub = __esm({
|
|
|
15442
15760
|
DEFAULT_HUB_UI_PORT = Number(DEFAULT_HUB_PORT);
|
|
15443
15761
|
DEFAULT_HUB_API_PORT = 3002;
|
|
15444
15762
|
FORWARD_PREFIXES = ["AWS_", "DEV_USER_"];
|
|
15763
|
+
localPlatformEnv2 = (sessionEnv) => ({
|
|
15764
|
+
// The issuer the local `mesh login` tokens carry. Zitadel routes by Host
|
|
15765
|
+
// header, so the host must stay `localhost` on both sides of the call.
|
|
15766
|
+
ZITADEL_ISSUER: sessionEnv.ZITADEL_ISSUER ?? "http://localhost:8080",
|
|
15767
|
+
// Postgres publishes on 5433; the Hub's ops schema lives in the `hub` database
|
|
15768
|
+
// alongside the per-app ones.
|
|
15769
|
+
OPS_DB_HOST: "localhost",
|
|
15770
|
+
OPS_DB_PORT: "5433",
|
|
15771
|
+
OPS_DB_NAME: "hub",
|
|
15772
|
+
OPS_DB_USER: "postgres",
|
|
15773
|
+
DATABASE_PASSWORD: "postgres",
|
|
15774
|
+
PGSSLMODE: "disable",
|
|
15775
|
+
DATABASE_URL: "postgres://postgres:postgres@localhost:5433/hub?sslmode=disable",
|
|
15776
|
+
// The Hub's ADMIN plane. `mesh start` seeds the machine key this names, in the
|
|
15777
|
+
// platform org with IAM_OWNER, so the Hub can administer each app tenant's own
|
|
15778
|
+
// Zitadel org rather than only its own.
|
|
15779
|
+
ZITADEL_OPSHUB_SECRET_NAME: "mesh/local/dev/zitadel/ops-hub",
|
|
15780
|
+
SPICEDB_ENDPOINT: "localhost:50051",
|
|
15781
|
+
SPICEDB_HTTP_ENDPOINT: "http://localhost:8443",
|
|
15782
|
+
SPICEDB_HTTP_SCHEME: "http",
|
|
15783
|
+
SPICEDB_PRESHARED_KEY: "local-dev-key",
|
|
15784
|
+
LOKI_URL: "http://localhost:3100",
|
|
15785
|
+
TEMPO_URL: "http://localhost:3200",
|
|
15786
|
+
PROMETHEUS_URL: "http://localhost:9090",
|
|
15787
|
+
TEMPORAL_UI_URL: "http://localhost:8233",
|
|
15788
|
+
// The stack's namespace. Absent, the Hub's compliance schedules fail to
|
|
15789
|
+
// register with "Temporal namespace is required".
|
|
15790
|
+
TEMPORAL_NAMESPACE: "local-dev"
|
|
15791
|
+
});
|
|
15445
15792
|
SENSITIVE_RE = /TOKEN|SECRET|KEY|PASSWORD/i;
|
|
15446
15793
|
}
|
|
15447
15794
|
});
|
|
@@ -16042,6 +16389,103 @@ async function ensureHubImages() {
|
|
|
16042
16389
|
}
|
|
16043
16390
|
return version;
|
|
16044
16391
|
}
|
|
16392
|
+
function readWorkspaceCatalog(repoRoot2) {
|
|
16393
|
+
const text = fs29.readFileSync(path36.join(repoRoot2, "pnpm-workspace.yaml"), "utf-8");
|
|
16394
|
+
const marker = "\ncatalog:\n";
|
|
16395
|
+
const at = text.indexOf(marker);
|
|
16396
|
+
if (at === -1) return {};
|
|
16397
|
+
const catalog = {};
|
|
16398
|
+
for (const line of text.slice(at + marker.length).split("\n")) {
|
|
16399
|
+
if (line.trim() !== "" && !/^\s/.test(line)) break;
|
|
16400
|
+
const m = /^\s+"?([^":\s]+)"?:\s*"?([^"\s#]+)"?/.exec(line);
|
|
16401
|
+
if (m) catalog[m[1]] = m[2];
|
|
16402
|
+
}
|
|
16403
|
+
return catalog;
|
|
16404
|
+
}
|
|
16405
|
+
function normalizeHubManifests(contextDir, catalog) {
|
|
16406
|
+
const DEP_FIELDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
16407
|
+
const touched = [];
|
|
16408
|
+
for (const entry of fs29.readdirSync(contextDir, { withFileTypes: true })) {
|
|
16409
|
+
if (!entry.isDirectory()) continue;
|
|
16410
|
+
const manifest = path36.join(contextDir, entry.name, "package.json");
|
|
16411
|
+
if (!fs29.existsSync(manifest)) continue;
|
|
16412
|
+
const pkg = JSON.parse(fs29.readFileSync(manifest, "utf-8"));
|
|
16413
|
+
let changed = false;
|
|
16414
|
+
for (const field of DEP_FIELDS) {
|
|
16415
|
+
const deps = pkg[field];
|
|
16416
|
+
if (!deps) continue;
|
|
16417
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
16418
|
+
if (typeof spec !== "string") continue;
|
|
16419
|
+
if (spec.startsWith("workspace:")) {
|
|
16420
|
+
delete deps[name];
|
|
16421
|
+
changed = true;
|
|
16422
|
+
} else if (spec.startsWith("catalog:")) {
|
|
16423
|
+
const key = spec.slice("catalog:".length) || name;
|
|
16424
|
+
const range = catalog[name] ?? catalog[key];
|
|
16425
|
+
if (!range) {
|
|
16426
|
+
throw new MeshCliError(
|
|
16427
|
+
`${entry.name}/package.json depends on "${name}": "${spec}", which the workspace catalog does not define.`,
|
|
16428
|
+
{ remediation: { command: "Add the dependency to the `catalog:` block in pnpm-workspace.yaml" } }
|
|
16429
|
+
);
|
|
16430
|
+
}
|
|
16431
|
+
deps[name] = range;
|
|
16432
|
+
changed = true;
|
|
16433
|
+
}
|
|
16434
|
+
}
|
|
16435
|
+
}
|
|
16436
|
+
if (changed) {
|
|
16437
|
+
fs29.writeFileSync(manifest, `${JSON.stringify(pkg, null, 2)}
|
|
16438
|
+
`);
|
|
16439
|
+
touched.push(`${entry.name}/package.json`);
|
|
16440
|
+
}
|
|
16441
|
+
}
|
|
16442
|
+
return touched;
|
|
16443
|
+
}
|
|
16444
|
+
async function buildHubImagesFromSource(repoRoot2) {
|
|
16445
|
+
const hubDir = path36.join(repoRoot2, "apps", "hub");
|
|
16446
|
+
if (!fs29.existsSync(path36.join(hubDir, "package.json"))) {
|
|
16447
|
+
throw new MeshCliError(
|
|
16448
|
+
`--hub-from-source needs a mesh-platform checkout; no apps/hub under ${repoRoot2}.`,
|
|
16449
|
+
{ remediation: { command: "Run mesh start from a mesh-platform checkout, or drop --hub-from-source" } }
|
|
16450
|
+
);
|
|
16451
|
+
}
|
|
16452
|
+
const npmrc = npmrcPath();
|
|
16453
|
+
const version = `${JSON.parse(fs29.readFileSync(path36.join(hubDir, "package.json"), "utf-8")).version}-src`;
|
|
16454
|
+
const build = startHeartbeat("building apps/hub (api + ui) from source");
|
|
16455
|
+
try {
|
|
16456
|
+
for (const pkg of ["@mesh-tech/hub-api", "@mesh-tech/hub-ui"]) {
|
|
16457
|
+
await execFileAsync2("pnpm", ["--filter", pkg, "build"], { cwd: repoRoot2, maxBuffer: 64 * 1024 * 1024 });
|
|
16458
|
+
}
|
|
16459
|
+
} finally {
|
|
16460
|
+
build.stop();
|
|
16461
|
+
}
|
|
16462
|
+
const context = path36.join(cacheDir(), `context-${version}`);
|
|
16463
|
+
fs29.rmSync(context, { recursive: true, force: true });
|
|
16464
|
+
fs29.mkdirSync(context, { recursive: true });
|
|
16465
|
+
await execFileAsync2("pnpm", ["pack", "--pack-destination", context], { cwd: hubDir, maxBuffer: 64 * 1024 * 1024 });
|
|
16466
|
+
const tarball = fs29.readdirSync(context).find((f) => f.endsWith(".tgz"));
|
|
16467
|
+
if (!tarball) throw new MeshCliError("pnpm pack produced no tarball for apps/hub");
|
|
16468
|
+
execFileSync26("tar", ["-xzf", path36.join(context, tarball), "-C", context, "--strip-components", "1"], {
|
|
16469
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16470
|
+
});
|
|
16471
|
+
const rewritten = normalizeHubManifests(context, readWorkspaceCatalog(repoRoot2));
|
|
16472
|
+
if (rewritten.length > 0) logInfo(`Normalized ${rewritten.length} manifest(s) for npm: ${rewritten.join(", ")}`);
|
|
16473
|
+
const hubStackDir = path36.join(findPackageRoot(), "stack", "hub");
|
|
16474
|
+
for (const [name, dockerfile] of [
|
|
16475
|
+
["mesh-local-hub-api", "Dockerfile.api"],
|
|
16476
|
+
["mesh-local-hub-ui", "Dockerfile.ui"]
|
|
16477
|
+
]) {
|
|
16478
|
+
const tag = `${name}:${version}`;
|
|
16479
|
+
logInfo(`Building ${tag} from source\u2026`);
|
|
16480
|
+
execFileSync26(
|
|
16481
|
+
"docker",
|
|
16482
|
+
["build", "-f", path36.join(hubStackDir, dockerfile), "-t", tag, "--secret", `id=npmrc,src=${npmrc}`, context],
|
|
16483
|
+
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
|
|
16484
|
+
);
|
|
16485
|
+
logSuccess(`Built ${tag}`);
|
|
16486
|
+
}
|
|
16487
|
+
return version;
|
|
16488
|
+
}
|
|
16045
16489
|
var execFileAsync2, HUB_PACKAGE, HUB_IMAGES, LOCAL_IMAGE_REV, HUB_AUTH_IMAGE;
|
|
16046
16490
|
var init_hub_local = __esm({
|
|
16047
16491
|
"libs/mesh-cli/src/commands/local/hub-local.ts"() {
|
|
@@ -16059,6 +16503,8 @@ var init_hub_local = __esm({
|
|
|
16059
16503
|
});
|
|
16060
16504
|
|
|
16061
16505
|
// libs/mesh-cli/src/commands/local/index.ts
|
|
16506
|
+
import * as fs30 from "fs";
|
|
16507
|
+
import * as path37 from "path";
|
|
16062
16508
|
import chalk5 from "chalk";
|
|
16063
16509
|
function crashError(crashed) {
|
|
16064
16510
|
return new MeshCliError(
|
|
@@ -16101,7 +16547,7 @@ async function waitForStack(io = {
|
|
|
16101
16547
|
);
|
|
16102
16548
|
}
|
|
16103
16549
|
logInfo(`Waiting for ${pending.map((e) => e.label).join(", ")} \u2026`);
|
|
16104
|
-
await new Promise((
|
|
16550
|
+
await new Promise((resolve16) => setTimeout(resolve16, WAIT_POLL_MS));
|
|
16105
16551
|
}
|
|
16106
16552
|
}
|
|
16107
16553
|
function printEndpoints(hubRunning) {
|
|
@@ -16144,10 +16590,22 @@ function printEndpoints(hubRunning) {
|
|
|
16144
16590
|
console.log("");
|
|
16145
16591
|
logInfo("Next: `mesh dev` in an apps repo wires services to this stack automatically.");
|
|
16146
16592
|
}
|
|
16593
|
+
function repoRootForHubSource() {
|
|
16594
|
+
for (let dir = process.cwd(); ; ) {
|
|
16595
|
+
if (fs30.existsSync(path37.join(dir, "apps", "hub", "package.json"))) return dir;
|
|
16596
|
+
const parent = path37.dirname(dir);
|
|
16597
|
+
if (parent === dir) break;
|
|
16598
|
+
dir = parent;
|
|
16599
|
+
}
|
|
16600
|
+
return path37.resolve(findPackageRoot(), "..", "..");
|
|
16601
|
+
}
|
|
16147
16602
|
function registerLocalCommands(program2) {
|
|
16148
16603
|
program2.command("start").description("Start the full-local Mesh platform (Docker only \u2014 no AWS, no VPN)").option("--no-seed", "skip first-boot seeding (tenant registry, artifacts bucket)").option("--no-hub", "start without the Hub (API + UI)").option(
|
|
16149
16604
|
"--with-hub",
|
|
16150
16605
|
"force-refresh the Hub to the latest published @mesh-tech/hub (the Hub is included by default when its images exist or registry auth is available)"
|
|
16606
|
+
).option(
|
|
16607
|
+
"--hub-from-source",
|
|
16608
|
+
"build the Hub images from THIS checkout's apps/hub instead of the published tarball (the only way to exercise a Hub change locally)"
|
|
16151
16609
|
).option(
|
|
16152
16610
|
"--takeover",
|
|
16153
16611
|
"re-own a stack that another checkout started (may recreate shared containers with THIS checkout's config)",
|
|
@@ -16172,7 +16630,9 @@ Re-running start from here may recreate shared containers with this checkout's c
|
|
|
16172
16630
|
}
|
|
16173
16631
|
let hubVersion;
|
|
16174
16632
|
if (opts.hub) {
|
|
16175
|
-
if (opts.
|
|
16633
|
+
if (opts.hubFromSource) {
|
|
16634
|
+
hubVersion = await buildHubImagesFromSource(repoRootForHubSource());
|
|
16635
|
+
} else if (opts.withHub) {
|
|
16176
16636
|
const probe = await probeRegistryToken();
|
|
16177
16637
|
const plan = planWithHubRefresh(probe.state, localHubVersion());
|
|
16178
16638
|
if (plan.action === "fail") {
|
|
@@ -16260,7 +16720,7 @@ Re-running start from here may recreate shared containers with this checkout's c
|
|
|
16260
16720
|
overlayStarted: true
|
|
16261
16721
|
});
|
|
16262
16722
|
if (crashed.length > 0) throw crashError(crashed);
|
|
16263
|
-
await new Promise((
|
|
16723
|
+
await new Promise((resolve16) => setTimeout(resolve16, 2e3));
|
|
16264
16724
|
up = await probeEndpoint(endpoint);
|
|
16265
16725
|
}
|
|
16266
16726
|
if (!up) {
|
|
@@ -16521,6 +16981,12 @@ import {
|
|
|
16521
16981
|
} from "@aws-sdk/client-secrets-manager";
|
|
16522
16982
|
import input from "@inquirer/input";
|
|
16523
16983
|
import password from "@inquirer/password";
|
|
16984
|
+
import {
|
|
16985
|
+
buildInstanceIndexValue,
|
|
16986
|
+
instanceIndexSecretId,
|
|
16987
|
+
isInstanceKey,
|
|
16988
|
+
parseInstanceIndex
|
|
16989
|
+
} from "@mesh-tech/secrets";
|
|
16524
16990
|
async function ensureAwsCredentialsForStack(stack) {
|
|
16525
16991
|
if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN) return;
|
|
16526
16992
|
if (!stack) return;
|
|
@@ -16705,6 +17171,12 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16705
17171
|
);
|
|
16706
17172
|
process.exit(1);
|
|
16707
17173
|
}
|
|
17174
|
+
if (opts.key && !isInstanceKey(opts.key)) {
|
|
17175
|
+
logError(
|
|
17176
|
+
`Invalid instance key "${opts.key}": keys must not contain "/", start with ".", or end with ".config" \u2014 those names are reserved for the index and config mirrors.`
|
|
17177
|
+
);
|
|
17178
|
+
process.exit(1);
|
|
17179
|
+
}
|
|
16708
17180
|
let existing = {};
|
|
16709
17181
|
try {
|
|
16710
17182
|
const response = await smClient.send(
|
|
@@ -16729,6 +17201,9 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16729
17201
|
const merged2 = deepMerge(existing, values2);
|
|
16730
17202
|
await writeSecret(smClient, secretId, merged2, svc.meta);
|
|
16731
17203
|
await writeConfigMirror(smClient, secretId, schema, merged2, svc.meta);
|
|
17204
|
+
if (opts.key) {
|
|
17205
|
+
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
|
|
17206
|
+
}
|
|
16732
17207
|
logSuccess(`Credentials written to ${secretId}`);
|
|
16733
17208
|
return;
|
|
16734
17209
|
}
|
|
@@ -16744,6 +17219,9 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16744
17219
|
const merged = deepMerge(existing, values);
|
|
16745
17220
|
await writeSecret(smClient, secretId, merged, svc.meta);
|
|
16746
17221
|
await writeConfigMirror(smClient, secretId, schema, merged, svc.meta);
|
|
17222
|
+
if (opts.key) {
|
|
17223
|
+
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
|
|
17224
|
+
}
|
|
16747
17225
|
logSuccess(`Credentials written to ${secretId}`);
|
|
16748
17226
|
console.error("\nStored fields:");
|
|
16749
17227
|
displaySummary(schema.fields, merged);
|
|
@@ -16809,6 +17287,45 @@ function extractNonSecretFields(fields, values) {
|
|
|
16809
17287
|
}
|
|
16810
17288
|
return result;
|
|
16811
17289
|
}
|
|
17290
|
+
async function addKeyToInstanceIndex(client, secretPrefix, key, serviceName) {
|
|
17291
|
+
const indexId = instanceIndexSecretId(secretPrefix);
|
|
17292
|
+
try {
|
|
17293
|
+
let keys = [];
|
|
17294
|
+
try {
|
|
17295
|
+
const res = await client.send(new GetSecretValueCommand5({ SecretId: indexId }));
|
|
17296
|
+
keys = parseInstanceIndex(res.SecretString ?? "");
|
|
17297
|
+
} catch (err) {
|
|
17298
|
+
if (!isResourceNotFound(err)) throw err;
|
|
17299
|
+
}
|
|
17300
|
+
if (keys.includes(key)) return;
|
|
17301
|
+
const secretString = buildInstanceIndexValue([...keys, key]);
|
|
17302
|
+
try {
|
|
17303
|
+
await client.send(
|
|
17304
|
+
new PutSecretValueCommand({ SecretId: indexId, SecretString: secretString })
|
|
17305
|
+
);
|
|
17306
|
+
} catch (err) {
|
|
17307
|
+
if (!isResourceNotFound(err)) throw err;
|
|
17308
|
+
await client.send(
|
|
17309
|
+
new CreateSecretCommand({
|
|
17310
|
+
Name: indexId,
|
|
17311
|
+
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
|
|
17312
|
+
SecretString: secretString,
|
|
17313
|
+
Tags: [
|
|
17314
|
+
{ Key: "mesh:type", Value: "external-service-index" },
|
|
17315
|
+
{ Key: "mesh:service", Value: serviceName }
|
|
17316
|
+
]
|
|
17317
|
+
})
|
|
17318
|
+
);
|
|
17319
|
+
logInfo(
|
|
17320
|
+
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack (it creates the index) before writing keys, or import this secret into the stack before its next deploy.`
|
|
17321
|
+
);
|
|
17322
|
+
}
|
|
17323
|
+
} catch (err) {
|
|
17324
|
+
logInfo(
|
|
17325
|
+
`Warning: failed to record instance "${key}" in ${indexId} \u2014 listInstances() will not see it until you run: mesh secrets reindex external/${serviceName} (${err})`
|
|
17326
|
+
);
|
|
17327
|
+
}
|
|
17328
|
+
}
|
|
16812
17329
|
async function writeConfigMirror(client, secretId, schema, merged, meta) {
|
|
16813
17330
|
const configId = `${secretId}/.config`;
|
|
16814
17331
|
const configValues = extractNonSecretFields(schema.fields, merged);
|
|
@@ -16850,17 +17367,157 @@ var init_set = __esm({
|
|
|
16850
17367
|
}
|
|
16851
17368
|
});
|
|
16852
17369
|
|
|
17370
|
+
// libs/mesh-cli/src/commands/secrets/reindex.ts
|
|
17371
|
+
import { SSMClient as SSMClient3 } from "@aws-sdk/client-ssm";
|
|
17372
|
+
import {
|
|
17373
|
+
SecretsManagerClient as SecretsManagerClient6,
|
|
17374
|
+
ListSecretsCommand,
|
|
17375
|
+
GetSecretValueCommand as GetSecretValueCommand6,
|
|
17376
|
+
PutSecretValueCommand as PutSecretValueCommand2,
|
|
17377
|
+
CreateSecretCommand as CreateSecretCommand2
|
|
17378
|
+
} from "@aws-sdk/client-secrets-manager";
|
|
17379
|
+
import {
|
|
17380
|
+
buildInstanceIndexValue as buildInstanceIndexValue2,
|
|
17381
|
+
instanceIndexSecretId as instanceIndexSecretId2,
|
|
17382
|
+
instanceKeyFromSecretName,
|
|
17383
|
+
parseInstanceIndex as parseInstanceIndex2
|
|
17384
|
+
} from "@mesh-tech/secrets";
|
|
17385
|
+
async function scanInstanceKeys(client, secretPrefix) {
|
|
17386
|
+
const keys = [];
|
|
17387
|
+
let nextToken;
|
|
17388
|
+
do {
|
|
17389
|
+
const response = await client.send(
|
|
17390
|
+
new ListSecretsCommand({
|
|
17391
|
+
Filters: [{ Key: "name", Values: [`${secretPrefix}/`] }],
|
|
17392
|
+
NextToken: nextToken
|
|
17393
|
+
})
|
|
17394
|
+
);
|
|
17395
|
+
for (const secret of response.SecretList ?? []) {
|
|
17396
|
+
const key = secret.Name ? instanceKeyFromSecretName(secretPrefix, secret.Name) : null;
|
|
17397
|
+
if (key) keys.push(key);
|
|
17398
|
+
}
|
|
17399
|
+
nextToken = response.NextToken;
|
|
17400
|
+
} while (nextToken);
|
|
17401
|
+
return keys;
|
|
17402
|
+
}
|
|
17403
|
+
function indexAlreadyMatches(missing, stale, indexExists, malformed) {
|
|
17404
|
+
return missing.length === 0 && stale.length === 0 && indexExists && !malformed;
|
|
17405
|
+
}
|
|
17406
|
+
async function reindexCommand(servicePath, opts) {
|
|
17407
|
+
const stackOpt = resolveStackOption(opts);
|
|
17408
|
+
const context = detectContext(stackOpt);
|
|
17409
|
+
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
17410
|
+
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
|
|
17411
|
+
const ssmClient = new SSMClient3({ region });
|
|
17412
|
+
const smClient = new SecretsManagerClient6({ region });
|
|
17413
|
+
const services = await discoverExternalServices(
|
|
17414
|
+
ssmClient,
|
|
17415
|
+
context.tenant,
|
|
17416
|
+
context.platformEnv
|
|
17417
|
+
);
|
|
17418
|
+
const serviceName = servicePath?.replace(/^external\//, "");
|
|
17419
|
+
if (!serviceName || !services.has(serviceName)) {
|
|
17420
|
+
if (serviceName) {
|
|
17421
|
+
logError(`External service "${serviceName}" not found.`);
|
|
17422
|
+
}
|
|
17423
|
+
const keyed = [...services.entries()].filter(([, s]) => s.schema?.keyedBy);
|
|
17424
|
+
console.error("\nMulti-instance external services:");
|
|
17425
|
+
for (const [name, svc2] of keyed) {
|
|
17426
|
+
console.error(` ${name} (keyed by ${svc2.schema.keyedBy})`);
|
|
17427
|
+
}
|
|
17428
|
+
if (keyed.length === 0) console.error(" (none)");
|
|
17429
|
+
console.error(`
|
|
17430
|
+
Usage: mesh secrets reindex external/<name>`);
|
|
17431
|
+
process.exit(serviceName ? 1 : 0);
|
|
17432
|
+
}
|
|
17433
|
+
const svc = services.get(serviceName);
|
|
17434
|
+
if (!svc.schema?.keyedBy) {
|
|
17435
|
+
logError(
|
|
17436
|
+
`"${serviceName}" is single-instance (no keyedBy) \u2014 it has no instance index.`
|
|
17437
|
+
);
|
|
17438
|
+
process.exit(1);
|
|
17439
|
+
}
|
|
17440
|
+
const secretPrefix = svc.meta.secretPrefix;
|
|
17441
|
+
const indexId = instanceIndexSecretId2(secretPrefix);
|
|
17442
|
+
const actual = [...new Set(await scanInstanceKeys(smClient, secretPrefix))].sort();
|
|
17443
|
+
let listed = [];
|
|
17444
|
+
let indexExists = true;
|
|
17445
|
+
let malformed = false;
|
|
17446
|
+
try {
|
|
17447
|
+
const res = await smClient.send(new GetSecretValueCommand6({ SecretId: indexId }));
|
|
17448
|
+
try {
|
|
17449
|
+
listed = parseInstanceIndex2(res.SecretString ?? "");
|
|
17450
|
+
} catch {
|
|
17451
|
+
malformed = true;
|
|
17452
|
+
logInfo(`Existing index at ${indexId} is malformed \u2014 rebuilding from scratch.`);
|
|
17453
|
+
}
|
|
17454
|
+
} catch (err) {
|
|
17455
|
+
if (!isResourceNotFound(err)) throw err;
|
|
17456
|
+
indexExists = false;
|
|
17457
|
+
logInfo(`No index at ${indexId} yet \u2014 it will be created.`);
|
|
17458
|
+
}
|
|
17459
|
+
const missing = actual.filter((k) => !listed.includes(k));
|
|
17460
|
+
const stale = listed.filter((k) => !actual.includes(k));
|
|
17461
|
+
logInfo(`Instances in Secrets Manager: ${actual.length ? actual.join(", ") : "(none)"}`);
|
|
17462
|
+
if (missing.length > 0) logInfo(`Missing from index: ${missing.join(", ")}`);
|
|
17463
|
+
if (stale.length > 0) logInfo(`Stale in index (secret gone): ${stale.join(", ")}`);
|
|
17464
|
+
if (indexAlreadyMatches(missing, stale, indexExists, malformed)) {
|
|
17465
|
+
logSuccess(`Index at ${indexId} already matches \u2014 nothing to do.`);
|
|
17466
|
+
return;
|
|
17467
|
+
}
|
|
17468
|
+
if (opts.dryRun) {
|
|
17469
|
+
logInfo(`Dry run \u2014 would write ${indexId} with ${actual.length} instance(s).`);
|
|
17470
|
+
return;
|
|
17471
|
+
}
|
|
17472
|
+
await writeInstanceIndex(smClient, secretPrefix, serviceName, actual);
|
|
17473
|
+
logSuccess(`Index rebuilt: ${indexId} now lists ${actual.length} instance(s).`);
|
|
17474
|
+
}
|
|
17475
|
+
async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
|
|
17476
|
+
const indexId = instanceIndexSecretId2(secretPrefix);
|
|
17477
|
+
const secretString = buildInstanceIndexValue2(keys);
|
|
17478
|
+
try {
|
|
17479
|
+
await client.send(
|
|
17480
|
+
new PutSecretValueCommand2({ SecretId: indexId, SecretString: secretString })
|
|
17481
|
+
);
|
|
17482
|
+
} catch (err) {
|
|
17483
|
+
if (!isResourceNotFound(err)) throw err;
|
|
17484
|
+
await client.send(
|
|
17485
|
+
new CreateSecretCommand2({
|
|
17486
|
+
Name: indexId,
|
|
17487
|
+
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
|
|
17488
|
+
SecretString: secretString,
|
|
17489
|
+
Tags: [
|
|
17490
|
+
{ Key: "mesh:type", Value: "external-service-index" },
|
|
17491
|
+
{ Key: "mesh:service", Value: serviceName }
|
|
17492
|
+
]
|
|
17493
|
+
})
|
|
17494
|
+
);
|
|
17495
|
+
logInfo(
|
|
17496
|
+
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack BEFORE reindex next time, or import this secret into the stack before its next deploy.`
|
|
17497
|
+
);
|
|
17498
|
+
}
|
|
17499
|
+
}
|
|
17500
|
+
var init_reindex = __esm({
|
|
17501
|
+
"libs/mesh-cli/src/commands/secrets/reindex.ts"() {
|
|
17502
|
+
"use strict";
|
|
17503
|
+
init_context();
|
|
17504
|
+
init_log();
|
|
17505
|
+
init_stack_flag();
|
|
17506
|
+
init_set();
|
|
17507
|
+
}
|
|
17508
|
+
});
|
|
17509
|
+
|
|
16853
17510
|
// libs/mesh-cli/src/commands/secrets/migrate-config.ts
|
|
16854
17511
|
import {
|
|
16855
|
-
SSMClient as
|
|
17512
|
+
SSMClient as SSMClient4,
|
|
16856
17513
|
GetParametersByPathCommand as GetParametersByPathCommand2
|
|
16857
17514
|
} from "@aws-sdk/client-ssm";
|
|
16858
17515
|
import {
|
|
16859
|
-
SecretsManagerClient as
|
|
16860
|
-
GetSecretValueCommand as
|
|
16861
|
-
PutSecretValueCommand as
|
|
16862
|
-
CreateSecretCommand as
|
|
16863
|
-
ListSecretsCommand
|
|
17516
|
+
SecretsManagerClient as SecretsManagerClient7,
|
|
17517
|
+
GetSecretValueCommand as GetSecretValueCommand7,
|
|
17518
|
+
PutSecretValueCommand as PutSecretValueCommand3,
|
|
17519
|
+
CreateSecretCommand as CreateSecretCommand3,
|
|
17520
|
+
ListSecretsCommand as ListSecretsCommand2
|
|
16864
17521
|
} from "@aws-sdk/client-secrets-manager";
|
|
16865
17522
|
function normalizeEntry2(raw) {
|
|
16866
17523
|
if (raw.type === "group" && raw.fields) {
|
|
@@ -16946,7 +17603,7 @@ function isResourceNotFound2(err) {
|
|
|
16946
17603
|
}
|
|
16947
17604
|
async function readSecret(client, secretId) {
|
|
16948
17605
|
try {
|
|
16949
|
-
const res = await client.send(new
|
|
17606
|
+
const res = await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
|
|
16950
17607
|
return res.SecretString ? JSON.parse(res.SecretString) : null;
|
|
16951
17608
|
} catch (err) {
|
|
16952
17609
|
if (isResourceNotFound2(err)) return null;
|
|
@@ -16956,10 +17613,10 @@ async function readSecret(client, secretId) {
|
|
|
16956
17613
|
async function writeSecret2(client, secretId, values, description) {
|
|
16957
17614
|
const secretString = JSON.stringify(values);
|
|
16958
17615
|
try {
|
|
16959
|
-
await client.send(new
|
|
17616
|
+
await client.send(new PutSecretValueCommand3({ SecretId: secretId, SecretString: secretString }));
|
|
16960
17617
|
} catch (err) {
|
|
16961
17618
|
if (isResourceNotFound2(err)) {
|
|
16962
|
-
await client.send(new
|
|
17619
|
+
await client.send(new CreateSecretCommand3({
|
|
16963
17620
|
Name: secretId,
|
|
16964
17621
|
Description: description,
|
|
16965
17622
|
SecretString: secretString,
|
|
@@ -16972,7 +17629,7 @@ async function writeSecret2(client, secretId, values, description) {
|
|
|
16972
17629
|
}
|
|
16973
17630
|
async function secretExists(client, secretId) {
|
|
16974
17631
|
try {
|
|
16975
|
-
await client.send(new
|
|
17632
|
+
await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
|
|
16976
17633
|
return true;
|
|
16977
17634
|
} catch (err) {
|
|
16978
17635
|
if (isResourceNotFound2(err)) return false;
|
|
@@ -16985,7 +17642,7 @@ async function listInstanceKeys(client, secretPrefix) {
|
|
|
16985
17642
|
let nextToken;
|
|
16986
17643
|
do {
|
|
16987
17644
|
const response = await client.send(
|
|
16988
|
-
new
|
|
17645
|
+
new ListSecretsCommand2({
|
|
16989
17646
|
Filters: [{ Key: "name", Values: [prefix2] }],
|
|
16990
17647
|
NextToken: nextToken
|
|
16991
17648
|
})
|
|
@@ -17051,8 +17708,8 @@ async function migrateService(smClient, meta, schema, opts) {
|
|
|
17051
17708
|
async function migrateConfigCommand(opts) {
|
|
17052
17709
|
const context = detectContext(resolveStackOption(opts));
|
|
17053
17710
|
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
17054
|
-
const ssmClient = new
|
|
17055
|
-
const smClient = new
|
|
17711
|
+
const ssmClient = new SSMClient4({ region });
|
|
17712
|
+
const smClient = new SecretsManagerClient7({ region });
|
|
17056
17713
|
logInfo(`Migrating .config mirrors for ${context.tenant}/${context.platformEnv}`);
|
|
17057
17714
|
if (opts.dryRun) logInfo("DRY-RUN mode \u2014 no writes");
|
|
17058
17715
|
if (opts.force) logInfo("FORCE mode \u2014 overwrite existing .config");
|
|
@@ -17105,6 +17762,11 @@ function registerSecretsCommands(program2) {
|
|
|
17105
17762
|
secrets.command("set [service]").description("Set credentials for an external service (e.g., mesh secrets set external/symitar)").option("--key <key>", "Instance key for multi-instance services (e.g., FI ID)").option("--all", "Prompt for all fields including optional ones").option("--json <json>", "Non-interactive: provide values as JSON").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
17106
17763
|
await setCommand(service, opts);
|
|
17107
17764
|
});
|
|
17765
|
+
secrets.command("reindex [service]").description(
|
|
17766
|
+
"Rebuild a multi-instance external service's instance index (the {prefix}/.index secret listInstances() reads) from the per-key secrets actually in Secrets Manager \u2014 the repair for an instance secret created or deleted out of band"
|
|
17767
|
+
).option("--dry-run", "Show what would change without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
17768
|
+
await reindexCommand(service, opts);
|
|
17769
|
+
});
|
|
17108
17770
|
secrets.command("migrate-config").description("Backfill .config mirror secrets for all external services").option("--force", "Overwrite existing .config mirrors").option("--dry-run", "Show what would be written without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (opts) => {
|
|
17109
17771
|
await migrateConfigCommand(opts);
|
|
17110
17772
|
});
|
|
@@ -17114,28 +17776,29 @@ var init_secrets = __esm({
|
|
|
17114
17776
|
"use strict";
|
|
17115
17777
|
init_exec2();
|
|
17116
17778
|
init_set();
|
|
17779
|
+
init_reindex();
|
|
17117
17780
|
init_migrate_config();
|
|
17118
17781
|
}
|
|
17119
17782
|
});
|
|
17120
17783
|
|
|
17121
17784
|
// libs/mesh-cli/src/commands/stack.ts
|
|
17122
17785
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
17123
|
-
import * as
|
|
17124
|
-
import * as
|
|
17786
|
+
import * as path38 from "path";
|
|
17787
|
+
import * as fs31 from "fs";
|
|
17125
17788
|
import { parse as parseYaml5 } from "yaml";
|
|
17126
17789
|
function readTopLevelYamlKey(appRoot, stack, key) {
|
|
17127
|
-
const configFile =
|
|
17128
|
-
if (!
|
|
17129
|
-
const content =
|
|
17790
|
+
const configFile = path38.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
17791
|
+
if (!fs31.existsSync(configFile)) return null;
|
|
17792
|
+
const content = fs31.readFileSync(configFile, "utf-8");
|
|
17130
17793
|
const pattern = new RegExp(`^${key}:\\s*(.+)$`, "m");
|
|
17131
17794
|
const match = content.match(pattern);
|
|
17132
17795
|
if (!match) return null;
|
|
17133
17796
|
return match[1].trim().replace(/^["']|["']$/g, "");
|
|
17134
17797
|
}
|
|
17135
17798
|
function readConfigBlockKey(appRoot, stack, key) {
|
|
17136
|
-
const configFile =
|
|
17137
|
-
if (!
|
|
17138
|
-
const content =
|
|
17799
|
+
const configFile = path38.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
17800
|
+
if (!fs31.existsSync(configFile)) return null;
|
|
17801
|
+
const content = fs31.readFileSync(configFile, "utf-8");
|
|
17139
17802
|
const pattern = new RegExp(`^\\s{2}${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
|
|
17140
17803
|
const match = content.match(pattern);
|
|
17141
17804
|
if (!match) return null;
|
|
@@ -17195,11 +17858,11 @@ ${records}
|
|
|
17195
17858
|
}
|
|
17196
17859
|
}
|
|
17197
17860
|
function readBaseConfigFromYaml(appRoot, stack) {
|
|
17198
|
-
const file =
|
|
17199
|
-
if (!
|
|
17861
|
+
const file = path38.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
17862
|
+
if (!fs31.existsSync(file)) return {};
|
|
17200
17863
|
let doc;
|
|
17201
17864
|
try {
|
|
17202
|
-
doc = parseYaml5(
|
|
17865
|
+
doc = parseYaml5(fs31.readFileSync(file, "utf-8"));
|
|
17203
17866
|
} catch {
|
|
17204
17867
|
return {};
|
|
17205
17868
|
}
|
|
@@ -17276,7 +17939,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
17276
17939
|
}
|
|
17277
17940
|
}
|
|
17278
17941
|
const baseConfigPath = `${appRoot}/Pulumi.${baseStack}.yaml`;
|
|
17279
|
-
if (!
|
|
17942
|
+
if (!fs31.existsSync(baseConfigPath)) {
|
|
17280
17943
|
logError(`Stack config not found: Pulumi.${baseStack}.yaml`);
|
|
17281
17944
|
process.exit(1);
|
|
17282
17945
|
}
|
|
@@ -17298,8 +17961,8 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
17298
17961
|
);
|
|
17299
17962
|
}
|
|
17300
17963
|
}
|
|
17301
|
-
const newConfigPath =
|
|
17302
|
-
const configExists =
|
|
17964
|
+
const newConfigPath = path38.join(appRoot, `Pulumi.${newStack}.yaml`);
|
|
17965
|
+
const configExists = fs31.existsSync(newConfigPath);
|
|
17303
17966
|
const secretsProvider = readTopLevelYamlKey(appRoot, baseStack, "secretsprovider");
|
|
17304
17967
|
const credEnv = await resolvePulumiEnv({ appRoot, stack: baseStack });
|
|
17305
17968
|
const pulumiEnv = { ...process.env, ...credEnv };
|
|
@@ -17946,9 +18609,9 @@ var init_capture_history = __esm({
|
|
|
17946
18609
|
|
|
17947
18610
|
// libs/mesh-cli/src/commands/temporal.ts
|
|
17948
18611
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
17949
|
-
import { writeFileSync as
|
|
18612
|
+
import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync22 } from "node:fs";
|
|
17950
18613
|
import { homedir as homedir9 } from "node:os";
|
|
17951
|
-
import { dirname as
|
|
18614
|
+
import { dirname as dirname27, join as join35, resolve as resolve14 } from "node:path";
|
|
17952
18615
|
async function resolveConnection(options) {
|
|
17953
18616
|
if (options.address && options.namespace) {
|
|
17954
18617
|
return { address: options.address, namespace: options.namespace };
|
|
@@ -18318,7 +18981,7 @@ async function recoverConversation(workflowId, runId, options) {
|
|
|
18318
18981
|
};
|
|
18319
18982
|
const output2 = JSON.stringify(blob, null, 2) + "\n";
|
|
18320
18983
|
if (options.out) {
|
|
18321
|
-
|
|
18984
|
+
writeFileSync23(options.out, output2);
|
|
18322
18985
|
logSuccess(
|
|
18323
18986
|
`Wrote snapshot (${messages.length} messages) to ${options.out}`
|
|
18324
18987
|
);
|
|
@@ -18341,7 +19004,7 @@ async function recoverConversation(workflowId, runId, options) {
|
|
|
18341
19004
|
2
|
|
18342
19005
|
) + "\n" : renderTranscriptMarkdown(turns);
|
|
18343
19006
|
if (options.out) {
|
|
18344
|
-
|
|
19007
|
+
writeFileSync23(options.out, output);
|
|
18345
19008
|
logSuccess(`Wrote ${turns.length} turns to ${options.out}`);
|
|
18346
19009
|
} else {
|
|
18347
19010
|
process.stdout.write(output);
|
|
@@ -18376,10 +19039,10 @@ async function captureHistory(workflowId, runId, options) {
|
|
|
18376
19039
|
}
|
|
18377
19040
|
const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
|
|
18378
19041
|
const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
|
|
18379
|
-
const outPath = options.out ??
|
|
18380
|
-
mkdirSync22(
|
|
19042
|
+
const outPath = options.out ?? join35(homedir9(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
|
|
19043
|
+
mkdirSync22(dirname27(outPath), { recursive: true });
|
|
18381
19044
|
if (options.out) warnIfNotGitIgnored(options.out);
|
|
18382
|
-
|
|
19045
|
+
writeFileSync23(outPath, JSON.stringify(fixture, null, 2) + "\n");
|
|
18383
19046
|
logSuccess(
|
|
18384
19047
|
`Wrote replay history (${eventCount} events, ${decryptedPayloads} payloads decrypted) to ${outPath}`
|
|
18385
19048
|
);
|
|
@@ -18394,9 +19057,9 @@ function sanitizeFileId(id) {
|
|
|
18394
19057
|
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
18395
19058
|
}
|
|
18396
19059
|
function warnIfNotGitIgnored(outPath) {
|
|
18397
|
-
const abs =
|
|
19060
|
+
const abs = resolve14(outPath);
|
|
18398
19061
|
try {
|
|
18399
|
-
const res = spawnSync4("git", ["-C",
|
|
19062
|
+
const res = spawnSync4("git", ["-C", dirname27(abs), "check-ignore", "-q", abs], {
|
|
18400
19063
|
stdio: "ignore"
|
|
18401
19064
|
});
|
|
18402
19065
|
if (res.status !== 1) return;
|
|
@@ -18564,8 +19227,8 @@ var init_temporal = __esm({
|
|
|
18564
19227
|
|
|
18565
19228
|
// libs/mesh-cli/src/commands/tenant.ts
|
|
18566
19229
|
import chalk6 from "chalk";
|
|
18567
|
-
import * as
|
|
18568
|
-
import * as
|
|
19230
|
+
import * as fs32 from "fs";
|
|
19231
|
+
import * as path39 from "path";
|
|
18569
19232
|
import { parseDocument, YAMLMap, isMap } from "yaml";
|
|
18570
19233
|
function validateTenantName(name) {
|
|
18571
19234
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.endsWith("-");
|
|
@@ -18633,16 +19296,16 @@ function resolveStackConfig(explicitStack) {
|
|
|
18633
19296
|
{ remediation: { command: "mesh tenant add <name> --stack <stack>" } }
|
|
18634
19297
|
);
|
|
18635
19298
|
}
|
|
18636
|
-
const file =
|
|
18637
|
-
if (!
|
|
19299
|
+
const file = path39.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
19300
|
+
if (!fs32.existsSync(file)) {
|
|
18638
19301
|
throw new MeshCliError(`Stack config not found: ${file}`, {
|
|
18639
19302
|
remediation: { command: "mesh tenant add <name> --stack <stack>" }
|
|
18640
19303
|
});
|
|
18641
19304
|
}
|
|
18642
|
-
return { appRoot, stack, file, content:
|
|
19305
|
+
return { appRoot, stack, file, content: fs32.readFileSync(file, "utf-8") };
|
|
18643
19306
|
}
|
|
18644
19307
|
function readProjectName(appRoot) {
|
|
18645
|
-
const projectDoc = parseDocument(
|
|
19308
|
+
const projectDoc = parseDocument(fs32.readFileSync(path39.join(appRoot, "Pulumi.yaml"), "utf-8"));
|
|
18646
19309
|
return String(projectDoc.get("name") ?? "");
|
|
18647
19310
|
}
|
|
18648
19311
|
function looksLikePlatformLayer(projectName) {
|
|
@@ -18685,12 +19348,12 @@ function registerTenantCommands(program2) {
|
|
|
18685
19348
|
if (!result.ok) {
|
|
18686
19349
|
if (result.reason === "exists") {
|
|
18687
19350
|
throw new MeshCliError(
|
|
18688
|
-
`${result.detail} (${
|
|
19351
|
+
`${result.detail} (${path39.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
|
|
18689
19352
|
);
|
|
18690
19353
|
}
|
|
18691
19354
|
throw new MeshCliError(`${resolved.file}: ${result.detail}`);
|
|
18692
19355
|
}
|
|
18693
|
-
atomicWriteFileSync(resolved.file, result.yaml,
|
|
19356
|
+
atomicWriteFileSync(resolved.file, result.yaml, fs32.statSync(resolved.file).mode & 511);
|
|
18694
19357
|
if (opts.json) {
|
|
18695
19358
|
emitJsonPayload({
|
|
18696
19359
|
ok: true,
|
|
@@ -18703,7 +19366,7 @@ function registerTenantCommands(program2) {
|
|
|
18703
19366
|
return;
|
|
18704
19367
|
}
|
|
18705
19368
|
logInfo(
|
|
18706
|
-
`Tenant '${name}' registered in ${
|
|
19369
|
+
`Tenant '${name}' registered in ${path39.basename(resolved.file)} (mesh:tenants, subdomain '${subdomain}')${result.createdTenantsBlock ? " \u2014 created the mesh:tenants block" : ""}`
|
|
18707
19370
|
);
|
|
18708
19371
|
console.log("");
|
|
18709
19372
|
console.log("Next steps:");
|
|
@@ -18729,7 +19392,7 @@ function registerTenantCommands(program2) {
|
|
|
18729
19392
|
`Project '${projectName}' does not look like a platform layer \u2014 tenants are declared on the tenant platform repo's PLATFORM stack (e.g. mesh-sandbox/platform).`
|
|
18730
19393
|
);
|
|
18731
19394
|
}
|
|
18732
|
-
logInfo(`No tenants declared in ${
|
|
19395
|
+
logInfo(`No tenants declared in ${path39.basename(resolved.file)}.`);
|
|
18733
19396
|
return;
|
|
18734
19397
|
}
|
|
18735
19398
|
logInfo(`Tenants on stack '${resolved.stack}':`);
|
|
@@ -18751,12 +19414,12 @@ var init_tenant = __esm({
|
|
|
18751
19414
|
|
|
18752
19415
|
// libs/mesh-cli/src/commands/tunnel/index.ts
|
|
18753
19416
|
import { spawn as spawn9 } from "child_process";
|
|
18754
|
-
import * as
|
|
19417
|
+
import * as fs33 from "fs";
|
|
18755
19418
|
import * as os13 from "os";
|
|
18756
|
-
import * as
|
|
19419
|
+
import * as path40 from "path";
|
|
18757
19420
|
import {
|
|
18758
|
-
SecretsManagerClient as
|
|
18759
|
-
GetSecretValueCommand as
|
|
19421
|
+
SecretsManagerClient as SecretsManagerClient8,
|
|
19422
|
+
GetSecretValueCommand as GetSecretValueCommand8
|
|
18760
19423
|
} from "@aws-sdk/client-secrets-manager";
|
|
18761
19424
|
function resolveTenantEnv(options) {
|
|
18762
19425
|
if (options.tenant && options.env) {
|
|
@@ -18773,9 +19436,9 @@ function readPulumiAwsRegion(stage) {
|
|
|
18773
19436
|
const preferred = stage ? `Pulumi.${stage}.yaml` : void 0;
|
|
18774
19437
|
const rank = (f) => f === preferred ? 0 : f === "Pulumi.yaml" ? 2 : 1;
|
|
18775
19438
|
try {
|
|
18776
|
-
const files =
|
|
19439
|
+
const files = fs33.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
|
|
18777
19440
|
for (const f of files) {
|
|
18778
|
-
const m =
|
|
19441
|
+
const m = fs33.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
|
|
18779
19442
|
if (m?.[1]) return m[1].trim();
|
|
18780
19443
|
}
|
|
18781
19444
|
} catch {
|
|
@@ -18784,14 +19447,14 @@ function readPulumiAwsRegion(stage) {
|
|
|
18784
19447
|
}
|
|
18785
19448
|
function awsProfileSections(name) {
|
|
18786
19449
|
return [
|
|
18787
|
-
[
|
|
18788
|
-
[
|
|
19450
|
+
[path40.join(os13.homedir(), ".aws", "config"), `[profile ${name}]`],
|
|
19451
|
+
[path40.join(os13.homedir(), ".aws", "credentials"), `[${name}]`]
|
|
18789
19452
|
];
|
|
18790
19453
|
}
|
|
18791
19454
|
function awsProfileExists(name) {
|
|
18792
19455
|
for (const [file, header] of awsProfileSections(name)) {
|
|
18793
19456
|
try {
|
|
18794
|
-
const lines =
|
|
19457
|
+
const lines = fs33.readFileSync(file, "utf-8").split("\n");
|
|
18795
19458
|
if (lines.some((l) => l.trim() === header)) return true;
|
|
18796
19459
|
} catch {
|
|
18797
19460
|
}
|
|
@@ -18817,7 +19480,7 @@ function awsProfileRegion(profile) {
|
|
|
18817
19480
|
if (!profile) return void 0;
|
|
18818
19481
|
for (const [file, header] of awsProfileSections(profile)) {
|
|
18819
19482
|
try {
|
|
18820
|
-
const region = parseProfileRegion(
|
|
19483
|
+
const region = parseProfileRegion(fs33.readFileSync(file, "utf-8"), header);
|
|
18821
19484
|
if (region) return region;
|
|
18822
19485
|
} catch {
|
|
18823
19486
|
}
|
|
@@ -18826,14 +19489,14 @@ function awsProfileRegion(profile) {
|
|
|
18826
19489
|
}
|
|
18827
19490
|
function awsDefaultProfileSections() {
|
|
18828
19491
|
return [
|
|
18829
|
-
[
|
|
18830
|
-
[
|
|
19492
|
+
[path40.join(os13.homedir(), ".aws", "config"), "[default]"],
|
|
19493
|
+
[path40.join(os13.homedir(), ".aws", "credentials"), "[default]"]
|
|
18831
19494
|
];
|
|
18832
19495
|
}
|
|
18833
19496
|
function awsDefaultProfileRegion() {
|
|
18834
19497
|
for (const [file, header] of awsDefaultProfileSections()) {
|
|
18835
19498
|
try {
|
|
18836
|
-
const region = parseProfileRegion(
|
|
19499
|
+
const region = parseProfileRegion(fs33.readFileSync(file, "utf-8"), header);
|
|
18837
19500
|
if (region) return region;
|
|
18838
19501
|
} catch {
|
|
18839
19502
|
}
|
|
@@ -18974,9 +19637,9 @@ async function tunnelServices(serviceNames, options) {
|
|
|
18974
19637
|
process.on("SIGTERM", cleanup);
|
|
18975
19638
|
await Promise.race(
|
|
18976
19639
|
processes.map(
|
|
18977
|
-
(proc) => new Promise((
|
|
18978
|
-
proc.on("exit", () =>
|
|
18979
|
-
proc.on("error", () =>
|
|
19640
|
+
(proc) => new Promise((resolve16) => {
|
|
19641
|
+
proc.on("exit", () => resolve16());
|
|
19642
|
+
proc.on("error", () => resolve16());
|
|
18980
19643
|
})
|
|
18981
19644
|
)
|
|
18982
19645
|
);
|
|
@@ -19019,10 +19682,10 @@ async function tunnelExternal(name, options) {
|
|
|
19019
19682
|
const { appTenant, appStage } = resolveCredentialAxis(options);
|
|
19020
19683
|
const secretId = externalSecretId(appTenant, appStage, name, key);
|
|
19021
19684
|
const setHint = `mesh secrets set external/${name}${key ? ` --key=${key}` : ""}`;
|
|
19022
|
-
const sm = new
|
|
19685
|
+
const sm = new SecretsManagerClient8({});
|
|
19023
19686
|
let creds;
|
|
19024
19687
|
try {
|
|
19025
|
-
const out = await sm.send(new
|
|
19688
|
+
const out = await sm.send(new GetSecretValueCommand8({ SecretId: secretId }));
|
|
19026
19689
|
creds = JSON.parse(out.SecretString ?? "{}");
|
|
19027
19690
|
} catch (err) {
|
|
19028
19691
|
logError(
|
|
@@ -19073,9 +19736,9 @@ async function tunnelExternal(name, options) {
|
|
|
19073
19736
|
};
|
|
19074
19737
|
process.on("SIGINT", cleanup);
|
|
19075
19738
|
process.on("SIGTERM", cleanup);
|
|
19076
|
-
await new Promise((
|
|
19077
|
-
proc.on("exit", () =>
|
|
19078
|
-
proc.on("error", () =>
|
|
19739
|
+
await new Promise((resolve16) => {
|
|
19740
|
+
proc.on("exit", () => resolve16());
|
|
19741
|
+
proc.on("error", () => resolve16());
|
|
19079
19742
|
});
|
|
19080
19743
|
cleanup();
|
|
19081
19744
|
}
|
|
@@ -19202,8 +19865,8 @@ async function resolveToken(opts) {
|
|
|
19202
19865
|
"no token: pass --token, set VCS_TOKEN, or pass --context <platform-context> (after mesh login)"
|
|
19203
19866
|
);
|
|
19204
19867
|
}
|
|
19205
|
-
async function vcsApi(baseUrl, token,
|
|
19206
|
-
const res = await fetch(`${baseUrl}${
|
|
19868
|
+
async function vcsApi(baseUrl, token, path42, method = "GET", body) {
|
|
19869
|
+
const res = await fetch(`${baseUrl}${path42}`, {
|
|
19207
19870
|
method,
|
|
19208
19871
|
headers: {
|
|
19209
19872
|
authorization: `Bearer ${token}`,
|
|
@@ -19214,7 +19877,7 @@ async function vcsApi(baseUrl, token, path41, method = "GET", body) {
|
|
|
19214
19877
|
const data = await res.json().catch(() => ({}));
|
|
19215
19878
|
if (!res.ok) {
|
|
19216
19879
|
throw new Error(
|
|
19217
|
-
`${method} ${
|
|
19880
|
+
`${method} ${path42} failed (${res.status}): ${String(data.error ?? "unknown error")}`
|
|
19218
19881
|
);
|
|
19219
19882
|
}
|
|
19220
19883
|
return data;
|
|
@@ -19257,8 +19920,8 @@ var init_clone = __esm({
|
|
|
19257
19920
|
|
|
19258
19921
|
// libs/mesh-cli/src/commands/vcs/get.ts
|
|
19259
19922
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
19260
|
-
import { join as
|
|
19261
|
-
async function getCommand(repo,
|
|
19923
|
+
import { join as join38, dirname as dirname28 } from "node:path";
|
|
19924
|
+
async function getCommand(repo, path42, opts) {
|
|
19262
19925
|
let vcsBaseUrl;
|
|
19263
19926
|
let token;
|
|
19264
19927
|
if (opts.target) {
|
|
@@ -19273,18 +19936,18 @@ async function getCommand(repo, path41, opts) {
|
|
|
19273
19936
|
if (!token) throw new Error("no token: pass --target (after mesh login), or --token / --context");
|
|
19274
19937
|
const reader = createVcsFolderReader({ vcsBaseUrl, token });
|
|
19275
19938
|
try {
|
|
19276
|
-
const { ref, files } = await reader.readPath(repo,
|
|
19939
|
+
const { ref, files } = await reader.readPath(repo, path42);
|
|
19277
19940
|
if (files.length === 0) {
|
|
19278
|
-
logInfo(`no files at ${repo}:${
|
|
19941
|
+
logInfo(`no files at ${repo}:${path42}`);
|
|
19279
19942
|
return;
|
|
19280
19943
|
}
|
|
19281
19944
|
if (opts.output) {
|
|
19282
19945
|
for (const f of files) {
|
|
19283
|
-
const abs =
|
|
19284
|
-
await mkdir2(
|
|
19946
|
+
const abs = join38(opts.output, f.path);
|
|
19947
|
+
await mkdir2(dirname28(abs), { recursive: true });
|
|
19285
19948
|
await writeFile2(abs, f.contents);
|
|
19286
19949
|
}
|
|
19287
|
-
logInfo(`wrote ${files.length} file(s) from ${repo}:${
|
|
19950
|
+
logInfo(`wrote ${files.length} file(s) from ${repo}:${path42} (${ref}) \u2192 ${opts.output}`);
|
|
19288
19951
|
} else {
|
|
19289
19952
|
for (const f of files) {
|
|
19290
19953
|
if (files.length > 1) process.stdout.write(`
|
|
@@ -19309,10 +19972,10 @@ var init_get = __esm({
|
|
|
19309
19972
|
});
|
|
19310
19973
|
|
|
19311
19974
|
// libs/mesh-cli/src/commands/vcs/drafts.ts
|
|
19312
|
-
async function call(opts, repo,
|
|
19975
|
+
async function call(opts, repo, path42, method = "GET", body) {
|
|
19313
19976
|
const target = await resolveTarget2({ ...opts, repo });
|
|
19314
19977
|
const token = await resolveToken(opts);
|
|
19315
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
19978
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path42}`, method, body);
|
|
19316
19979
|
}
|
|
19317
19980
|
async function draftsListCommand(repo, opts) {
|
|
19318
19981
|
const data = await call(opts, repo, `/drafts${opts.all ? "?all=true" : ""}`);
|
|
@@ -19367,7 +20030,7 @@ var init_drafts = __esm({
|
|
|
19367
20030
|
|
|
19368
20031
|
// libs/mesh-cli/src/commands/vcs/propose.ts
|
|
19369
20032
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
19370
|
-
import { join as
|
|
20033
|
+
import { join as join39 } from "node:path";
|
|
19371
20034
|
function parseStatus(out) {
|
|
19372
20035
|
const tokens = out.split("\0");
|
|
19373
20036
|
const changes = [];
|
|
@@ -19422,7 +20085,7 @@ async function proposeCommand(opts) {
|
|
|
19422
20085
|
async (c) => c.status.startsWith("D") ? { op: "delete", path: c.path } : {
|
|
19423
20086
|
op: "write",
|
|
19424
20087
|
path: c.path,
|
|
19425
|
-
content: await readFile2(
|
|
20088
|
+
content: await readFile2(join39(cwd, c.path), "utf8")
|
|
19426
20089
|
}
|
|
19427
20090
|
)
|
|
19428
20091
|
);
|
|
@@ -19446,8 +20109,8 @@ async function proposeCommand(opts) {
|
|
|
19446
20109
|
operations,
|
|
19447
20110
|
...opts.mergeParent ? { mergeParent: opts.mergeParent } : {}
|
|
19448
20111
|
};
|
|
19449
|
-
const
|
|
19450
|
-
const data = await vcsApi(target.baseUrl, token,
|
|
20112
|
+
const path42 = opts.revise ? `/v1/repos/${target.repo}/proposals/${opts.revise}/revisions` : `/v1/repos/${target.repo}/proposals`;
|
|
20113
|
+
const data = await vcsApi(target.baseUrl, token, path42, "POST", body);
|
|
19451
20114
|
console.log(
|
|
19452
20115
|
`proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 review: mesh vcs show ${target.repo} ${data.proposalId} --url ${target.baseUrl}`
|
|
19453
20116
|
);
|
|
@@ -19460,10 +20123,10 @@ var init_propose = __esm({
|
|
|
19460
20123
|
});
|
|
19461
20124
|
|
|
19462
20125
|
// libs/mesh-cli/src/commands/vcs/review.ts
|
|
19463
|
-
async function call2(opts, repo,
|
|
20126
|
+
async function call2(opts, repo, path42, method = "GET", body) {
|
|
19464
20127
|
const target = await resolveTarget2({ ...opts, repo });
|
|
19465
20128
|
const token = await resolveToken(opts);
|
|
19466
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
20129
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path42}`, method, body);
|
|
19467
20130
|
}
|
|
19468
20131
|
async function proposalsCommand(repo, opts) {
|
|
19469
20132
|
console.log(JSON.stringify(await call2(opts, repo, "/proposals"), null, 2));
|
|
@@ -19604,8 +20267,8 @@ function computeHappyPath(nodes, edges) {
|
|
|
19604
20267
|
const queue = [{ id: startNode.id, path: [startNode.id] }];
|
|
19605
20268
|
const visited = /* @__PURE__ */ new Set([startNode.id]);
|
|
19606
20269
|
while (queue.length > 0) {
|
|
19607
|
-
const { id, path:
|
|
19608
|
-
if (targetIds.has(id)) return
|
|
20270
|
+
const { id, path: path42 } = queue.shift();
|
|
20271
|
+
if (targetIds.has(id)) return path42;
|
|
19609
20272
|
const neighbors = adjacency.get(id) ?? [];
|
|
19610
20273
|
const hasMainEdge = neighbors.some((e) => e.isMainPath);
|
|
19611
20274
|
const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
|
|
@@ -19624,7 +20287,7 @@ function computeHappyPath(nodes, edges) {
|
|
|
19624
20287
|
if (visited.has(neighbor.to)) continue;
|
|
19625
20288
|
if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to))) continue;
|
|
19626
20289
|
visited.add(neighbor.to);
|
|
19627
|
-
queue.push({ id: neighbor.to, path: [...
|
|
20290
|
+
queue.push({ id: neighbor.to, path: [...path42, neighbor.to] });
|
|
19628
20291
|
}
|
|
19629
20292
|
}
|
|
19630
20293
|
return null;
|
|
@@ -19872,11 +20535,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
|
|
|
19872
20535
|
}
|
|
19873
20536
|
}
|
|
19874
20537
|
if (found) {
|
|
19875
|
-
const
|
|
19876
|
-
for (let c = found; c !== void 0; c = prev.get(c))
|
|
19877
|
-
for (let i = 0; i <
|
|
20538
|
+
const path42 = [];
|
|
20539
|
+
for (let c = found; c !== void 0; c = prev.get(c)) path42.unshift(c);
|
|
20540
|
+
for (let i = 0; i < path42.length - 1; i++) mainEdgeKeys.add(edgeKey(path42[i], path42[i + 1]));
|
|
19878
20541
|
mainEdgeKeys.add(edgeKey(found, successExit.id));
|
|
19879
|
-
mainBody =
|
|
20542
|
+
mainBody = path42;
|
|
19880
20543
|
mainTail = [successExit.id];
|
|
19881
20544
|
}
|
|
19882
20545
|
}
|
|
@@ -20278,11 +20941,11 @@ var init_apply_preview_patch = __esm({
|
|
|
20278
20941
|
|
|
20279
20942
|
// libs/workflow-model/src/process-artifact.ts
|
|
20280
20943
|
import { z as z4 } from "zod";
|
|
20281
|
-
function formatPath(
|
|
20282
|
-
return
|
|
20944
|
+
function formatPath(path42) {
|
|
20945
|
+
return path42.length > 0 ? z4.core.toDotPath(path42) : "(root)";
|
|
20283
20946
|
}
|
|
20284
20947
|
function issueToDiagnostic(issue) {
|
|
20285
|
-
const
|
|
20948
|
+
const path42 = formatPath(issue.path);
|
|
20286
20949
|
let code = "SCHEMA_INVALID";
|
|
20287
20950
|
if (issue.code === "custom") {
|
|
20288
20951
|
const paramCode = issue.params?.code;
|
|
@@ -20293,8 +20956,8 @@ function issueToDiagnostic(issue) {
|
|
|
20293
20956
|
return {
|
|
20294
20957
|
severity: "error",
|
|
20295
20958
|
code,
|
|
20296
|
-
message: `${
|
|
20297
|
-
path:
|
|
20959
|
+
message: `${path42}: ${issue.message}`,
|
|
20960
|
+
path: path42
|
|
20298
20961
|
};
|
|
20299
20962
|
}
|
|
20300
20963
|
function parseProcessArtifact(json) {
|
|
@@ -20374,20 +21037,20 @@ var init_process_artifact = __esm({
|
|
|
20374
21037
|
outcome: "DUPLICATE_OUTCOME_ID"
|
|
20375
21038
|
};
|
|
20376
21039
|
const idFirstSeenAt = /* @__PURE__ */ new Map();
|
|
20377
|
-
const checkId = (id, kind,
|
|
21040
|
+
const checkId = (id, kind, path42) => {
|
|
20378
21041
|
const first = idFirstSeenAt.get(id);
|
|
20379
21042
|
if (first) {
|
|
20380
21043
|
const kindLabel = kind === first.kind ? kind : "process";
|
|
20381
21044
|
ctx.addIssue({
|
|
20382
21045
|
code: "custom",
|
|
20383
|
-
path:
|
|
20384
|
-
message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(
|
|
21046
|
+
path: path42,
|
|
21047
|
+
message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(path42)} collides with ${first.kind} at ${z4.core.toDotPath(first.path)}.`,
|
|
20385
21048
|
params: {
|
|
20386
21049
|
code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
|
|
20387
21050
|
}
|
|
20388
21051
|
});
|
|
20389
21052
|
} else {
|
|
20390
|
-
idFirstSeenAt.set(id, { kind, path:
|
|
21053
|
+
idFirstSeenAt.set(id, { kind, path: path42 });
|
|
20391
21054
|
}
|
|
20392
21055
|
};
|
|
20393
21056
|
process2.stages.forEach((stage, stageIndex) => {
|
|
@@ -20425,13 +21088,13 @@ function lintProcess(artifact, inventory, predicateNames) {
|
|
|
20425
21088
|
const internalCommands = process2.internalCommands ?? [];
|
|
20426
21089
|
const internalSet = new Set(internalCommands);
|
|
20427
21090
|
const boundCommandPaths = /* @__PURE__ */ new Map();
|
|
20428
|
-
const checkPredicate = (predicate,
|
|
21091
|
+
const checkPredicate = (predicate, path42) => {
|
|
20429
21092
|
if (!predicateSet.has(predicate)) {
|
|
20430
21093
|
diagnostics.push({
|
|
20431
21094
|
severity: "error",
|
|
20432
21095
|
code: "UNKNOWN_PREDICATE",
|
|
20433
|
-
message: `Predicate "${predicate}" referenced at ${
|
|
20434
|
-
path:
|
|
21096
|
+
message: `Predicate "${predicate}" referenced at ${path42} is not in the predicate registry.`,
|
|
21097
|
+
path: path42
|
|
20435
21098
|
});
|
|
20436
21099
|
}
|
|
20437
21100
|
};
|
|
@@ -20572,8 +21235,8 @@ var init_src3 = __esm({
|
|
|
20572
21235
|
});
|
|
20573
21236
|
|
|
20574
21237
|
// libs/mesh-cli/src/commands/workflow.ts
|
|
20575
|
-
import * as
|
|
20576
|
-
import * as
|
|
21238
|
+
import * as fs34 from "fs";
|
|
21239
|
+
import * as path41 from "path";
|
|
20577
21240
|
import { createRequire as createRequire2 } from "module";
|
|
20578
21241
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
20579
21242
|
function resolveExtractorPath() {
|
|
@@ -20589,7 +21252,7 @@ function runExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
20589
21252
|
let explicitProcessArtifact;
|
|
20590
21253
|
if (explicitProcessPath) {
|
|
20591
21254
|
try {
|
|
20592
|
-
explicitProcessArtifact = JSON.parse(
|
|
21255
|
+
explicitProcessArtifact = JSON.parse(fs34.readFileSync(explicitProcessPath, "utf-8"));
|
|
20593
21256
|
} catch (err) {
|
|
20594
21257
|
logError(
|
|
20595
21258
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -20689,7 +21352,7 @@ function runLintExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
20689
21352
|
let explicitProcessArtifact;
|
|
20690
21353
|
if (explicitProcessPath) {
|
|
20691
21354
|
try {
|
|
20692
|
-
explicitProcessArtifact = JSON.parse(
|
|
21355
|
+
explicitProcessArtifact = JSON.parse(fs34.readFileSync(explicitProcessPath, "utf-8"));
|
|
20693
21356
|
} catch (err) {
|
|
20694
21357
|
logError(
|
|
20695
21358
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -20794,16 +21457,16 @@ function registerWorkflowCommands(program2) {
|
|
|
20794
21457
|
).action(
|
|
20795
21458
|
async (targetPath, opts) => {
|
|
20796
21459
|
try {
|
|
20797
|
-
const resolvedPath =
|
|
20798
|
-
if (!
|
|
21460
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21461
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
20799
21462
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
20800
21463
|
process.exitCode = 1;
|
|
20801
21464
|
return;
|
|
20802
21465
|
}
|
|
20803
21466
|
let resolvedProcessPath;
|
|
20804
21467
|
if (opts.process) {
|
|
20805
|
-
resolvedProcessPath =
|
|
20806
|
-
if (!
|
|
21468
|
+
resolvedProcessPath = path41.resolve(opts.process);
|
|
21469
|
+
if (!fs34.existsSync(resolvedProcessPath)) {
|
|
20807
21470
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
20808
21471
|
process.exitCode = 1;
|
|
20809
21472
|
return;
|
|
@@ -20852,8 +21515,8 @@ function registerWorkflowCommands(program2) {
|
|
|
20852
21515
|
"Extract the as-built command/query/activity inventory from Temporal workflow source\n\nThe inventory ({ workflowType, commands, queries, activities }) is the process-\nconformance lint's code-side input \u2014 it is never hand-edited. Supports single\nfiles or directories."
|
|
20853
21516
|
).action(async (targetPath) => {
|
|
20854
21517
|
try {
|
|
20855
|
-
const resolvedPath =
|
|
20856
|
-
if (!
|
|
21518
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21519
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
20857
21520
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
20858
21521
|
process.exitCode = 1;
|
|
20859
21522
|
return;
|
|
@@ -20896,16 +21559,16 @@ function registerWorkflowCommands(program2) {
|
|
|
20896
21559
|
"Comma-separated named-predicate registry (enables UNKNOWN_PREDICATE checks)"
|
|
20897
21560
|
).action(async (targetPath, opts) => {
|
|
20898
21561
|
try {
|
|
20899
|
-
const resolvedPath =
|
|
20900
|
-
if (!
|
|
21562
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21563
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
20901
21564
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
20902
21565
|
process.exitCode = 1;
|
|
20903
21566
|
return;
|
|
20904
21567
|
}
|
|
20905
21568
|
let resolvedProcessPath;
|
|
20906
21569
|
if (opts.process) {
|
|
20907
|
-
resolvedProcessPath =
|
|
20908
|
-
if (!
|
|
21570
|
+
resolvedProcessPath = path41.resolve(opts.process);
|
|
21571
|
+
if (!fs34.existsSync(resolvedProcessPath)) {
|
|
20909
21572
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
20910
21573
|
process.exitCode = 1;
|
|
20911
21574
|
return;
|