@mesh-tech/mesh-cli 0.14.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 +686 -226
- package/dist/bin/mesh.js.map +3 -3
- 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/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 +2 -2
- package/skills/core/SKILL.md +1 -1
- 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
|
}
|
|
@@ -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`);
|
|
@@ -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",
|
|
@@ -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,12 +3789,39 @@ 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
3826
|
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3724
3827
|
const ssm = new SSMClient5(awsConfig);
|
|
@@ -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
|
|
|
@@ -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}`);
|
|
@@ -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",
|
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -17323,22 +17783,22 @@ var init_secrets = __esm({
|
|
|
17323
17783
|
|
|
17324
17784
|
// libs/mesh-cli/src/commands/stack.ts
|
|
17325
17785
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
17326
|
-
import * as
|
|
17327
|
-
import * as
|
|
17786
|
+
import * as path38 from "path";
|
|
17787
|
+
import * as fs31 from "fs";
|
|
17328
17788
|
import { parse as parseYaml5 } from "yaml";
|
|
17329
17789
|
function readTopLevelYamlKey(appRoot, stack, key) {
|
|
17330
|
-
const configFile =
|
|
17331
|
-
if (!
|
|
17332
|
-
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");
|
|
17333
17793
|
const pattern = new RegExp(`^${key}:\\s*(.+)$`, "m");
|
|
17334
17794
|
const match = content.match(pattern);
|
|
17335
17795
|
if (!match) return null;
|
|
17336
17796
|
return match[1].trim().replace(/^["']|["']$/g, "");
|
|
17337
17797
|
}
|
|
17338
17798
|
function readConfigBlockKey(appRoot, stack, key) {
|
|
17339
|
-
const configFile =
|
|
17340
|
-
if (!
|
|
17341
|
-
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");
|
|
17342
17802
|
const pattern = new RegExp(`^\\s{2}${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
|
|
17343
17803
|
const match = content.match(pattern);
|
|
17344
17804
|
if (!match) return null;
|
|
@@ -17398,11 +17858,11 @@ ${records}
|
|
|
17398
17858
|
}
|
|
17399
17859
|
}
|
|
17400
17860
|
function readBaseConfigFromYaml(appRoot, stack) {
|
|
17401
|
-
const file =
|
|
17402
|
-
if (!
|
|
17861
|
+
const file = path38.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
17862
|
+
if (!fs31.existsSync(file)) return {};
|
|
17403
17863
|
let doc;
|
|
17404
17864
|
try {
|
|
17405
|
-
doc = parseYaml5(
|
|
17865
|
+
doc = parseYaml5(fs31.readFileSync(file, "utf-8"));
|
|
17406
17866
|
} catch {
|
|
17407
17867
|
return {};
|
|
17408
17868
|
}
|
|
@@ -17479,7 +17939,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
17479
17939
|
}
|
|
17480
17940
|
}
|
|
17481
17941
|
const baseConfigPath = `${appRoot}/Pulumi.${baseStack}.yaml`;
|
|
17482
|
-
if (!
|
|
17942
|
+
if (!fs31.existsSync(baseConfigPath)) {
|
|
17483
17943
|
logError(`Stack config not found: Pulumi.${baseStack}.yaml`);
|
|
17484
17944
|
process.exit(1);
|
|
17485
17945
|
}
|
|
@@ -17501,8 +17961,8 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
17501
17961
|
);
|
|
17502
17962
|
}
|
|
17503
17963
|
}
|
|
17504
|
-
const newConfigPath =
|
|
17505
|
-
const configExists =
|
|
17964
|
+
const newConfigPath = path38.join(appRoot, `Pulumi.${newStack}.yaml`);
|
|
17965
|
+
const configExists = fs31.existsSync(newConfigPath);
|
|
17506
17966
|
const secretsProvider = readTopLevelYamlKey(appRoot, baseStack, "secretsprovider");
|
|
17507
17967
|
const credEnv = await resolvePulumiEnv({ appRoot, stack: baseStack });
|
|
17508
17968
|
const pulumiEnv = { ...process.env, ...credEnv };
|
|
@@ -18149,9 +18609,9 @@ var init_capture_history = __esm({
|
|
|
18149
18609
|
|
|
18150
18610
|
// libs/mesh-cli/src/commands/temporal.ts
|
|
18151
18611
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18152
|
-
import { writeFileSync as
|
|
18612
|
+
import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync22 } from "node:fs";
|
|
18153
18613
|
import { homedir as homedir9 } from "node:os";
|
|
18154
|
-
import { dirname as
|
|
18614
|
+
import { dirname as dirname27, join as join35, resolve as resolve14 } from "node:path";
|
|
18155
18615
|
async function resolveConnection(options) {
|
|
18156
18616
|
if (options.address && options.namespace) {
|
|
18157
18617
|
return { address: options.address, namespace: options.namespace };
|
|
@@ -18521,7 +18981,7 @@ async function recoverConversation(workflowId, runId, options) {
|
|
|
18521
18981
|
};
|
|
18522
18982
|
const output2 = JSON.stringify(blob, null, 2) + "\n";
|
|
18523
18983
|
if (options.out) {
|
|
18524
|
-
|
|
18984
|
+
writeFileSync23(options.out, output2);
|
|
18525
18985
|
logSuccess(
|
|
18526
18986
|
`Wrote snapshot (${messages.length} messages) to ${options.out}`
|
|
18527
18987
|
);
|
|
@@ -18544,7 +19004,7 @@ async function recoverConversation(workflowId, runId, options) {
|
|
|
18544
19004
|
2
|
|
18545
19005
|
) + "\n" : renderTranscriptMarkdown(turns);
|
|
18546
19006
|
if (options.out) {
|
|
18547
|
-
|
|
19007
|
+
writeFileSync23(options.out, output);
|
|
18548
19008
|
logSuccess(`Wrote ${turns.length} turns to ${options.out}`);
|
|
18549
19009
|
} else {
|
|
18550
19010
|
process.stdout.write(output);
|
|
@@ -18579,10 +19039,10 @@ async function captureHistory(workflowId, runId, options) {
|
|
|
18579
19039
|
}
|
|
18580
19040
|
const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
|
|
18581
19041
|
const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
|
|
18582
|
-
const outPath = options.out ??
|
|
18583
|
-
mkdirSync22(
|
|
19042
|
+
const outPath = options.out ?? join35(homedir9(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
|
|
19043
|
+
mkdirSync22(dirname27(outPath), { recursive: true });
|
|
18584
19044
|
if (options.out) warnIfNotGitIgnored(options.out);
|
|
18585
|
-
|
|
19045
|
+
writeFileSync23(outPath, JSON.stringify(fixture, null, 2) + "\n");
|
|
18586
19046
|
logSuccess(
|
|
18587
19047
|
`Wrote replay history (${eventCount} events, ${decryptedPayloads} payloads decrypted) to ${outPath}`
|
|
18588
19048
|
);
|
|
@@ -18597,9 +19057,9 @@ function sanitizeFileId(id) {
|
|
|
18597
19057
|
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
18598
19058
|
}
|
|
18599
19059
|
function warnIfNotGitIgnored(outPath) {
|
|
18600
|
-
const abs =
|
|
19060
|
+
const abs = resolve14(outPath);
|
|
18601
19061
|
try {
|
|
18602
|
-
const res = spawnSync4("git", ["-C",
|
|
19062
|
+
const res = spawnSync4("git", ["-C", dirname27(abs), "check-ignore", "-q", abs], {
|
|
18603
19063
|
stdio: "ignore"
|
|
18604
19064
|
});
|
|
18605
19065
|
if (res.status !== 1) return;
|
|
@@ -18767,8 +19227,8 @@ var init_temporal = __esm({
|
|
|
18767
19227
|
|
|
18768
19228
|
// libs/mesh-cli/src/commands/tenant.ts
|
|
18769
19229
|
import chalk6 from "chalk";
|
|
18770
|
-
import * as
|
|
18771
|
-
import * as
|
|
19230
|
+
import * as fs32 from "fs";
|
|
19231
|
+
import * as path39 from "path";
|
|
18772
19232
|
import { parseDocument, YAMLMap, isMap } from "yaml";
|
|
18773
19233
|
function validateTenantName(name) {
|
|
18774
19234
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.endsWith("-");
|
|
@@ -18836,16 +19296,16 @@ function resolveStackConfig(explicitStack) {
|
|
|
18836
19296
|
{ remediation: { command: "mesh tenant add <name> --stack <stack>" } }
|
|
18837
19297
|
);
|
|
18838
19298
|
}
|
|
18839
|
-
const file =
|
|
18840
|
-
if (!
|
|
19299
|
+
const file = path39.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
19300
|
+
if (!fs32.existsSync(file)) {
|
|
18841
19301
|
throw new MeshCliError(`Stack config not found: ${file}`, {
|
|
18842
19302
|
remediation: { command: "mesh tenant add <name> --stack <stack>" }
|
|
18843
19303
|
});
|
|
18844
19304
|
}
|
|
18845
|
-
return { appRoot, stack, file, content:
|
|
19305
|
+
return { appRoot, stack, file, content: fs32.readFileSync(file, "utf-8") };
|
|
18846
19306
|
}
|
|
18847
19307
|
function readProjectName(appRoot) {
|
|
18848
|
-
const projectDoc = parseDocument(
|
|
19308
|
+
const projectDoc = parseDocument(fs32.readFileSync(path39.join(appRoot, "Pulumi.yaml"), "utf-8"));
|
|
18849
19309
|
return String(projectDoc.get("name") ?? "");
|
|
18850
19310
|
}
|
|
18851
19311
|
function looksLikePlatformLayer(projectName) {
|
|
@@ -18888,12 +19348,12 @@ function registerTenantCommands(program2) {
|
|
|
18888
19348
|
if (!result.ok) {
|
|
18889
19349
|
if (result.reason === "exists") {
|
|
18890
19350
|
throw new MeshCliError(
|
|
18891
|
-
`${result.detail} (${
|
|
19351
|
+
`${result.detail} (${path39.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
|
|
18892
19352
|
);
|
|
18893
19353
|
}
|
|
18894
19354
|
throw new MeshCliError(`${resolved.file}: ${result.detail}`);
|
|
18895
19355
|
}
|
|
18896
|
-
atomicWriteFileSync(resolved.file, result.yaml,
|
|
19356
|
+
atomicWriteFileSync(resolved.file, result.yaml, fs32.statSync(resolved.file).mode & 511);
|
|
18897
19357
|
if (opts.json) {
|
|
18898
19358
|
emitJsonPayload({
|
|
18899
19359
|
ok: true,
|
|
@@ -18906,7 +19366,7 @@ function registerTenantCommands(program2) {
|
|
|
18906
19366
|
return;
|
|
18907
19367
|
}
|
|
18908
19368
|
logInfo(
|
|
18909
|
-
`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" : ""}`
|
|
18910
19370
|
);
|
|
18911
19371
|
console.log("");
|
|
18912
19372
|
console.log("Next steps:");
|
|
@@ -18932,7 +19392,7 @@ function registerTenantCommands(program2) {
|
|
|
18932
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).`
|
|
18933
19393
|
);
|
|
18934
19394
|
}
|
|
18935
|
-
logInfo(`No tenants declared in ${
|
|
19395
|
+
logInfo(`No tenants declared in ${path39.basename(resolved.file)}.`);
|
|
18936
19396
|
return;
|
|
18937
19397
|
}
|
|
18938
19398
|
logInfo(`Tenants on stack '${resolved.stack}':`);
|
|
@@ -18954,9 +19414,9 @@ var init_tenant = __esm({
|
|
|
18954
19414
|
|
|
18955
19415
|
// libs/mesh-cli/src/commands/tunnel/index.ts
|
|
18956
19416
|
import { spawn as spawn9 } from "child_process";
|
|
18957
|
-
import * as
|
|
19417
|
+
import * as fs33 from "fs";
|
|
18958
19418
|
import * as os13 from "os";
|
|
18959
|
-
import * as
|
|
19419
|
+
import * as path40 from "path";
|
|
18960
19420
|
import {
|
|
18961
19421
|
SecretsManagerClient as SecretsManagerClient8,
|
|
18962
19422
|
GetSecretValueCommand as GetSecretValueCommand8
|
|
@@ -18976,9 +19436,9 @@ function readPulumiAwsRegion(stage) {
|
|
|
18976
19436
|
const preferred = stage ? `Pulumi.${stage}.yaml` : void 0;
|
|
18977
19437
|
const rank = (f) => f === preferred ? 0 : f === "Pulumi.yaml" ? 2 : 1;
|
|
18978
19438
|
try {
|
|
18979
|
-
const files =
|
|
19439
|
+
const files = fs33.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
|
|
18980
19440
|
for (const f of files) {
|
|
18981
|
-
const m =
|
|
19441
|
+
const m = fs33.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
|
|
18982
19442
|
if (m?.[1]) return m[1].trim();
|
|
18983
19443
|
}
|
|
18984
19444
|
} catch {
|
|
@@ -18987,14 +19447,14 @@ function readPulumiAwsRegion(stage) {
|
|
|
18987
19447
|
}
|
|
18988
19448
|
function awsProfileSections(name) {
|
|
18989
19449
|
return [
|
|
18990
|
-
[
|
|
18991
|
-
[
|
|
19450
|
+
[path40.join(os13.homedir(), ".aws", "config"), `[profile ${name}]`],
|
|
19451
|
+
[path40.join(os13.homedir(), ".aws", "credentials"), `[${name}]`]
|
|
18992
19452
|
];
|
|
18993
19453
|
}
|
|
18994
19454
|
function awsProfileExists(name) {
|
|
18995
19455
|
for (const [file, header] of awsProfileSections(name)) {
|
|
18996
19456
|
try {
|
|
18997
|
-
const lines =
|
|
19457
|
+
const lines = fs33.readFileSync(file, "utf-8").split("\n");
|
|
18998
19458
|
if (lines.some((l) => l.trim() === header)) return true;
|
|
18999
19459
|
} catch {
|
|
19000
19460
|
}
|
|
@@ -19020,7 +19480,7 @@ function awsProfileRegion(profile) {
|
|
|
19020
19480
|
if (!profile) return void 0;
|
|
19021
19481
|
for (const [file, header] of awsProfileSections(profile)) {
|
|
19022
19482
|
try {
|
|
19023
|
-
const region = parseProfileRegion(
|
|
19483
|
+
const region = parseProfileRegion(fs33.readFileSync(file, "utf-8"), header);
|
|
19024
19484
|
if (region) return region;
|
|
19025
19485
|
} catch {
|
|
19026
19486
|
}
|
|
@@ -19029,14 +19489,14 @@ function awsProfileRegion(profile) {
|
|
|
19029
19489
|
}
|
|
19030
19490
|
function awsDefaultProfileSections() {
|
|
19031
19491
|
return [
|
|
19032
|
-
[
|
|
19033
|
-
[
|
|
19492
|
+
[path40.join(os13.homedir(), ".aws", "config"), "[default]"],
|
|
19493
|
+
[path40.join(os13.homedir(), ".aws", "credentials"), "[default]"]
|
|
19034
19494
|
];
|
|
19035
19495
|
}
|
|
19036
19496
|
function awsDefaultProfileRegion() {
|
|
19037
19497
|
for (const [file, header] of awsDefaultProfileSections()) {
|
|
19038
19498
|
try {
|
|
19039
|
-
const region = parseProfileRegion(
|
|
19499
|
+
const region = parseProfileRegion(fs33.readFileSync(file, "utf-8"), header);
|
|
19040
19500
|
if (region) return region;
|
|
19041
19501
|
} catch {
|
|
19042
19502
|
}
|
|
@@ -19177,9 +19637,9 @@ async function tunnelServices(serviceNames, options) {
|
|
|
19177
19637
|
process.on("SIGTERM", cleanup);
|
|
19178
19638
|
await Promise.race(
|
|
19179
19639
|
processes.map(
|
|
19180
|
-
(proc) => new Promise((
|
|
19181
|
-
proc.on("exit", () =>
|
|
19182
|
-
proc.on("error", () =>
|
|
19640
|
+
(proc) => new Promise((resolve16) => {
|
|
19641
|
+
proc.on("exit", () => resolve16());
|
|
19642
|
+
proc.on("error", () => resolve16());
|
|
19183
19643
|
})
|
|
19184
19644
|
)
|
|
19185
19645
|
);
|
|
@@ -19276,9 +19736,9 @@ async function tunnelExternal(name, options) {
|
|
|
19276
19736
|
};
|
|
19277
19737
|
process.on("SIGINT", cleanup);
|
|
19278
19738
|
process.on("SIGTERM", cleanup);
|
|
19279
|
-
await new Promise((
|
|
19280
|
-
proc.on("exit", () =>
|
|
19281
|
-
proc.on("error", () =>
|
|
19739
|
+
await new Promise((resolve16) => {
|
|
19740
|
+
proc.on("exit", () => resolve16());
|
|
19741
|
+
proc.on("error", () => resolve16());
|
|
19282
19742
|
});
|
|
19283
19743
|
cleanup();
|
|
19284
19744
|
}
|
|
@@ -19405,8 +19865,8 @@ async function resolveToken(opts) {
|
|
|
19405
19865
|
"no token: pass --token, set VCS_TOKEN, or pass --context <platform-context> (after mesh login)"
|
|
19406
19866
|
);
|
|
19407
19867
|
}
|
|
19408
|
-
async function vcsApi(baseUrl, token,
|
|
19409
|
-
const res = await fetch(`${baseUrl}${
|
|
19868
|
+
async function vcsApi(baseUrl, token, path42, method = "GET", body) {
|
|
19869
|
+
const res = await fetch(`${baseUrl}${path42}`, {
|
|
19410
19870
|
method,
|
|
19411
19871
|
headers: {
|
|
19412
19872
|
authorization: `Bearer ${token}`,
|
|
@@ -19417,7 +19877,7 @@ async function vcsApi(baseUrl, token, path41, method = "GET", body) {
|
|
|
19417
19877
|
const data = await res.json().catch(() => ({}));
|
|
19418
19878
|
if (!res.ok) {
|
|
19419
19879
|
throw new Error(
|
|
19420
|
-
`${method} ${
|
|
19880
|
+
`${method} ${path42} failed (${res.status}): ${String(data.error ?? "unknown error")}`
|
|
19421
19881
|
);
|
|
19422
19882
|
}
|
|
19423
19883
|
return data;
|
|
@@ -19460,8 +19920,8 @@ var init_clone = __esm({
|
|
|
19460
19920
|
|
|
19461
19921
|
// libs/mesh-cli/src/commands/vcs/get.ts
|
|
19462
19922
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
19463
|
-
import { join as
|
|
19464
|
-
async function getCommand(repo,
|
|
19923
|
+
import { join as join38, dirname as dirname28 } from "node:path";
|
|
19924
|
+
async function getCommand(repo, path42, opts) {
|
|
19465
19925
|
let vcsBaseUrl;
|
|
19466
19926
|
let token;
|
|
19467
19927
|
if (opts.target) {
|
|
@@ -19476,18 +19936,18 @@ async function getCommand(repo, path41, opts) {
|
|
|
19476
19936
|
if (!token) throw new Error("no token: pass --target (after mesh login), or --token / --context");
|
|
19477
19937
|
const reader = createVcsFolderReader({ vcsBaseUrl, token });
|
|
19478
19938
|
try {
|
|
19479
|
-
const { ref, files } = await reader.readPath(repo,
|
|
19939
|
+
const { ref, files } = await reader.readPath(repo, path42);
|
|
19480
19940
|
if (files.length === 0) {
|
|
19481
|
-
logInfo(`no files at ${repo}:${
|
|
19941
|
+
logInfo(`no files at ${repo}:${path42}`);
|
|
19482
19942
|
return;
|
|
19483
19943
|
}
|
|
19484
19944
|
if (opts.output) {
|
|
19485
19945
|
for (const f of files) {
|
|
19486
|
-
const abs =
|
|
19487
|
-
await mkdir2(
|
|
19946
|
+
const abs = join38(opts.output, f.path);
|
|
19947
|
+
await mkdir2(dirname28(abs), { recursive: true });
|
|
19488
19948
|
await writeFile2(abs, f.contents);
|
|
19489
19949
|
}
|
|
19490
|
-
logInfo(`wrote ${files.length} file(s) from ${repo}:${
|
|
19950
|
+
logInfo(`wrote ${files.length} file(s) from ${repo}:${path42} (${ref}) \u2192 ${opts.output}`);
|
|
19491
19951
|
} else {
|
|
19492
19952
|
for (const f of files) {
|
|
19493
19953
|
if (files.length > 1) process.stdout.write(`
|
|
@@ -19512,10 +19972,10 @@ var init_get = __esm({
|
|
|
19512
19972
|
});
|
|
19513
19973
|
|
|
19514
19974
|
// libs/mesh-cli/src/commands/vcs/drafts.ts
|
|
19515
|
-
async function call(opts, repo,
|
|
19975
|
+
async function call(opts, repo, path42, method = "GET", body) {
|
|
19516
19976
|
const target = await resolveTarget2({ ...opts, repo });
|
|
19517
19977
|
const token = await resolveToken(opts);
|
|
19518
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
19978
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path42}`, method, body);
|
|
19519
19979
|
}
|
|
19520
19980
|
async function draftsListCommand(repo, opts) {
|
|
19521
19981
|
const data = await call(opts, repo, `/drafts${opts.all ? "?all=true" : ""}`);
|
|
@@ -19570,7 +20030,7 @@ var init_drafts = __esm({
|
|
|
19570
20030
|
|
|
19571
20031
|
// libs/mesh-cli/src/commands/vcs/propose.ts
|
|
19572
20032
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
19573
|
-
import { join as
|
|
20033
|
+
import { join as join39 } from "node:path";
|
|
19574
20034
|
function parseStatus(out) {
|
|
19575
20035
|
const tokens = out.split("\0");
|
|
19576
20036
|
const changes = [];
|
|
@@ -19625,7 +20085,7 @@ async function proposeCommand(opts) {
|
|
|
19625
20085
|
async (c) => c.status.startsWith("D") ? { op: "delete", path: c.path } : {
|
|
19626
20086
|
op: "write",
|
|
19627
20087
|
path: c.path,
|
|
19628
|
-
content: await readFile2(
|
|
20088
|
+
content: await readFile2(join39(cwd, c.path), "utf8")
|
|
19629
20089
|
}
|
|
19630
20090
|
)
|
|
19631
20091
|
);
|
|
@@ -19649,8 +20109,8 @@ async function proposeCommand(opts) {
|
|
|
19649
20109
|
operations,
|
|
19650
20110
|
...opts.mergeParent ? { mergeParent: opts.mergeParent } : {}
|
|
19651
20111
|
};
|
|
19652
|
-
const
|
|
19653
|
-
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);
|
|
19654
20114
|
console.log(
|
|
19655
20115
|
`proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 review: mesh vcs show ${target.repo} ${data.proposalId} --url ${target.baseUrl}`
|
|
19656
20116
|
);
|
|
@@ -19663,10 +20123,10 @@ var init_propose = __esm({
|
|
|
19663
20123
|
});
|
|
19664
20124
|
|
|
19665
20125
|
// libs/mesh-cli/src/commands/vcs/review.ts
|
|
19666
|
-
async function call2(opts, repo,
|
|
20126
|
+
async function call2(opts, repo, path42, method = "GET", body) {
|
|
19667
20127
|
const target = await resolveTarget2({ ...opts, repo });
|
|
19668
20128
|
const token = await resolveToken(opts);
|
|
19669
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
20129
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path42}`, method, body);
|
|
19670
20130
|
}
|
|
19671
20131
|
async function proposalsCommand(repo, opts) {
|
|
19672
20132
|
console.log(JSON.stringify(await call2(opts, repo, "/proposals"), null, 2));
|
|
@@ -19807,8 +20267,8 @@ function computeHappyPath(nodes, edges) {
|
|
|
19807
20267
|
const queue = [{ id: startNode.id, path: [startNode.id] }];
|
|
19808
20268
|
const visited = /* @__PURE__ */ new Set([startNode.id]);
|
|
19809
20269
|
while (queue.length > 0) {
|
|
19810
|
-
const { id, path:
|
|
19811
|
-
if (targetIds.has(id)) return
|
|
20270
|
+
const { id, path: path42 } = queue.shift();
|
|
20271
|
+
if (targetIds.has(id)) return path42;
|
|
19812
20272
|
const neighbors = adjacency.get(id) ?? [];
|
|
19813
20273
|
const hasMainEdge = neighbors.some((e) => e.isMainPath);
|
|
19814
20274
|
const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
|
|
@@ -19827,7 +20287,7 @@ function computeHappyPath(nodes, edges) {
|
|
|
19827
20287
|
if (visited.has(neighbor.to)) continue;
|
|
19828
20288
|
if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to))) continue;
|
|
19829
20289
|
visited.add(neighbor.to);
|
|
19830
|
-
queue.push({ id: neighbor.to, path: [...
|
|
20290
|
+
queue.push({ id: neighbor.to, path: [...path42, neighbor.to] });
|
|
19831
20291
|
}
|
|
19832
20292
|
}
|
|
19833
20293
|
return null;
|
|
@@ -20075,11 +20535,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
|
|
|
20075
20535
|
}
|
|
20076
20536
|
}
|
|
20077
20537
|
if (found) {
|
|
20078
|
-
const
|
|
20079
|
-
for (let c = found; c !== void 0; c = prev.get(c))
|
|
20080
|
-
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]));
|
|
20081
20541
|
mainEdgeKeys.add(edgeKey(found, successExit.id));
|
|
20082
|
-
mainBody =
|
|
20542
|
+
mainBody = path42;
|
|
20083
20543
|
mainTail = [successExit.id];
|
|
20084
20544
|
}
|
|
20085
20545
|
}
|
|
@@ -20481,11 +20941,11 @@ var init_apply_preview_patch = __esm({
|
|
|
20481
20941
|
|
|
20482
20942
|
// libs/workflow-model/src/process-artifact.ts
|
|
20483
20943
|
import { z as z4 } from "zod";
|
|
20484
|
-
function formatPath(
|
|
20485
|
-
return
|
|
20944
|
+
function formatPath(path42) {
|
|
20945
|
+
return path42.length > 0 ? z4.core.toDotPath(path42) : "(root)";
|
|
20486
20946
|
}
|
|
20487
20947
|
function issueToDiagnostic(issue) {
|
|
20488
|
-
const
|
|
20948
|
+
const path42 = formatPath(issue.path);
|
|
20489
20949
|
let code = "SCHEMA_INVALID";
|
|
20490
20950
|
if (issue.code === "custom") {
|
|
20491
20951
|
const paramCode = issue.params?.code;
|
|
@@ -20496,8 +20956,8 @@ function issueToDiagnostic(issue) {
|
|
|
20496
20956
|
return {
|
|
20497
20957
|
severity: "error",
|
|
20498
20958
|
code,
|
|
20499
|
-
message: `${
|
|
20500
|
-
path:
|
|
20959
|
+
message: `${path42}: ${issue.message}`,
|
|
20960
|
+
path: path42
|
|
20501
20961
|
};
|
|
20502
20962
|
}
|
|
20503
20963
|
function parseProcessArtifact(json) {
|
|
@@ -20577,20 +21037,20 @@ var init_process_artifact = __esm({
|
|
|
20577
21037
|
outcome: "DUPLICATE_OUTCOME_ID"
|
|
20578
21038
|
};
|
|
20579
21039
|
const idFirstSeenAt = /* @__PURE__ */ new Map();
|
|
20580
|
-
const checkId = (id, kind,
|
|
21040
|
+
const checkId = (id, kind, path42) => {
|
|
20581
21041
|
const first = idFirstSeenAt.get(id);
|
|
20582
21042
|
if (first) {
|
|
20583
21043
|
const kindLabel = kind === first.kind ? kind : "process";
|
|
20584
21044
|
ctx.addIssue({
|
|
20585
21045
|
code: "custom",
|
|
20586
|
-
path:
|
|
20587
|
-
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)}.`,
|
|
20588
21048
|
params: {
|
|
20589
21049
|
code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
|
|
20590
21050
|
}
|
|
20591
21051
|
});
|
|
20592
21052
|
} else {
|
|
20593
|
-
idFirstSeenAt.set(id, { kind, path:
|
|
21053
|
+
idFirstSeenAt.set(id, { kind, path: path42 });
|
|
20594
21054
|
}
|
|
20595
21055
|
};
|
|
20596
21056
|
process2.stages.forEach((stage, stageIndex) => {
|
|
@@ -20628,13 +21088,13 @@ function lintProcess(artifact, inventory, predicateNames) {
|
|
|
20628
21088
|
const internalCommands = process2.internalCommands ?? [];
|
|
20629
21089
|
const internalSet = new Set(internalCommands);
|
|
20630
21090
|
const boundCommandPaths = /* @__PURE__ */ new Map();
|
|
20631
|
-
const checkPredicate = (predicate,
|
|
21091
|
+
const checkPredicate = (predicate, path42) => {
|
|
20632
21092
|
if (!predicateSet.has(predicate)) {
|
|
20633
21093
|
diagnostics.push({
|
|
20634
21094
|
severity: "error",
|
|
20635
21095
|
code: "UNKNOWN_PREDICATE",
|
|
20636
|
-
message: `Predicate "${predicate}" referenced at ${
|
|
20637
|
-
path:
|
|
21096
|
+
message: `Predicate "${predicate}" referenced at ${path42} is not in the predicate registry.`,
|
|
21097
|
+
path: path42
|
|
20638
21098
|
});
|
|
20639
21099
|
}
|
|
20640
21100
|
};
|
|
@@ -20775,8 +21235,8 @@ var init_src3 = __esm({
|
|
|
20775
21235
|
});
|
|
20776
21236
|
|
|
20777
21237
|
// libs/mesh-cli/src/commands/workflow.ts
|
|
20778
|
-
import * as
|
|
20779
|
-
import * as
|
|
21238
|
+
import * as fs34 from "fs";
|
|
21239
|
+
import * as path41 from "path";
|
|
20780
21240
|
import { createRequire as createRequire2 } from "module";
|
|
20781
21241
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
20782
21242
|
function resolveExtractorPath() {
|
|
@@ -20792,7 +21252,7 @@ function runExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
20792
21252
|
let explicitProcessArtifact;
|
|
20793
21253
|
if (explicitProcessPath) {
|
|
20794
21254
|
try {
|
|
20795
|
-
explicitProcessArtifact = JSON.parse(
|
|
21255
|
+
explicitProcessArtifact = JSON.parse(fs34.readFileSync(explicitProcessPath, "utf-8"));
|
|
20796
21256
|
} catch (err) {
|
|
20797
21257
|
logError(
|
|
20798
21258
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -20892,7 +21352,7 @@ function runLintExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
20892
21352
|
let explicitProcessArtifact;
|
|
20893
21353
|
if (explicitProcessPath) {
|
|
20894
21354
|
try {
|
|
20895
|
-
explicitProcessArtifact = JSON.parse(
|
|
21355
|
+
explicitProcessArtifact = JSON.parse(fs34.readFileSync(explicitProcessPath, "utf-8"));
|
|
20896
21356
|
} catch (err) {
|
|
20897
21357
|
logError(
|
|
20898
21358
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -20997,16 +21457,16 @@ function registerWorkflowCommands(program2) {
|
|
|
20997
21457
|
).action(
|
|
20998
21458
|
async (targetPath, opts) => {
|
|
20999
21459
|
try {
|
|
21000
|
-
const resolvedPath =
|
|
21001
|
-
if (!
|
|
21460
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21461
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
21002
21462
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
21003
21463
|
process.exitCode = 1;
|
|
21004
21464
|
return;
|
|
21005
21465
|
}
|
|
21006
21466
|
let resolvedProcessPath;
|
|
21007
21467
|
if (opts.process) {
|
|
21008
|
-
resolvedProcessPath =
|
|
21009
|
-
if (!
|
|
21468
|
+
resolvedProcessPath = path41.resolve(opts.process);
|
|
21469
|
+
if (!fs34.existsSync(resolvedProcessPath)) {
|
|
21010
21470
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
21011
21471
|
process.exitCode = 1;
|
|
21012
21472
|
return;
|
|
@@ -21055,8 +21515,8 @@ function registerWorkflowCommands(program2) {
|
|
|
21055
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."
|
|
21056
21516
|
).action(async (targetPath) => {
|
|
21057
21517
|
try {
|
|
21058
|
-
const resolvedPath =
|
|
21059
|
-
if (!
|
|
21518
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21519
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
21060
21520
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
21061
21521
|
process.exitCode = 1;
|
|
21062
21522
|
return;
|
|
@@ -21099,16 +21559,16 @@ function registerWorkflowCommands(program2) {
|
|
|
21099
21559
|
"Comma-separated named-predicate registry (enables UNKNOWN_PREDICATE checks)"
|
|
21100
21560
|
).action(async (targetPath, opts) => {
|
|
21101
21561
|
try {
|
|
21102
|
-
const resolvedPath =
|
|
21103
|
-
if (!
|
|
21562
|
+
const resolvedPath = path41.resolve(targetPath);
|
|
21563
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
21104
21564
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
21105
21565
|
process.exitCode = 1;
|
|
21106
21566
|
return;
|
|
21107
21567
|
}
|
|
21108
21568
|
let resolvedProcessPath;
|
|
21109
21569
|
if (opts.process) {
|
|
21110
|
-
resolvedProcessPath =
|
|
21111
|
-
if (!
|
|
21570
|
+
resolvedProcessPath = path41.resolve(opts.process);
|
|
21571
|
+
if (!fs34.existsSync(resolvedProcessPath)) {
|
|
21112
21572
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
21113
21573
|
process.exitCode = 1;
|
|
21114
21574
|
return;
|