@mesh-tech/mesh-cli 0.38.1 → 0.39.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.auth-preflight-AQJ5TQES.js → mesh.auth-preflight-JO5G7SOF.js} +1 -1
- package/dist/bin/{mesh.chunk-LMYPTJ2F.js → mesh.chunk-2ZJR5LGN.js} +573 -209
- package/dist/bin/mesh.chunk-2ZJR5LGN.js.map +6 -0
- package/dist/bin/{mesh.chunk-BF2SH6XT.js → mesh.chunk-C2TKCYHP.js} +1 -1
- package/dist/bin/{mesh.chunk-HPYZWPRM.js → mesh.chunk-C4ZM7DM2.js} +1 -1
- package/dist/bin/{mesh.chunk-I6NXLXGC.js → mesh.chunk-CNLSVKYY.js} +17 -15
- package/dist/bin/mesh.chunk-CNLSVKYY.js.map +6 -0
- package/dist/bin/{mesh.chunk-TZAGJTKU.js → mesh.chunk-O6WRZU4X.js} +1 -1
- package/dist/bin/{mesh.chunk-KN4BZOYH.js → mesh.chunk-OSV3EZQ3.js} +1 -1
- package/dist/bin/{mesh.chunk-2YRWJH5X.js → mesh.chunk-UI6EFAMD.js} +4 -4
- package/dist/bin/{mesh.chunk-2YRWJH5X.js.map → mesh.chunk-UI6EFAMD.js.map} +1 -1
- package/dist/bin/{mesh.chunk-IU2IWH6O.js → mesh.chunk-WODQO6TP.js} +3 -3
- package/dist/bin/{mesh.create-app-NGXACFAU.js → mesh.create-app-SOHBL75U.js} +3 -3
- package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js → mesh.dev-token-server-SS7QEQOQ.js} +2 -2
- package/dist/bin/mesh.js +8 -8
- package/dist/bin/{mesh.login-7SIAKFXJ.js → mesh.login-6VPMWHHU.js} +2 -2
- package/dist/bin/{mesh.platform-setup-CJXE3BCP.js → mesh.platform-setup-DJJB2PWL.js} +1 -1
- package/dist/bin/{mesh.program-BIDQBSSI.js → mesh.program-Y4HUNBFP.js} +8 -8
- package/dist/bin/{mesh.registry-7P4MGXEK.js → mesh.registry-5P4ZLFLJ.js} +4 -4
- package/dist/bin/{mesh.registry-identity-JW4KO7N7.js → mesh.registry-identity-ONGUJQAY.js} +3 -3
- package/dist/bin/{mesh.start-MPQFIEMH.js → mesh.start-5CPWOHDJ.js} +2 -2
- package/dist/bin/{mesh.wizard-ZJE4S6PC.js → mesh.wizard-FNRTSGVN.js} +7 -7
- package/dist/build-info.json +2 -2
- package/dist/src/commands/app-check.js +2 -2
- package/dist/src/commands/dev.js +35 -0
- package/dist/src/commands/local/app-authz.d.ts +118 -0
- package/dist/src/commands/local/app-authz.js +295 -0
- package/dist/src/commands/local/auth-provision.d.ts +1 -0
- package/dist/src/commands/local/auth-provision.js +12 -1
- package/dist/src/commands/local/index.js +2 -0
- package/dist/src/commands/temporal.js +16 -1
- package/dist/src/commands/temporal.js.map +2 -2
- package/fragments/base/index.ts.hbs +1 -1
- package/package.json +2 -2
- package/templates/api-auth/api/src/schema.ts.hbs +17 -0
- package/templates/api-auth/package.json.hbs +3 -0
- package/templates/api-role-gating/api/src/schema.ts.hbs +10 -0
- package/dist/bin/mesh.chunk-I6NXLXGC.js.map +0 -6
- package/dist/bin/mesh.chunk-LMYPTJ2F.js.map +0 -6
- /package/dist/bin/{mesh.auth-preflight-AQJ5TQES.js.map → mesh.auth-preflight-JO5G7SOF.js.map} +0 -0
- /package/dist/bin/{mesh.chunk-BF2SH6XT.js.map → mesh.chunk-C2TKCYHP.js.map} +0 -0
- /package/dist/bin/{mesh.chunk-HPYZWPRM.js.map → mesh.chunk-C4ZM7DM2.js.map} +0 -0
- /package/dist/bin/{mesh.chunk-TZAGJTKU.js.map → mesh.chunk-O6WRZU4X.js.map} +0 -0
- /package/dist/bin/{mesh.chunk-KN4BZOYH.js.map → mesh.chunk-OSV3EZQ3.js.map} +0 -0
- /package/dist/bin/{mesh.chunk-IU2IWH6O.js.map → mesh.chunk-WODQO6TP.js.map} +0 -0
- /package/dist/bin/{mesh.create-app-NGXACFAU.js.map → mesh.create-app-SOHBL75U.js.map} +0 -0
- /package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js.map → mesh.dev-token-server-SS7QEQOQ.js.map} +0 -0
- /package/dist/bin/{mesh.login-7SIAKFXJ.js.map → mesh.login-6VPMWHHU.js.map} +0 -0
- /package/dist/bin/{mesh.platform-setup-CJXE3BCP.js.map → mesh.platform-setup-DJJB2PWL.js.map} +0 -0
- /package/dist/bin/{mesh.program-BIDQBSSI.js.map → mesh.program-Y4HUNBFP.js.map} +0 -0
- /package/dist/bin/{mesh.registry-7P4MGXEK.js.map → mesh.registry-5P4ZLFLJ.js.map} +0 -0
- /package/dist/bin/{mesh.registry-identity-JW4KO7N7.js.map → mesh.registry-identity-ONGUJQAY.js.map} +0 -0
- /package/dist/bin/{mesh.start-MPQFIEMH.js.map → mesh.start-5CPWOHDJ.js.map} +0 -0
- /package/dist/bin/{mesh.wizard-ZJE4S6PC.js.map → mesh.wizard-FNRTSGVN.js.map} +0 -0
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "./mesh.chunk-2QIVBQX5.js";
|
|
15
15
|
import {
|
|
16
16
|
ARTIFACTS_BUCKET,
|
|
17
|
+
COMPOSE_PROJECT,
|
|
17
18
|
DEFAULT_HUB_PORT,
|
|
18
19
|
LOCAL_AWS_CONFIG,
|
|
19
20
|
LOCAL_ENV,
|
|
@@ -48,13 +49,13 @@ import {
|
|
|
48
49
|
isExpiredAwsTokenMessage,
|
|
49
50
|
probeRegistryToken,
|
|
50
51
|
registryLoginFix
|
|
51
|
-
} from "./mesh.chunk-
|
|
52
|
+
} from "./mesh.chunk-OSV3EZQ3.js";
|
|
52
53
|
|
|
53
54
|
// packages/mesh-cli/src/commands/login.ts
|
|
54
55
|
import * as http from "http";
|
|
55
56
|
import * as crypto3 from "crypto";
|
|
56
|
-
import * as
|
|
57
|
-
import * as
|
|
57
|
+
import * as fs19 from "fs";
|
|
58
|
+
import * as path20 from "path";
|
|
58
59
|
import { execFileSync as execFileSync15 } from "child_process";
|
|
59
60
|
|
|
60
61
|
// packages/mesh-cli/src/utils/aws-auth.ts
|
|
@@ -1193,10 +1194,10 @@ function unregisterDnsRecords(cfg, suffix) {
|
|
|
1193
1194
|
import * as net7 from "node:net";
|
|
1194
1195
|
|
|
1195
1196
|
// packages/mesh-cli/src/utils/tailscale.ts
|
|
1196
|
-
import * as
|
|
1197
|
+
import * as fs18 from "node:fs";
|
|
1197
1198
|
import * as net6 from "node:net";
|
|
1198
1199
|
import * as os4 from "node:os";
|
|
1199
|
-
import * as
|
|
1200
|
+
import * as path19 from "node:path";
|
|
1200
1201
|
import { spawn as spawn2, execFileSync as execFileSync13 } from "node:child_process";
|
|
1201
1202
|
|
|
1202
1203
|
// packages/mesh-cli/src/utils/pid.ts
|
|
@@ -1236,15 +1237,15 @@ function assertSignalablePid(pid, source) {
|
|
|
1236
1237
|
|
|
1237
1238
|
// packages/mesh-cli/src/utils/tailscale-targets.ts
|
|
1238
1239
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
1239
|
-
import * as
|
|
1240
|
-
import * as
|
|
1240
|
+
import * as fs16 from "node:fs";
|
|
1241
|
+
import * as path17 from "node:path";
|
|
1241
1242
|
|
|
1242
1243
|
// packages/mesh-cli/src/commands/dev.ts
|
|
1243
|
-
import { execFileSync as execFileSync10, spawn, spawnSync } from "child_process";
|
|
1244
|
-
import * as
|
|
1244
|
+
import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync2 } from "child_process";
|
|
1245
|
+
import * as fs15 from "fs";
|
|
1245
1246
|
import * as net3 from "net";
|
|
1246
1247
|
import * as os3 from "os";
|
|
1247
|
-
import * as
|
|
1248
|
+
import * as path16 from "path";
|
|
1248
1249
|
import { Option } from "commander";
|
|
1249
1250
|
import { SecretsManagerClient as SecretsManagerClient2, GetSecretValueCommand as GetSecretValueCommand2 } from "@aws-sdk/client-secrets-manager";
|
|
1250
1251
|
|
|
@@ -3218,6 +3219,10 @@ async function reconcileRegistryFromZitadel() {
|
|
|
3218
3219
|
}
|
|
3219
3220
|
return { tenants, apps };
|
|
3220
3221
|
}
|
|
3222
|
+
function isZitadelNoChanges(err) {
|
|
3223
|
+
if (!(err instanceof Error)) return false;
|
|
3224
|
+
return err.status === 400 && /No changes|COMMAND-1m88i/.test(err.message);
|
|
3225
|
+
}
|
|
3221
3226
|
async function ensureSignInApp(args) {
|
|
3222
3227
|
const pat = readSeederPat();
|
|
3223
3228
|
const orgId = await ensureOrg(pat, args.tenant);
|
|
@@ -3244,13 +3249,17 @@ async function ensureSignInApp(args) {
|
|
|
3244
3249
|
};
|
|
3245
3250
|
const existing = await searchZitadelApp(pat, projectId, name, orgId);
|
|
3246
3251
|
if (existing) {
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3252
|
+
try {
|
|
3253
|
+
await api(
|
|
3254
|
+
pat,
|
|
3255
|
+
"PUT",
|
|
3256
|
+
`/management/v1/projects/${projectId}/apps/${existing.id}/oidc_config`,
|
|
3257
|
+
config,
|
|
3258
|
+
orgId
|
|
3259
|
+
);
|
|
3260
|
+
} catch (err) {
|
|
3261
|
+
if (!isZitadelNoChanges(err)) throw err;
|
|
3262
|
+
}
|
|
3254
3263
|
return;
|
|
3255
3264
|
}
|
|
3256
3265
|
const created = await api(
|
|
@@ -3553,12 +3562,335 @@ async function ensureLocalPlatformRunning() {
|
|
|
3553
3562
|
}
|
|
3554
3563
|
}
|
|
3555
3564
|
|
|
3556
|
-
// packages/mesh-cli/src/commands/local/
|
|
3565
|
+
// packages/mesh-cli/src/commands/local/app-authz.ts
|
|
3566
|
+
import { spawnSync } from "node:child_process";
|
|
3557
3567
|
import { createHash as createHash2 } from "node:crypto";
|
|
3558
|
-
import
|
|
3559
|
-
import
|
|
3560
|
-
import
|
|
3568
|
+
import { createRequire } from "node:module";
|
|
3569
|
+
import fs11 from "node:fs";
|
|
3570
|
+
import path11 from "node:path";
|
|
3561
3571
|
import { pathToFileURL } from "node:url";
|
|
3572
|
+
var LOCAL_APP_SPICEDB_IMAGE = "authzed/spicedb:v1.42.1";
|
|
3573
|
+
var LOCAL_APP_SPICEDB_KEY = "local-dev-key";
|
|
3574
|
+
var LOCAL_APP_SPICEDB_LABEL = "mesh.local.app-spicedb";
|
|
3575
|
+
var LOCAL_STACK_NETWORK = `${COMPOSE_PROJECT}_mesh_network`;
|
|
3576
|
+
var STACK = "local";
|
|
3577
|
+
var GRPC_PORT_BASE = 50100;
|
|
3578
|
+
var HTTP_PORT_BASE = 18100;
|
|
3579
|
+
var PORT_SLOTS = 100;
|
|
3580
|
+
function readLocalAuthzConfig(appRoot) {
|
|
3581
|
+
try {
|
|
3582
|
+
const pkg = JSON.parse(fs11.readFileSync(path11.join(appRoot, "package.json"), "utf-8"));
|
|
3583
|
+
const schema = pkg?.mesh?.authz?.schema;
|
|
3584
|
+
if (typeof schema !== "string" || !schema.trim()) return null;
|
|
3585
|
+
return { schema: schema.trim(), prebuilt: pkg?.mesh?.authz?.metadata !== void 0 };
|
|
3586
|
+
} catch {
|
|
3587
|
+
return null;
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
var slug = (name, sep3) => name.toLowerCase().replace(/[^a-z0-9]+/g, sep3).replace(new RegExp(`^\\${sep3}+|\\${sep3}+$`, "g"), "");
|
|
3591
|
+
function appSpiceDBSlot(app) {
|
|
3592
|
+
return createHash2("sha256").update(app).digest().readUInt32BE(0) % PORT_SLOTS;
|
|
3593
|
+
}
|
|
3594
|
+
function appSpiceDBPorts(slot) {
|
|
3595
|
+
const s = (slot % PORT_SLOTS + PORT_SLOTS) % PORT_SLOTS;
|
|
3596
|
+
return { grpc: GRPC_PORT_BASE + s, http: HTTP_PORT_BASE + s };
|
|
3597
|
+
}
|
|
3598
|
+
var appSpiceDBContainerName = (app) => `mesh-local-spicedb-${slug(app, "-")}`;
|
|
3599
|
+
var appSpiceDBDatabase = (app) => `spicedb_${slug(app, "_")}`.slice(0, 63);
|
|
3600
|
+
var appSpiceDBSecretName = (tenant, env, app) => `mesh/${tenant}/${env}/${app}/spicedb/preshared-key`;
|
|
3601
|
+
var spicedbInstanceParam = (tenant, env, instanceId) => `/mesh-platform/${tenant}/${env}/authz/spicedb-instances/${instanceId}`;
|
|
3602
|
+
var appAuthzParam = (tenant, env, app) => `/mesh-platform/${tenant}/${env}/apps/${app}/stacks/${STACK}/authz`;
|
|
3603
|
+
function instanceRegistryValue(args) {
|
|
3604
|
+
const secret = appSpiceDBSecretName(args.tenant, args.env, args.app);
|
|
3605
|
+
return {
|
|
3606
|
+
grpcEndpoint: `localhost:${args.grpcPort}`,
|
|
3607
|
+
httpEndpoint: `localhost:${args.httpPort}`,
|
|
3608
|
+
namespace: args.app,
|
|
3609
|
+
presharedKeySecretName: secret,
|
|
3610
|
+
configSecretName: secret,
|
|
3611
|
+
owner: { kind: "app", name: args.app }
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
function buildAppAuthzPointer(args) {
|
|
3615
|
+
const existingZitadel = args.existing.zitadel && typeof args.existing.zitadel === "object" ? args.existing.zitadel : {};
|
|
3616
|
+
return {
|
|
3617
|
+
...args.existing,
|
|
3618
|
+
spicedb: {
|
|
3619
|
+
instanceRefs: args.mode === "policy-engine" && args.instanceId ? [{ instanceId: args.instanceId }] : []
|
|
3620
|
+
},
|
|
3621
|
+
...args.zitadel || Object.keys(existingZitadel).length > 0 ? { zitadel: { ...existingZitadel, ...args.zitadel ?? {} } } : {},
|
|
3622
|
+
opsHubMetadataRef: args.metadataRef,
|
|
3623
|
+
mode: args.mode
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3626
|
+
function appSpiceDBEnv(args) {
|
|
3627
|
+
return {
|
|
3628
|
+
SPICEDB_ENDPOINT: `localhost:${args.grpcPort}`,
|
|
3629
|
+
SPICEDB_HTTP_ENDPOINT: `http://localhost:${args.httpPort}`,
|
|
3630
|
+
SPICEDB_PRESHARED_KEY: LOCAL_APP_SPICEDB_KEY,
|
|
3631
|
+
SPICEDB_TOKEN: LOCAL_APP_SPICEDB_KEY,
|
|
3632
|
+
AUTHZ_METADATA_SSM_PATH: args.metadataPath
|
|
3633
|
+
};
|
|
3634
|
+
}
|
|
3635
|
+
var datastoreUri = (app) => `postgres://postgres:postgres@database:5432/${appSpiceDBDatabase(app)}?sslmode=disable`;
|
|
3636
|
+
function spicedbMigrateArgs(app) {
|
|
3637
|
+
return [
|
|
3638
|
+
"run",
|
|
3639
|
+
"--rm",
|
|
3640
|
+
"--network",
|
|
3641
|
+
LOCAL_STACK_NETWORK,
|
|
3642
|
+
"-e",
|
|
3643
|
+
"SPICEDB_DATASTORE_ENGINE=postgres",
|
|
3644
|
+
"-e",
|
|
3645
|
+
`SPICEDB_DATASTORE_CONN_URI=${datastoreUri(app)}`,
|
|
3646
|
+
LOCAL_APP_SPICEDB_IMAGE,
|
|
3647
|
+
"datastore",
|
|
3648
|
+
"migrate",
|
|
3649
|
+
"head"
|
|
3650
|
+
];
|
|
3651
|
+
}
|
|
3652
|
+
function spicedbServeArgs(app, ports) {
|
|
3653
|
+
return [
|
|
3654
|
+
"run",
|
|
3655
|
+
"-d",
|
|
3656
|
+
"--name",
|
|
3657
|
+
appSpiceDBContainerName(app),
|
|
3658
|
+
"--label",
|
|
3659
|
+
`${LOCAL_APP_SPICEDB_LABEL}=${app}`,
|
|
3660
|
+
"--restart",
|
|
3661
|
+
"unless-stopped",
|
|
3662
|
+
"--network",
|
|
3663
|
+
LOCAL_STACK_NETWORK,
|
|
3664
|
+
"-p",
|
|
3665
|
+
`127.0.0.1:${ports.grpc}:50051`,
|
|
3666
|
+
"-p",
|
|
3667
|
+
`127.0.0.1:${ports.http}:8443`,
|
|
3668
|
+
"-e",
|
|
3669
|
+
`SPICEDB_GRPC_PRESHARED_KEY=${LOCAL_APP_SPICEDB_KEY}`,
|
|
3670
|
+
"-e",
|
|
3671
|
+
"SPICEDB_DATASTORE_ENGINE=postgres",
|
|
3672
|
+
"-e",
|
|
3673
|
+
`SPICEDB_DATASTORE_CONN_URI=${datastoreUri(app)}`,
|
|
3674
|
+
"-e",
|
|
3675
|
+
"SPICEDB_HTTP_ENABLED=true",
|
|
3676
|
+
// SpiceDB opens 30 datastore connections by default, and every app's
|
|
3677
|
+
// instance shares the stack's Postgres (100 connections in all): a local
|
|
3678
|
+
// app needs a handful.
|
|
3679
|
+
"-e",
|
|
3680
|
+
"SPICEDB_DATASTORE_CONN_POOL_READ_MAX_OPEN=4",
|
|
3681
|
+
"-e",
|
|
3682
|
+
"SPICEDB_DATASTORE_CONN_POOL_READ_MIN_OPEN=1",
|
|
3683
|
+
"-e",
|
|
3684
|
+
"SPICEDB_DATASTORE_CONN_POOL_WRITE_MAX_OPEN=2",
|
|
3685
|
+
"-e",
|
|
3686
|
+
"SPICEDB_DATASTORE_CONN_POOL_WRITE_MIN_OPEN=1",
|
|
3687
|
+
LOCAL_APP_SPICEDB_IMAGE,
|
|
3688
|
+
"serve"
|
|
3689
|
+
];
|
|
3690
|
+
}
|
|
3691
|
+
function parseDockerPort(output) {
|
|
3692
|
+
const match = output.trim().split("\n")[0]?.match(/:(\d+)\s*$/);
|
|
3693
|
+
return match ? Number(match[1]) : null;
|
|
3694
|
+
}
|
|
3695
|
+
var defaultDocker = (args) => {
|
|
3696
|
+
const r = spawnSync("docker", args, { encoding: "utf-8" });
|
|
3697
|
+
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
3698
|
+
};
|
|
3699
|
+
var mustDocker = (docker2, args, what) => {
|
|
3700
|
+
const r = docker2(args);
|
|
3701
|
+
if (r.status !== 0) {
|
|
3702
|
+
throw new MeshCliError(`${what} failed: ${(r.stderr || r.stdout).trim() || `docker exited ${r.status}`}`);
|
|
3703
|
+
}
|
|
3704
|
+
return r.stdout;
|
|
3705
|
+
};
|
|
3706
|
+
function ensureDatabase(docker2, app) {
|
|
3707
|
+
const container = mustDocker(
|
|
3708
|
+
docker2,
|
|
3709
|
+
[
|
|
3710
|
+
"ps",
|
|
3711
|
+
"-q",
|
|
3712
|
+
"--filter",
|
|
3713
|
+
`label=com.docker.compose.project=${COMPOSE_PROJECT}`,
|
|
3714
|
+
"--filter",
|
|
3715
|
+
"label=com.docker.compose.service=database"
|
|
3716
|
+
],
|
|
3717
|
+
"Finding the local Postgres"
|
|
3718
|
+
).trim().split("\n")[0];
|
|
3719
|
+
if (!container) {
|
|
3720
|
+
throw new MeshCliError("The local platform's Postgres is not running.", {
|
|
3721
|
+
remediation: { command: "mesh start" }
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
const db = appSpiceDBDatabase(app);
|
|
3725
|
+
const exists = mustDocker(
|
|
3726
|
+
docker2,
|
|
3727
|
+
["exec", container, "psql", "-U", "postgres", "-tAc", `SELECT 1 FROM pg_database WHERE datname = '${db}'`],
|
|
3728
|
+
`Checking database ${db}`
|
|
3729
|
+
).trim();
|
|
3730
|
+
if (exists !== "1") {
|
|
3731
|
+
mustDocker(docker2, ["exec", container, "psql", "-U", "postgres", "-c", `CREATE DATABASE "${db}"`], `Creating database ${db}`);
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
async function ensureLocalAppSpiceDB(app, deps = {}) {
|
|
3735
|
+
const docker2 = deps.docker ?? defaultDocker;
|
|
3736
|
+
const portFree = deps.portFree ?? (async (port) => !await probeTcp(port, { timeoutMs: 300 }));
|
|
3737
|
+
const name = appSpiceDBContainerName(app);
|
|
3738
|
+
const state = docker2(["inspect", "-f", "{{.State.Running}}", name]);
|
|
3739
|
+
if (state.status === 0) {
|
|
3740
|
+
if (state.stdout.trim() !== "true") mustDocker(docker2, ["start", name], `Starting ${name}`);
|
|
3741
|
+
const grpcPort = parseDockerPort(mustDocker(docker2, ["port", name, "50051/tcp"], `Reading ${name}'s ports`));
|
|
3742
|
+
const httpPort = parseDockerPort(mustDocker(docker2, ["port", name, "8443/tcp"], `Reading ${name}'s ports`));
|
|
3743
|
+
if (grpcPort && httpPort) return { grpcPort, httpPort, created: false };
|
|
3744
|
+
mustDocker(docker2, ["rm", "-f", name], `Removing ${name}`);
|
|
3745
|
+
}
|
|
3746
|
+
ensureDatabase(docker2, app);
|
|
3747
|
+
mustDocker(docker2, spicedbMigrateArgs(app), `Migrating ${name}'s datastore`);
|
|
3748
|
+
const first = appSpiceDBSlot(app);
|
|
3749
|
+
for (let i = 0; i < PORT_SLOTS; i++) {
|
|
3750
|
+
const ports = appSpiceDBPorts(first + i);
|
|
3751
|
+
if (!await portFree(ports.grpc) || !await portFree(ports.http)) continue;
|
|
3752
|
+
mustDocker(docker2, spicedbServeArgs(app, ports), `Starting ${name}`);
|
|
3753
|
+
return { grpcPort: ports.grpc, httpPort: ports.http, created: true };
|
|
3754
|
+
}
|
|
3755
|
+
throw new MeshCliError(`No free port pair for ${name} in ${GRPC_PORT_BASE}-${GRPC_PORT_BASE + PORT_SLOTS - 1}.`);
|
|
3756
|
+
}
|
|
3757
|
+
async function writeSpiceDBSchema(httpUrl, zed, opts = {}) {
|
|
3758
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
3759
|
+
const attempts = opts.attempts ?? 30;
|
|
3760
|
+
let last = "";
|
|
3761
|
+
for (let i = 0; i < attempts; i++) {
|
|
3762
|
+
try {
|
|
3763
|
+
const res = await fetchImpl(`${httpUrl}/v1/schema/write`, {
|
|
3764
|
+
method: "POST",
|
|
3765
|
+
headers: { Authorization: `Bearer ${LOCAL_APP_SPICEDB_KEY}`, "content-type": "application/json" },
|
|
3766
|
+
body: JSON.stringify({ schema: zed })
|
|
3767
|
+
});
|
|
3768
|
+
if (res.ok) return;
|
|
3769
|
+
last = `${res.status} ${await res.text()}`;
|
|
3770
|
+
if (res.status >= 400 && res.status < 500) break;
|
|
3771
|
+
} catch (err) {
|
|
3772
|
+
last = err instanceof Error ? err.message : String(err);
|
|
3773
|
+
}
|
|
3774
|
+
await new Promise((r) => setTimeout(r, opts.delayMs ?? 500));
|
|
3775
|
+
}
|
|
3776
|
+
throw new MeshCliError(`SpiceDB at ${httpUrl} did not accept the app's schema: ${last}`);
|
|
3777
|
+
}
|
|
3778
|
+
function removeLocalAppSpiceDBs(docker2 = defaultDocker) {
|
|
3779
|
+
const ids = docker2(["ps", "-aq", "--filter", `label=${LOCAL_APP_SPICEDB_LABEL}`]).stdout.trim().split("\n").filter(Boolean);
|
|
3780
|
+
if (ids.length > 0) docker2(["rm", "-f", ...ids]);
|
|
3781
|
+
return ids;
|
|
3782
|
+
}
|
|
3783
|
+
var tsxImporter = async (specifier, parentFile) => {
|
|
3784
|
+
let api2;
|
|
3785
|
+
try {
|
|
3786
|
+
api2 = createRequire(parentFile).resolve("tsx/esm/api");
|
|
3787
|
+
} catch {
|
|
3788
|
+
throw new MeshCliError(
|
|
3789
|
+
`Cannot load the app's authz schema: \`tsx\` is not installed in ${path11.dirname(parentFile)}. Add it to the app's devDependencies (\`pnpm add -D tsx\`).`
|
|
3790
|
+
);
|
|
3791
|
+
}
|
|
3792
|
+
const { tsImport } = await import(pathToFileURL(api2).href);
|
|
3793
|
+
return tsImport(specifier, pathToFileURL(parentFile).href);
|
|
3794
|
+
};
|
|
3795
|
+
var unwrapDefault = (mod) => {
|
|
3796
|
+
const d = mod.default ?? mod.schema;
|
|
3797
|
+
return d && typeof d === "object" && "default" in d && !("namespace" in d) ? d.default : d;
|
|
3798
|
+
};
|
|
3799
|
+
async function loadAppAuthz(appRoot, schemaRel, importer = tsxImporter) {
|
|
3800
|
+
const file = path11.resolve(appRoot, schemaRel);
|
|
3801
|
+
if (!fs11.existsSync(file)) {
|
|
3802
|
+
throw new MeshCliError(`mesh.authz.schema names ${schemaRel}, which does not exist under ${appRoot}.`);
|
|
3803
|
+
}
|
|
3804
|
+
const schema = unwrapDefault(await importer(pathToFileURL(file).href, file));
|
|
3805
|
+
if (!schema || typeof schema.namespace !== "string") {
|
|
3806
|
+
throw new MeshCliError(`${schemaRel} must export the app's defineSchema(...) result as its default export.`);
|
|
3807
|
+
}
|
|
3808
|
+
const compilers = await importer("@mesh-tech/authz/compilers", file);
|
|
3809
|
+
let chunk;
|
|
3810
|
+
try {
|
|
3811
|
+
const ssm = await importer("@mesh-tech/authz/metadata/ssm", file);
|
|
3812
|
+
if (typeof ssm.chunkSsmValue === "function") chunk = ssm.chunkSsmValue;
|
|
3813
|
+
} catch {
|
|
3814
|
+
}
|
|
3815
|
+
return { schema, compilers, chunk };
|
|
3816
|
+
}
|
|
3817
|
+
var localParamStore = async () => {
|
|
3818
|
+
const { SSMClient: SSMClient2, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3819
|
+
const ssm = new SSMClient2(LOCAL_AWS_CONFIG);
|
|
3820
|
+
return {
|
|
3821
|
+
async get(name) {
|
|
3822
|
+
try {
|
|
3823
|
+
return (await ssm.send(new GetParameterCommand2({ Name: name }))).Parameter?.Value;
|
|
3824
|
+
} catch {
|
|
3825
|
+
return void 0;
|
|
3826
|
+
}
|
|
3827
|
+
},
|
|
3828
|
+
async put(name, value) {
|
|
3829
|
+
await ssm.send(new PutParameterCommand({ Name: name, Value: value, Type: "String", Tier: "Advanced", Overwrite: true }));
|
|
3830
|
+
}
|
|
3831
|
+
};
|
|
3832
|
+
};
|
|
3833
|
+
async function publishLocalAppAuthz(args, deps = {}) {
|
|
3834
|
+
const env = LOCAL_ENV;
|
|
3835
|
+
const { schema, compilers, chunk } = await loadAppAuthz(args.appRoot, args.schema, deps.importer);
|
|
3836
|
+
const mode = schema.mode === "role-gating" ? "role-gating" : "policy-engine";
|
|
3837
|
+
const params = deps.params ?? await localParamStore();
|
|
3838
|
+
const base = appAuthzParam(args.tenant, env, args.app);
|
|
3839
|
+
const metadataRef = `${base}/ops-hub-metadata`;
|
|
3840
|
+
let instance;
|
|
3841
|
+
if (mode === "policy-engine") {
|
|
3842
|
+
const running = await (deps.ensureSpiceDB ?? ensureLocalAppSpiceDB)(args.app);
|
|
3843
|
+
instance = { grpcPort: running.grpcPort, httpPort: running.httpPort };
|
|
3844
|
+
await (deps.writeSchema ?? writeSpiceDBSchema)(
|
|
3845
|
+
`http://localhost:${instance.httpPort}`,
|
|
3846
|
+
compilers.compileSpiceDBSchema(schema).zed
|
|
3847
|
+
);
|
|
3848
|
+
await (deps.putSecret ?? upsertLocalSecret)(appSpiceDBSecretName(args.tenant, env, args.app), {
|
|
3849
|
+
PRESHARED_KEY: LOCAL_APP_SPICEDB_KEY
|
|
3850
|
+
});
|
|
3851
|
+
await params.put(
|
|
3852
|
+
spicedbInstanceParam(args.tenant, env, args.app),
|
|
3853
|
+
JSON.stringify(instanceRegistryValue({ app: args.app, tenant: args.tenant, env, ...instance }))
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
const body = JSON.stringify(compilers.compileOpsHubMetadata(schema));
|
|
3857
|
+
const split = chunk ? chunk(body) : { value: body, parts: [] };
|
|
3858
|
+
for (const [i, part] of split.parts.entries()) await params.put(`${metadataRef}/part-${i}`, part);
|
|
3859
|
+
await params.put(metadataRef, split.value);
|
|
3860
|
+
let existing = {};
|
|
3861
|
+
const raw = await params.get(base);
|
|
3862
|
+
if (raw) {
|
|
3863
|
+
try {
|
|
3864
|
+
const parsed = JSON.parse(raw);
|
|
3865
|
+
if (parsed && typeof parsed === "object") existing = parsed;
|
|
3866
|
+
} catch {
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
await params.put(
|
|
3870
|
+
base,
|
|
3871
|
+
JSON.stringify(
|
|
3872
|
+
buildAppAuthzPointer({
|
|
3873
|
+
existing,
|
|
3874
|
+
mode,
|
|
3875
|
+
instanceId: instance ? args.app : void 0,
|
|
3876
|
+
zitadel: args.zitadel,
|
|
3877
|
+
metadataRef
|
|
3878
|
+
})
|
|
3879
|
+
)
|
|
3880
|
+
);
|
|
3881
|
+
return {
|
|
3882
|
+
mode,
|
|
3883
|
+
env: instance ? appSpiceDBEnv({ ...instance, metadataPath: `${base}/metadata` }) : {},
|
|
3884
|
+
...instance ? { instance } : {}
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
// packages/mesh-cli/src/commands/local/publish-docs.ts
|
|
3889
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3890
|
+
import * as fs12 from "node:fs";
|
|
3891
|
+
import * as path12 from "node:path";
|
|
3892
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
3893
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
3562
3894
|
|
|
3563
3895
|
// packages/mesh-cli/src/commands/local/local-spicedb.ts
|
|
3564
3896
|
var LOCAL_SPICEDB_INSTANCE_ID = "mesh-local";
|
|
@@ -3709,14 +4041,14 @@ function zedOf(value) {
|
|
|
3709
4041
|
if (typeof zed !== "string" || !zed.trim()) throw new Error("the spicedbSchema export returned no zed schema");
|
|
3710
4042
|
return zed;
|
|
3711
4043
|
}
|
|
3712
|
-
async function spicedbHttp(
|
|
3713
|
-
const res = await fetch(`${HOST_HTTP}${
|
|
4044
|
+
async function spicedbHttp(path21, body) {
|
|
4045
|
+
const res = await fetch(`${HOST_HTTP}${path21}`, {
|
|
3714
4046
|
method: "POST",
|
|
3715
4047
|
headers: { Authorization: `Bearer ${LOCAL_SPICEDB_KEY}`, "Content-Type": "application/json" },
|
|
3716
4048
|
body: JSON.stringify(body)
|
|
3717
4049
|
});
|
|
3718
4050
|
const parsed = await res.json().catch(() => ({}));
|
|
3719
|
-
if (!res.ok) throw Object.assign(new Error(`SpiceDB ${
|
|
4051
|
+
if (!res.ok) throw Object.assign(new Error(`SpiceDB ${path21}: ${res.status} ${parsed.message ?? ""}`.trim()), { status: res.status });
|
|
3720
4052
|
return parsed;
|
|
3721
4053
|
}
|
|
3722
4054
|
async function readLocalSpicedbSchema() {
|
|
@@ -3853,7 +4185,7 @@ function readLocalPublishDeclaration(pkg, appRoot) {
|
|
|
3853
4185
|
}
|
|
3854
4186
|
if (typeof docs?.config === "string" && docs.config) {
|
|
3855
4187
|
out.docs = {
|
|
3856
|
-
config:
|
|
4188
|
+
config: path12.resolve(appRoot, docs.config),
|
|
3857
4189
|
actAs: docs.actAs === true,
|
|
3858
4190
|
...sandbox?.success ? { sandbox: sandbox.data } : {},
|
|
3859
4191
|
...typeof docs.local === "string" && docs.local.includes("#") ? { local: docs.local } : {},
|
|
@@ -3883,10 +4215,10 @@ function readLocalPublishDeclaration(pkg, appRoot) {
|
|
|
3883
4215
|
}
|
|
3884
4216
|
async function callExport(appRoot, spec, arg) {
|
|
3885
4217
|
const [modulePath, exportName] = spec.split("#");
|
|
3886
|
-
const file =
|
|
4218
|
+
const file = path12.resolve(appRoot, modulePath);
|
|
3887
4219
|
let mod;
|
|
3888
4220
|
if (/\.m?js$/.test(file)) {
|
|
3889
|
-
mod = await import(
|
|
4221
|
+
mod = await import(pathToFileURL2(file).href);
|
|
3890
4222
|
} else {
|
|
3891
4223
|
let tsImport;
|
|
3892
4224
|
try {
|
|
@@ -4062,12 +4394,12 @@ async function publishLocalDocs(input) {
|
|
|
4062
4394
|
...local.events ? { events: local.events } : {}
|
|
4063
4395
|
});
|
|
4064
4396
|
result.problems.push(...built.problems);
|
|
4065
|
-
const config = parseDocsConfig(
|
|
4397
|
+
const config = parseDocsConfig(fs12.readFileSync(docs.config, "utf8"), docs.config);
|
|
4066
4398
|
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
4067
4399
|
const s3 = new S3Client({ ...awsConfig, forcePathStyle: true });
|
|
4068
4400
|
const specKey = `api-docs/${app}/openapi.json`;
|
|
4069
4401
|
const sourceKey = docsSourceKey(app);
|
|
4070
|
-
const specText =
|
|
4402
|
+
const specText = fs12.readFileSync(built.specPath, "utf8");
|
|
4071
4403
|
await s3.send(new PutObjectCommand({ Bucket: ARTIFACTS_BUCKET, Key: specKey, Body: specText, ContentType: "application/json" }));
|
|
4072
4404
|
await s3.send(new PutObjectCommand({ Bucket: ARTIFACTS_BUCKET, Key: sourceKey, Body: built.json, ContentType: "application/json" }));
|
|
4073
4405
|
if (docs.actAs) {
|
|
@@ -4084,7 +4416,7 @@ async function publishLocalDocs(input) {
|
|
|
4084
4416
|
...config.description ? { description: config.description } : {},
|
|
4085
4417
|
docs: {
|
|
4086
4418
|
url: `s3://${ARTIFACTS_BUCKET}/${specKey}`,
|
|
4087
|
-
contentHash:
|
|
4419
|
+
contentHash: createHash3("sha256").update(specText).digest("hex"),
|
|
4088
4420
|
sourceUrl: `s3://${ARTIFACTS_BUCKET}/${sourceKey}`,
|
|
4089
4421
|
sourceContentHash: built.contentHash
|
|
4090
4422
|
},
|
|
@@ -4233,8 +4565,8 @@ async function ensureLocalApiKey(input) {
|
|
|
4233
4565
|
|
|
4234
4566
|
// packages/mesh-cli/src/commands/local/docker-runner.ts
|
|
4235
4567
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
4236
|
-
import * as
|
|
4237
|
-
import * as
|
|
4568
|
+
import * as fs13 from "fs";
|
|
4569
|
+
import * as path13 from "path";
|
|
4238
4570
|
var DEV_RUNNER_IMAGE = "node:22-bookworm@sha256:5647be709086c696ff32edaaf1c70cd26d1da6ab2b39c32f3c7b4c4a31957e37";
|
|
4239
4571
|
function sessionDir(sessionName) {
|
|
4240
4572
|
return meshCacheDir("mesh-local", "dev", sessionName);
|
|
@@ -4257,8 +4589,8 @@ function writeDevCompose(sessionName, appRoot, services) {
|
|
|
4257
4589
|
];
|
|
4258
4590
|
for (const [name, service] of Object.entries(services)) {
|
|
4259
4591
|
const env = service.env ?? {};
|
|
4260
|
-
|
|
4261
|
-
const workdir =
|
|
4592
|
+
fs13.writeFileSync(path13.join(dir, `${name}.env`), renderDockerEnvFile(env), { mode: 384 });
|
|
4593
|
+
const workdir = path13.posix.join("/workspace", service.src === "." ? "" : service.src);
|
|
4262
4594
|
const ship = env.OTEL_RESOURCE_ATTRIBUTES ? ` 2>&1 | NODE_OPTIONS= node /mesh-assets/log-shipper.mjs` : "";
|
|
4263
4595
|
blocks.push(
|
|
4264
4596
|
` ${name}:`,
|
|
@@ -4267,7 +4599,7 @@ function writeDevCompose(sessionName, appRoot, services) {
|
|
|
4267
4599
|
` working_dir: ${workdir}`,
|
|
4268
4600
|
` volumes:`,
|
|
4269
4601
|
` - '${appRoot}:/workspace'`,
|
|
4270
|
-
` - '${
|
|
4602
|
+
` - '${path13.dirname(shipper)}:/mesh-assets:ro'`,
|
|
4271
4603
|
` environment:`,
|
|
4272
4604
|
// node_modules is the HOST's bind-mounted install — pnpm inside the
|
|
4273
4605
|
// container must never "verify" it and try a purge/reinstall (pnpm
|
|
@@ -4283,12 +4615,12 @@ function writeDevCompose(sessionName, appRoot, services) {
|
|
|
4283
4615
|
` restart: unless-stopped`
|
|
4284
4616
|
);
|
|
4285
4617
|
}
|
|
4286
|
-
const composePath =
|
|
4287
|
-
|
|
4618
|
+
const composePath = path13.join(dir, "compose.yml");
|
|
4619
|
+
fs13.writeFileSync(composePath, blocks.join("\n") + "\n");
|
|
4288
4620
|
return composePath;
|
|
4289
4621
|
}
|
|
4290
4622
|
function composeArgs(sessionName, args) {
|
|
4291
|
-
return ["compose", "-p", sessionName, "-f",
|
|
4623
|
+
return ["compose", "-p", sessionName, "-f", path13.join(sessionDir(sessionName), "compose.yml"), ...args];
|
|
4292
4624
|
}
|
|
4293
4625
|
function dockerDevUp(sessionName) {
|
|
4294
4626
|
execFileSync7("docker", composeArgs(sessionName, ["up", "-d"]), {
|
|
@@ -4362,9 +4694,9 @@ function rewriteClusterHostsToLocal(devOutput) {
|
|
|
4362
4694
|
|
|
4363
4695
|
// packages/mesh-cli/src/commands/dev-doctor.ts
|
|
4364
4696
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
4365
|
-
import * as
|
|
4697
|
+
import * as fs14 from "node:fs";
|
|
4366
4698
|
import * as net2 from "node:net";
|
|
4367
|
-
import * as
|
|
4699
|
+
import * as path14 from "node:path";
|
|
4368
4700
|
function aggregateStatus(results) {
|
|
4369
4701
|
if (results.some((r) => r.status === "error")) return "error";
|
|
4370
4702
|
if (results.some((r) => r.status === "warn")) return "warn";
|
|
@@ -4532,9 +4864,9 @@ function parsePortSquatter(lsofOut, psOut) {
|
|
|
4532
4864
|
return { pid, command };
|
|
4533
4865
|
}
|
|
4534
4866
|
function sharesWorktreeRoot(serviceSrc, repoRoot) {
|
|
4535
|
-
const src =
|
|
4536
|
-
const root =
|
|
4537
|
-
return src === root || src.startsWith(root +
|
|
4867
|
+
const src = path14.resolve(serviceSrc);
|
|
4868
|
+
const root = path14.resolve(repoRoot);
|
|
4869
|
+
return src === root || src.startsWith(root + path14.sep);
|
|
4538
4870
|
}
|
|
4539
4871
|
function classifyPortListener(listenerPid, sessionPids) {
|
|
4540
4872
|
if (listenerPid === null) return "dead";
|
|
@@ -4690,14 +5022,14 @@ var configStalenessCheck = {
|
|
|
4690
5022
|
if (!state) {
|
|
4691
5023
|
return { status: "ok", summary: "no session \u2014 config read fresh at launch" };
|
|
4692
5024
|
}
|
|
4693
|
-
const configPath =
|
|
4694
|
-
if (!
|
|
5025
|
+
const configPath = path14.join(ctx.appRoot, `Pulumi.${ctx.stack}.yaml`);
|
|
5026
|
+
if (!fs14.existsSync(configPath)) {
|
|
4695
5027
|
return {
|
|
4696
5028
|
status: "ok",
|
|
4697
5029
|
summary: `no Pulumi.${ctx.stack}.yaml to compare`
|
|
4698
5030
|
};
|
|
4699
5031
|
}
|
|
4700
|
-
const mtimeMs =
|
|
5032
|
+
const mtimeMs = fs14.statSync(configPath).mtimeMs;
|
|
4701
5033
|
if (isConfigStale(mtimeMs, state.startedAt)) {
|
|
4702
5034
|
return {
|
|
4703
5035
|
status: "warn",
|
|
@@ -4888,7 +5220,7 @@ ${ICONS[status]} overall: ${status}`);
|
|
|
4888
5220
|
// packages/mesh-cli/src/utils/worktree-identity.ts
|
|
4889
5221
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
4890
5222
|
import * as crypto from "node:crypto";
|
|
4891
|
-
import * as
|
|
5223
|
+
import * as path15 from "node:path";
|
|
4892
5224
|
var NUM_PORT_BLOCKS = 64;
|
|
4893
5225
|
var PORT_BLOCK_BASE = 4e4;
|
|
4894
5226
|
var PORT_BLOCK_SIZE = 40;
|
|
@@ -4910,7 +5242,7 @@ function blockBasePort(block) {
|
|
|
4910
5242
|
}
|
|
4911
5243
|
function withAppScopedPortBlock(wt, appRoot) {
|
|
4912
5244
|
if (wt.isPrimary) return wt;
|
|
4913
|
-
const appHash = worktreeHash(`${wt.worktreeRoot}#${
|
|
5245
|
+
const appHash = worktreeHash(`${wt.worktreeRoot}#${path15.resolve(appRoot)}`);
|
|
4914
5246
|
return { ...wt, portBlock: portBlockFor(appHash, false) };
|
|
4915
5247
|
}
|
|
4916
5248
|
var STACK_SLUG_MAX = 16;
|
|
@@ -4927,28 +5259,28 @@ function resolveWorktreeIdentity(cwd = process.cwd(), gitRunner = defaultGitRunn
|
|
|
4927
5259
|
let worktreeRoot;
|
|
4928
5260
|
let isPrimary = true;
|
|
4929
5261
|
try {
|
|
4930
|
-
worktreeRoot =
|
|
5262
|
+
worktreeRoot = path15.resolve(gitRunner(["rev-parse", "--show-toplevel"], cwd));
|
|
4931
5263
|
const commonDir = gitRunner(["rev-parse", "--git-common-dir"], cwd);
|
|
4932
|
-
const primaryRoot =
|
|
5264
|
+
const primaryRoot = path15.resolve(path15.dirname(path15.resolve(cwd, commonDir)));
|
|
4933
5265
|
isPrimary = worktreeRoot === primaryRoot;
|
|
4934
5266
|
} catch {
|
|
4935
5267
|
return {
|
|
4936
|
-
worktreeRoot:
|
|
5268
|
+
worktreeRoot: path15.resolve(cwd),
|
|
4937
5269
|
isPrimary: true,
|
|
4938
|
-
slug: sanitizeSlug(
|
|
4939
|
-
hash: worktreeHash(
|
|
5270
|
+
slug: sanitizeSlug(path15.basename(path15.resolve(cwd))),
|
|
5271
|
+
hash: worktreeHash(path15.resolve(cwd)),
|
|
4940
5272
|
token: "",
|
|
4941
5273
|
portBlock: 0,
|
|
4942
5274
|
taskQueueSuffix: ""
|
|
4943
5275
|
};
|
|
4944
5276
|
}
|
|
4945
|
-
const
|
|
5277
|
+
const slug2 = sanitizeSlug(path15.basename(worktreeRoot));
|
|
4946
5278
|
const hash = worktreeHash(worktreeRoot);
|
|
4947
|
-
const token = isPrimary ? "" : `${
|
|
5279
|
+
const token = isPrimary ? "" : `${slug2}-${hash}`;
|
|
4948
5280
|
return {
|
|
4949
5281
|
worktreeRoot,
|
|
4950
5282
|
isPrimary,
|
|
4951
|
-
slug,
|
|
5283
|
+
slug: slug2,
|
|
4952
5284
|
hash,
|
|
4953
5285
|
token,
|
|
4954
5286
|
portBlock: portBlockFor(hash, isPrimary),
|
|
@@ -5002,9 +5334,9 @@ mesh dev plan \u2014 ${kind}
|
|
|
5002
5334
|
console.log(` (none \u2014 all services are deployed/K8s)`);
|
|
5003
5335
|
} else {
|
|
5004
5336
|
const monorepoRoot = findMonorepoRoot();
|
|
5005
|
-
const wtWithSep = wt.worktreeRoot.endsWith(
|
|
5337
|
+
const wtWithSep = wt.worktreeRoot.endsWith(path16.sep) ? wt.worktreeRoot : wt.worktreeRoot + path16.sep;
|
|
5006
5338
|
for (const [name, s] of locals) {
|
|
5007
|
-
const dir =
|
|
5339
|
+
const dir = path16.resolve(appRoot, rebaseServiceSrc(s.src, monorepoRoot));
|
|
5008
5340
|
const outside = dir !== wt.worktreeRoot && !dir.startsWith(wtWithSep);
|
|
5009
5341
|
const port = s.port > 0 ? `:${s.port}` : "(no port)";
|
|
5010
5342
|
console.log(` ${name.padEnd(18)} ${port.padEnd(7)} ${dir}${outside ? " \u26A0 OUTSIDE worktree" : ""}`);
|
|
@@ -5013,18 +5345,18 @@ mesh dev plan \u2014 ${kind}
|
|
|
5013
5345
|
console.log("");
|
|
5014
5346
|
}
|
|
5015
5347
|
function getSessionStatePath(sessionName) {
|
|
5016
|
-
const dir =
|
|
5017
|
-
if (!
|
|
5018
|
-
return
|
|
5348
|
+
const dir = path16.join(os3.tmpdir(), "mesh-dev-sessions");
|
|
5349
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
5350
|
+
return path16.join(dir, `${sessionName}.json`);
|
|
5019
5351
|
}
|
|
5020
5352
|
function saveSessionState(sessionName, state) {
|
|
5021
|
-
|
|
5353
|
+
fs15.writeFileSync(getSessionStatePath(sessionName), JSON.stringify(state, null, 2));
|
|
5022
5354
|
}
|
|
5023
5355
|
function loadSessionState(sessionName) {
|
|
5024
5356
|
const filePath = getSessionStatePath(sessionName);
|
|
5025
|
-
if (!
|
|
5357
|
+
if (!fs15.existsSync(filePath)) return null;
|
|
5026
5358
|
try {
|
|
5027
|
-
return JSON.parse(
|
|
5359
|
+
return JSON.parse(fs15.readFileSync(filePath, "utf-8"));
|
|
5028
5360
|
} catch {
|
|
5029
5361
|
return null;
|
|
5030
5362
|
}
|
|
@@ -5048,18 +5380,18 @@ function workflowChangeWouldStrand(priorState, appRoot, force) {
|
|
|
5048
5380
|
return true;
|
|
5049
5381
|
}
|
|
5050
5382
|
function getSessionEnvDir(sessionName) {
|
|
5051
|
-
return
|
|
5383
|
+
return path16.join(os3.tmpdir(), "mesh-dev-sessions", sessionName);
|
|
5052
5384
|
}
|
|
5053
5385
|
function getServiceEnvFilePath(sessionName, serviceName) {
|
|
5054
|
-
return
|
|
5386
|
+
return path16.join(getSessionEnvDir(sessionName), envFileName(serviceName));
|
|
5055
5387
|
}
|
|
5056
5388
|
function removeSessionState(sessionName) {
|
|
5057
5389
|
try {
|
|
5058
|
-
|
|
5390
|
+
fs15.unlinkSync(getSessionStatePath(sessionName));
|
|
5059
5391
|
} catch {
|
|
5060
5392
|
}
|
|
5061
5393
|
try {
|
|
5062
|
-
|
|
5394
|
+
fs15.rmSync(getSessionEnvDir(sessionName), { recursive: true, force: true });
|
|
5063
5395
|
} catch {
|
|
5064
5396
|
}
|
|
5065
5397
|
}
|
|
@@ -5187,16 +5519,16 @@ function mergePinnedPorts(fresh, pinned) {
|
|
|
5187
5519
|
}
|
|
5188
5520
|
function findMonorepoRoot() {
|
|
5189
5521
|
let dir = process.cwd();
|
|
5190
|
-
const root =
|
|
5522
|
+
const root = path16.parse(dir).root;
|
|
5191
5523
|
while (dir !== root) {
|
|
5192
|
-
if (
|
|
5524
|
+
if (fs15.existsSync(path16.join(dir, "pnpm-workspace.yaml")) || fs15.existsSync(path16.join(dir, "turbo.json"))) {
|
|
5193
5525
|
return dir;
|
|
5194
5526
|
}
|
|
5195
|
-
const gitPath =
|
|
5196
|
-
if (
|
|
5527
|
+
const gitPath = path16.join(dir, ".git");
|
|
5528
|
+
if (fs15.existsSync(gitPath)) {
|
|
5197
5529
|
return dir;
|
|
5198
5530
|
}
|
|
5199
|
-
dir =
|
|
5531
|
+
dir = path16.dirname(dir);
|
|
5200
5532
|
}
|
|
5201
5533
|
return null;
|
|
5202
5534
|
}
|
|
@@ -5204,40 +5536,40 @@ function discoverApps(monorepoRoot) {
|
|
|
5204
5536
|
const apps = [];
|
|
5205
5537
|
function addApp(appDir, tenant) {
|
|
5206
5538
|
const projectName = getProjectName(appDir);
|
|
5207
|
-
const stacks =
|
|
5539
|
+
const stacks = fs15.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
|
|
5208
5540
|
apps.push({
|
|
5209
5541
|
name: projectName,
|
|
5210
5542
|
tenant,
|
|
5211
5543
|
appPath: appDir,
|
|
5212
|
-
relativePath:
|
|
5544
|
+
relativePath: path16.relative(monorepoRoot, appDir),
|
|
5213
5545
|
stacks
|
|
5214
5546
|
});
|
|
5215
5547
|
}
|
|
5216
5548
|
const searchDirs = ["tenants", "tests/tenants"];
|
|
5217
5549
|
for (const searchDir of searchDirs) {
|
|
5218
|
-
const base =
|
|
5219
|
-
if (!
|
|
5220
|
-
for (const tenant of
|
|
5221
|
-
const appsDir =
|
|
5222
|
-
if (!
|
|
5223
|
-
for (const app of
|
|
5224
|
-
const appDir =
|
|
5225
|
-
if (!
|
|
5550
|
+
const base = path16.join(monorepoRoot, searchDir);
|
|
5551
|
+
if (!fs15.existsSync(base)) continue;
|
|
5552
|
+
for (const tenant of fs15.readdirSync(base)) {
|
|
5553
|
+
const appsDir = path16.join(base, tenant, "apps");
|
|
5554
|
+
if (!fs15.existsSync(appsDir) || !fs15.statSync(appsDir).isDirectory()) continue;
|
|
5555
|
+
for (const app of fs15.readdirSync(appsDir)) {
|
|
5556
|
+
const appDir = path16.join(appsDir, app);
|
|
5557
|
+
if (!fs15.existsSync(path16.join(appDir, "Pulumi.yaml"))) continue;
|
|
5226
5558
|
addApp(appDir, tenant);
|
|
5227
5559
|
}
|
|
5228
5560
|
}
|
|
5229
5561
|
}
|
|
5230
|
-
const flatAppsDir =
|
|
5231
|
-
if (
|
|
5232
|
-
for (const app of
|
|
5233
|
-
const appDir =
|
|
5234
|
-
if (!
|
|
5235
|
-
if (!
|
|
5562
|
+
const flatAppsDir = path16.join(monorepoRoot, "apps");
|
|
5563
|
+
if (fs15.existsSync(flatAppsDir) && fs15.statSync(flatAppsDir).isDirectory()) {
|
|
5564
|
+
for (const app of fs15.readdirSync(flatAppsDir)) {
|
|
5565
|
+
const appDir = path16.join(flatAppsDir, app);
|
|
5566
|
+
if (!fs15.statSync(appDir).isDirectory()) continue;
|
|
5567
|
+
if (!fs15.existsSync(path16.join(appDir, "Pulumi.yaml"))) continue;
|
|
5236
5568
|
let tenant = "unknown";
|
|
5237
|
-
const stackConfigs =
|
|
5569
|
+
const stackConfigs = fs15.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
|
|
5238
5570
|
if (stackConfigs.length > 0) {
|
|
5239
5571
|
try {
|
|
5240
|
-
const content =
|
|
5572
|
+
const content = fs15.readFileSync(path16.join(appDir, stackConfigs[0]), "utf-8");
|
|
5241
5573
|
const tenantMatch = content.match(/mesh:tenant:\s*(\S+)/);
|
|
5242
5574
|
if (tenantMatch) tenant = tenantMatch[1];
|
|
5243
5575
|
} catch {
|
|
@@ -5250,32 +5582,32 @@ function discoverApps(monorepoRoot) {
|
|
|
5250
5582
|
}
|
|
5251
5583
|
function findAppRoot2(appPath) {
|
|
5252
5584
|
if (appPath) {
|
|
5253
|
-
const resolved =
|
|
5254
|
-
if (
|
|
5585
|
+
const resolved = path16.resolve(appPath);
|
|
5586
|
+
if (fs15.existsSync(path16.join(resolved, "Pulumi.yaml"))) return resolved;
|
|
5255
5587
|
const mono = findMonorepoRoot();
|
|
5256
5588
|
if (mono) {
|
|
5257
|
-
const fromMono =
|
|
5258
|
-
if (
|
|
5589
|
+
const fromMono = path16.resolve(mono, appPath);
|
|
5590
|
+
if (fs15.existsSync(path16.join(fromMono, "Pulumi.yaml"))) return fromMono;
|
|
5259
5591
|
}
|
|
5260
5592
|
logError(`No Pulumi.yaml found at: ${appPath}`);
|
|
5261
5593
|
process.exit(1);
|
|
5262
5594
|
}
|
|
5263
5595
|
let dir = process.cwd();
|
|
5264
|
-
const root =
|
|
5596
|
+
const root = path16.parse(dir).root;
|
|
5265
5597
|
while (dir !== root) {
|
|
5266
|
-
if (
|
|
5598
|
+
if (fs15.existsSync(path16.join(dir, "Pulumi.yaml"))) {
|
|
5267
5599
|
return dir;
|
|
5268
5600
|
}
|
|
5269
|
-
dir =
|
|
5601
|
+
dir = path16.dirname(dir);
|
|
5270
5602
|
}
|
|
5271
5603
|
return process.cwd();
|
|
5272
5604
|
}
|
|
5273
5605
|
function getProjectName(appRoot) {
|
|
5274
|
-
const yamlPath =
|
|
5275
|
-
if (!
|
|
5276
|
-
const content =
|
|
5606
|
+
const yamlPath = path16.join(appRoot, "Pulumi.yaml");
|
|
5607
|
+
if (!fs15.existsSync(yamlPath)) return path16.basename(appRoot);
|
|
5608
|
+
const content = fs15.readFileSync(yamlPath, "utf-8");
|
|
5277
5609
|
const match = content.match(/^name:\s*(.+)$/m);
|
|
5278
|
-
return match?.[1]?.trim() ??
|
|
5610
|
+
return match?.[1]?.trim() ?? path16.basename(appRoot);
|
|
5279
5611
|
}
|
|
5280
5612
|
function detectStack(appRoot, stageArg) {
|
|
5281
5613
|
if (stageArg) return stageArg;
|
|
@@ -5290,7 +5622,7 @@ function detectStack(appRoot, stageArg) {
|
|
|
5290
5622
|
} catch {
|
|
5291
5623
|
}
|
|
5292
5624
|
try {
|
|
5293
|
-
const files =
|
|
5625
|
+
const files = fs15.readdirSync(appRoot).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
|
|
5294
5626
|
if (files.length === 1) {
|
|
5295
5627
|
const match = files[0].match(/^Pulumi\.(.+)\.yaml$/);
|
|
5296
5628
|
if (match?.[1]) {
|
|
@@ -5411,9 +5743,9 @@ function buildChildAwsEnv(awsEnv, method, profile, opts) {
|
|
|
5411
5743
|
if (method === "zitadel" && opts) {
|
|
5412
5744
|
const sanitizedContext = opts.context.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
5413
5745
|
const profileName = `mesh-dev-${sanitizedContext}`;
|
|
5414
|
-
const configPath =
|
|
5746
|
+
const configPath = path16.join(opts.sessionScratchDir, "aws-config");
|
|
5415
5747
|
const region = awsEnv.AWS_REGION ?? awsEnv.AWS_DEFAULT_REGION ?? "";
|
|
5416
|
-
|
|
5748
|
+
fs15.mkdirSync(opts.sessionScratchDir, { recursive: true });
|
|
5417
5749
|
const profileBlock = renderCredentialProcessProfile({
|
|
5418
5750
|
profileName,
|
|
5419
5751
|
context: opts.context,
|
|
@@ -5421,7 +5753,7 @@ function buildChildAwsEnv(awsEnv, method, profile, opts) {
|
|
|
5421
5753
|
region,
|
|
5422
5754
|
meshBin: opts.meshBin
|
|
5423
5755
|
});
|
|
5424
|
-
const existing =
|
|
5756
|
+
const existing = fs15.existsSync(configPath) ? fs15.readFileSync(configPath, "utf8") : "";
|
|
5425
5757
|
const next = upsertManagedAwsConfigSection(
|
|
5426
5758
|
stripBareProfile(existing, profileName),
|
|
5427
5759
|
`${opts.context} (mesh dev)`,
|
|
@@ -5446,7 +5778,7 @@ function buildChildAwsEnv(awsEnv, method, profile, opts) {
|
|
|
5446
5778
|
function readDotEnvFile(file) {
|
|
5447
5779
|
let text;
|
|
5448
5780
|
try {
|
|
5449
|
-
text =
|
|
5781
|
+
text = fs15.readFileSync(file, "utf-8");
|
|
5450
5782
|
} catch {
|
|
5451
5783
|
return {};
|
|
5452
5784
|
}
|
|
@@ -5479,15 +5811,15 @@ function rebaseServiceSrc(src, monorepoRoot) {
|
|
|
5479
5811
|
if (src === "." || src === "./") return src;
|
|
5480
5812
|
const rel = monorepoRelativeSrc(src);
|
|
5481
5813
|
if (rel === null) return src;
|
|
5482
|
-
const rebased =
|
|
5483
|
-
return
|
|
5814
|
+
const rebased = path16.join(monorepoRoot, rel);
|
|
5815
|
+
return fs15.existsSync(rebased) ? rebased : src;
|
|
5484
5816
|
}
|
|
5485
5817
|
function isLinkedDependencyDir(serviceDir, appRoot) {
|
|
5486
5818
|
let pkgRoot = null;
|
|
5487
5819
|
let name = "";
|
|
5488
|
-
for (let dir = serviceDir; ; dir =
|
|
5820
|
+
for (let dir = serviceDir; ; dir = path16.dirname(dir)) {
|
|
5489
5821
|
try {
|
|
5490
|
-
const manifest = JSON.parse(
|
|
5822
|
+
const manifest = JSON.parse(fs15.readFileSync(path16.join(dir, "package.json"), "utf8"));
|
|
5491
5823
|
if (typeof manifest?.name === "string" && manifest.name) {
|
|
5492
5824
|
pkgRoot = dir;
|
|
5493
5825
|
name = manifest.name;
|
|
@@ -5495,21 +5827,21 @@ function isLinkedDependencyDir(serviceDir, appRoot) {
|
|
|
5495
5827
|
}
|
|
5496
5828
|
} catch {
|
|
5497
5829
|
}
|
|
5498
|
-
const parent =
|
|
5830
|
+
const parent = path16.dirname(dir);
|
|
5499
5831
|
if (parent === dir) break;
|
|
5500
5832
|
}
|
|
5501
5833
|
if (!pkgRoot) return false;
|
|
5502
|
-
for (let dir = appRoot; ; dir =
|
|
5834
|
+
for (let dir = appRoot; ; dir = path16.dirname(dir)) {
|
|
5503
5835
|
try {
|
|
5504
|
-
if (
|
|
5836
|
+
if (fs15.realpathSync(path16.join(dir, "node_modules", name)) === pkgRoot) return true;
|
|
5505
5837
|
} catch {
|
|
5506
5838
|
}
|
|
5507
|
-
const parent =
|
|
5839
|
+
const parent = path16.dirname(dir);
|
|
5508
5840
|
if (parent === dir) return false;
|
|
5509
5841
|
}
|
|
5510
5842
|
}
|
|
5511
5843
|
function monorepoRelativeSrc(src) {
|
|
5512
|
-
if (!
|
|
5844
|
+
if (!path16.isAbsolute(src)) return src.replace(/^\.\//, "") || ".";
|
|
5513
5845
|
const m = src.match(/^.*\/mesh-platform(?:\/(.*))?$/);
|
|
5514
5846
|
if (!m) return null;
|
|
5515
5847
|
const rest = (m[1] ?? "").replace(/^\.worktrees\/[^/]+(?:\/|$)/, "");
|
|
@@ -5621,7 +5953,7 @@ async function resolveTemporalAuthVars(sessionName, devOutput) {
|
|
|
5621
5953
|
setTmuxEnv(sessionName, { KUBECONFIG: kubeconfigPath });
|
|
5622
5954
|
} else if (!process.env.KUBECONFIG) {
|
|
5623
5955
|
const existing = sessionKubeconfigPath(sessionName);
|
|
5624
|
-
if (
|
|
5956
|
+
if (fs15.existsSync(existing)) {
|
|
5625
5957
|
process.env.KUBECONFIG = existing;
|
|
5626
5958
|
}
|
|
5627
5959
|
}
|
|
@@ -5909,7 +6241,7 @@ async function startSsmTunnels(sessionName, devOutput) {
|
|
|
5909
6241
|
return rewritten;
|
|
5910
6242
|
}
|
|
5911
6243
|
async function updateDevboxProxy(devOutput, appRoot) {
|
|
5912
|
-
const appName = devOutput.app ??
|
|
6244
|
+
const appName = devOutput.app ?? path16.basename(appRoot);
|
|
5913
6245
|
const tenant = devOutput.platform?.tenant ?? "mesh";
|
|
5914
6246
|
const env = devOutput.platform?.env ?? "dev";
|
|
5915
6247
|
const stack = devOutput.stack ?? "dev";
|
|
@@ -5987,7 +6319,7 @@ CADDYEOF`
|
|
|
5987
6319
|
async function setupSubdomainRouting(devOutput, appRoot) {
|
|
5988
6320
|
const tsInfo = await getTailscaleInfo();
|
|
5989
6321
|
if (!tsInfo) return null;
|
|
5990
|
-
const appName = devOutput.app ??
|
|
6322
|
+
const appName = devOutput.app ?? path16.basename(appRoot);
|
|
5991
6323
|
const stack = devOutput.stack ?? "dev";
|
|
5992
6324
|
const baseDomain = `${appName}.${stack}.${tsInfo.hostname}.vpn.internal`;
|
|
5993
6325
|
const platform = devOutput.platform ?? { tenant: "mesh", env: "dev" };
|
|
@@ -6006,8 +6338,8 @@ async function setupSubdomainRouting(devOutput, appRoot) {
|
|
|
6006
6338
|
} catch {
|
|
6007
6339
|
}
|
|
6008
6340
|
const caddyDir = "/tmp/mesh-dev-caddy";
|
|
6009
|
-
const caddyfile =
|
|
6010
|
-
if (!
|
|
6341
|
+
const caddyfile = path16.join(caddyDir, "Caddyfile");
|
|
6342
|
+
if (!fs15.existsSync(caddyDir)) fs15.mkdirSync(caddyDir, { recursive: true });
|
|
6011
6343
|
const caddyLines = [`# mesh-dev: ${appName}/${stack} (${tsInfo.hostname})`, ""];
|
|
6012
6344
|
for (const [name, service] of Object.entries(devOutput.services)) {
|
|
6013
6345
|
if (service.port) {
|
|
@@ -6025,7 +6357,7 @@ async function setupSubdomainRouting(devOutput, appRoot) {
|
|
|
6025
6357
|
caddyLines.push("}");
|
|
6026
6358
|
caddyLines.push("");
|
|
6027
6359
|
}
|
|
6028
|
-
|
|
6360
|
+
fs15.writeFileSync(caddyfile, caddyLines.join("\n"));
|
|
6029
6361
|
let caddyRunning = false;
|
|
6030
6362
|
try {
|
|
6031
6363
|
execFileSync10("pgrep", ["-f", "caddy run.*mesh-dev-caddy"], { stdio: "pipe" });
|
|
@@ -6225,9 +6557,9 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
|
|
|
6225
6557
|
logInfo(`Skipped: ${name} (no dev command, deployed to K8s)`);
|
|
6226
6558
|
continue;
|
|
6227
6559
|
}
|
|
6228
|
-
const serviceDir =
|
|
6229
|
-
if (worktreeRoot &&
|
|
6230
|
-
const wtRootWithSep = worktreeRoot.endsWith(
|
|
6560
|
+
const serviceDir = path16.resolve(appRoot, rebaseServiceSrc(service.src, monorepoRoot));
|
|
6561
|
+
if (worktreeRoot && fs15.existsSync(serviceDir)) {
|
|
6562
|
+
const wtRootWithSep = worktreeRoot.endsWith(path16.sep) ? worktreeRoot : worktreeRoot + path16.sep;
|
|
6231
6563
|
const outside = serviceDir !== worktreeRoot && !serviceDir.startsWith(wtRootWithSep);
|
|
6232
6564
|
if (outside && isLinkedDependencyDir(serviceDir, appRoot)) {
|
|
6233
6565
|
logInfo(`${name}: source is a dev-linked package (${serviceDir})`);
|
|
@@ -6238,7 +6570,7 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
|
|
|
6238
6570
|
process.exit(1);
|
|
6239
6571
|
}
|
|
6240
6572
|
}
|
|
6241
|
-
if (!
|
|
6573
|
+
if (!fs15.existsSync(serviceDir)) {
|
|
6242
6574
|
logWarn(
|
|
6243
6575
|
`Skipped: ${name} (source directory not found: ${serviceDir}${serviceDir !== service.src ? ` \u2014 rebased from ${service.src}` : ""})`
|
|
6244
6576
|
);
|
|
@@ -6328,9 +6660,9 @@ async function startServices(sessionName, appRoot, devOutput, headless, awsEnv,
|
|
|
6328
6660
|
if (!headless) {
|
|
6329
6661
|
logInfo("Attaching to tmux session...");
|
|
6330
6662
|
if (process.env.TMUX) {
|
|
6331
|
-
|
|
6663
|
+
spawnSync2("tmux", ["switch-client", "-t", sessionName], { stdio: "inherit" });
|
|
6332
6664
|
} else {
|
|
6333
|
-
|
|
6665
|
+
spawnSync2("tmux", ["attach", "-t", sessionName], { stdio: "inherit" });
|
|
6334
6666
|
}
|
|
6335
6667
|
} else {
|
|
6336
6668
|
console.log(`Attach with: tmux attach -t ${sessionName}`);
|
|
@@ -6481,8 +6813,8 @@ async function restartService(sessionName, serviceName, appRoot, devOutput, awsE
|
|
|
6481
6813
|
const cmd = service.command.join(" ");
|
|
6482
6814
|
const envFilePath = getServiceEnvFilePath(sessionName, serviceName);
|
|
6483
6815
|
let restartCmd;
|
|
6484
|
-
if (
|
|
6485
|
-
const serviceDir =
|
|
6816
|
+
if (fs15.existsSync(envFilePath)) {
|
|
6817
|
+
const serviceDir = path16.resolve(appRoot, rebaseServiceSrc(service.src, findMonorepoRoot()));
|
|
6486
6818
|
restartCmd = buildLaunchCommand(
|
|
6487
6819
|
envFilePath,
|
|
6488
6820
|
serviceDir,
|
|
@@ -6562,7 +6894,7 @@ function registerDevCommand(program) {
|
|
|
6562
6894
|
"tmux"
|
|
6563
6895
|
);
|
|
6564
6896
|
dev.command("__token-server <port> <context>", { hidden: true }).action(async (port, context) => {
|
|
6565
|
-
const { startTokenServer } = await import("./mesh.dev-token-server-
|
|
6897
|
+
const { startTokenServer } = await import("./mesh.dev-token-server-SS7QEQOQ.js");
|
|
6566
6898
|
await startTokenServer(Number(port), context);
|
|
6567
6899
|
});
|
|
6568
6900
|
dev.action(async (options) => {
|
|
@@ -6740,7 +7072,7 @@ function registerDevCommand(program) {
|
|
|
6740
7072
|
signInServices = [];
|
|
6741
7073
|
try {
|
|
6742
7074
|
const appPkg = JSON.parse(
|
|
6743
|
-
|
|
7075
|
+
fs15.readFileSync(path16.join(appRoot, "package.json"), "utf-8")
|
|
6744
7076
|
);
|
|
6745
7077
|
if (Array.isArray(appPkg?.mesh?.auth?.roles)) {
|
|
6746
7078
|
authRoles = appPkg.mesh.auth.roles.filter((r) => typeof r === "string");
|
|
@@ -6761,6 +7093,37 @@ function registerDevCommand(program) {
|
|
|
6761
7093
|
`Auth auto-provisioning skipped: ${err instanceof Error ? err.message : err} \u2014 if the local Zitadel predates seeding, run: mesh stop --destroy && mesh start`
|
|
6762
7094
|
);
|
|
6763
7095
|
}
|
|
7096
|
+
const authzConfig = readLocalAuthzConfig(appRoot);
|
|
7097
|
+
if (authzConfig?.prebuilt) {
|
|
7098
|
+
logWarn(
|
|
7099
|
+
`package.json declares both mesh.authz.schema and mesh.authz.metadata \u2014 both publish this app's authorization. Using mesh.authz.metadata; remove one of them.`
|
|
7100
|
+
);
|
|
7101
|
+
} else if (authzConfig) {
|
|
7102
|
+
const app = rawDevOutput2.app ?? projectName;
|
|
7103
|
+
try {
|
|
7104
|
+
const published = await publishLocalAppAuthz({
|
|
7105
|
+
appRoot,
|
|
7106
|
+
schema: authzConfig.schema,
|
|
7107
|
+
tenant: localTenant,
|
|
7108
|
+
app,
|
|
7109
|
+
...provisioned ? { zitadel: { projectId: provisioned.projectId, orgId: provisioned.orgId } } : {}
|
|
7110
|
+
});
|
|
7111
|
+
for (const [name, svc] of Object.entries(rawDevOutput2.services)) {
|
|
7112
|
+
if (name.startsWith("mock-")) continue;
|
|
7113
|
+
svc.env = {
|
|
7114
|
+
...svc.env ?? {},
|
|
7115
|
+
...Object.fromEntries(Object.entries(published.env).map(([k, value]) => [k, { value }]))
|
|
7116
|
+
};
|
|
7117
|
+
}
|
|
7118
|
+
logSuccess(
|
|
7119
|
+
published.instance ? `Authz (${published.mode}) published for '${app}' \u2014 its SpiceDB on localhost:${published.instance.grpcPort} (HTTP ${published.instance.httpPort})` : `Authz (${published.mode}) published for '${app}'`
|
|
7120
|
+
);
|
|
7121
|
+
} catch (err) {
|
|
7122
|
+
logWarn(
|
|
7123
|
+
`Could not publish '${app}'s authorization (${err instanceof Error ? err.message : err}) \u2014 the Hub cannot author its roles, and a policy-engine app denies every check.`
|
|
7124
|
+
);
|
|
7125
|
+
}
|
|
7126
|
+
}
|
|
6764
7127
|
const devOutput2 = await allocatePorts(rawDevOutput2, worktree);
|
|
6765
7128
|
for (const service of signInServices) {
|
|
6766
7129
|
const port = devOutput2.services[service]?.port;
|
|
@@ -6805,7 +7168,7 @@ function registerDevCommand(program) {
|
|
|
6805
7168
|
}
|
|
6806
7169
|
let localDocs;
|
|
6807
7170
|
try {
|
|
6808
|
-
const appPkg = JSON.parse(
|
|
7171
|
+
const appPkg = JSON.parse(fs15.readFileSync(path16.join(appRoot, "package.json"), "utf-8"));
|
|
6809
7172
|
const declaration = readLocalPublishDeclaration(appPkg, appRoot);
|
|
6810
7173
|
if (declaration.docs || declaration.authzMetadata || declaration.problems.length > 0) {
|
|
6811
7174
|
const app = devOutput2.app ?? projectName;
|
|
@@ -6815,10 +7178,10 @@ function registerDevCommand(program) {
|
|
|
6815
7178
|
if (!httpService || !service?.port) {
|
|
6816
7179
|
logWarn(`mesh.docs names service '${httpService ?? ""}', which this app does not run \u2014 docs not published.`);
|
|
6817
7180
|
} else {
|
|
6818
|
-
const serviceDir =
|
|
7181
|
+
const serviceDir = path16.resolve(appRoot, service.src);
|
|
6819
7182
|
const env = service.env ?? (service.env = {});
|
|
6820
7183
|
const serviceEnv = {
|
|
6821
|
-
...readDotEnvFile(
|
|
7184
|
+
...readDotEnvFile(path16.join(serviceDir, ".env")),
|
|
6822
7185
|
...Object.fromEntries(Object.entries(process.env).filter((e) => typeof e[1] === "string")),
|
|
6823
7186
|
...Object.fromEntries(Object.entries(env).map(([k, v]) => [k, v.value]))
|
|
6824
7187
|
};
|
|
@@ -7007,9 +7370,9 @@ function registerDevCommand(program) {
|
|
|
7007
7370
|
logInfo(`Project: ${projectName}, Stack: ${stack}`);
|
|
7008
7371
|
const monorepoRoot = findMonorepoRoot();
|
|
7009
7372
|
const looksLikeStandaloneApp = ["api", "worker", "web"].some(
|
|
7010
|
-
(dir) =>
|
|
7373
|
+
(dir) => fs15.existsSync(path16.join(appRoot, dir, "package.json"))
|
|
7011
7374
|
);
|
|
7012
|
-
if (monorepoRoot &&
|
|
7375
|
+
if (monorepoRoot && path16.resolve(appRoot) === path16.resolve(monorepoRoot) && !looksLikeStandaloneApp) {
|
|
7013
7376
|
logError(`mesh dev should be run from an app directory, not the monorepo root.
|
|
7014
7377
|
`);
|
|
7015
7378
|
logInfo("Try:");
|
|
@@ -7441,17 +7804,17 @@ function registerDevCommand(program) {
|
|
|
7441
7804
|
// packages/mesh-cli/src/utils/tailscale-targets.ts
|
|
7442
7805
|
var PORT_BLOCK = 3e3;
|
|
7443
7806
|
function tenantPortIndex(tenant, registryPath) {
|
|
7444
|
-
const p = registryPath ??
|
|
7807
|
+
const p = registryPath ?? path17.join(CONFIG_DIR, "tailscale", "port-registry.json");
|
|
7445
7808
|
let reg;
|
|
7446
7809
|
try {
|
|
7447
|
-
reg = JSON.parse(
|
|
7810
|
+
reg = JSON.parse(fs16.readFileSync(p, "utf8"));
|
|
7448
7811
|
} catch {
|
|
7449
7812
|
reg = { mesh: 0 };
|
|
7450
7813
|
}
|
|
7451
7814
|
if (tenant in reg) return reg[tenant];
|
|
7452
7815
|
const idx = Math.max(-1, ...Object.values(reg)) + 1;
|
|
7453
7816
|
reg[tenant] = idx;
|
|
7454
|
-
|
|
7817
|
+
fs16.mkdirSync(path17.dirname(p), { recursive: true });
|
|
7455
7818
|
atomicWriteFileSync(p, JSON.stringify(reg, null, 2), 384);
|
|
7456
7819
|
return idx;
|
|
7457
7820
|
}
|
|
@@ -7563,29 +7926,29 @@ function createForwarder(opts) {
|
|
|
7563
7926
|
|
|
7564
7927
|
// packages/mesh-cli/src/utils/tunnel-ownership.ts
|
|
7565
7928
|
import * as crypto2 from "node:crypto";
|
|
7566
|
-
import * as
|
|
7929
|
+
import * as fs17 from "node:fs";
|
|
7567
7930
|
import * as net5 from "node:net";
|
|
7568
|
-
import * as
|
|
7931
|
+
import * as path18 from "node:path";
|
|
7569
7932
|
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
7570
7933
|
function mintRunnerToken() {
|
|
7571
7934
|
return crypto2.randomBytes(16).toString("hex");
|
|
7572
7935
|
}
|
|
7573
7936
|
function readRunnerManifest(file) {
|
|
7574
|
-
if (!
|
|
7937
|
+
if (!fs17.existsSync(file)) return null;
|
|
7575
7938
|
try {
|
|
7576
|
-
return JSON.parse(
|
|
7939
|
+
return JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
7577
7940
|
} catch {
|
|
7578
7941
|
return null;
|
|
7579
7942
|
}
|
|
7580
7943
|
}
|
|
7581
7944
|
function writeRunnerManifest(file, manifest) {
|
|
7582
|
-
|
|
7583
|
-
|
|
7945
|
+
fs17.mkdirSync(path18.dirname(file), { recursive: true });
|
|
7946
|
+
fs17.writeFileSync(file, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
7584
7947
|
}
|
|
7585
7948
|
function clearRunnerManifest(file, token) {
|
|
7586
7949
|
if (token !== void 0 && readRunnerManifest(file)?.token !== token) return;
|
|
7587
7950
|
try {
|
|
7588
|
-
|
|
7951
|
+
fs17.unlinkSync(file);
|
|
7589
7952
|
} catch {
|
|
7590
7953
|
}
|
|
7591
7954
|
}
|
|
@@ -7751,58 +8114,58 @@ function buildSquatterError(tenant, squatted, detail) {
|
|
|
7751
8114
|
// packages/mesh-cli/src/utils/tailscale.ts
|
|
7752
8115
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7753
8116
|
function tenantStateDir(tenant) {
|
|
7754
|
-
return
|
|
8117
|
+
return path19.join(CONFIG_DIR, "tailscale", tenant);
|
|
7755
8118
|
}
|
|
7756
8119
|
function socketPath(tenant) {
|
|
7757
|
-
return
|
|
8120
|
+
return path19.join(tenantStateDir(tenant), "tailscaled.sock");
|
|
7758
8121
|
}
|
|
7759
8122
|
function logPath(tenant) {
|
|
7760
|
-
return
|
|
8123
|
+
return path19.join(tenantStateDir(tenant), "tailscaled.log");
|
|
7761
8124
|
}
|
|
7762
8125
|
function statePath(tenant) {
|
|
7763
|
-
return
|
|
8126
|
+
return path19.join(tenantStateDir(tenant), "tunnel-state.json");
|
|
7764
8127
|
}
|
|
7765
8128
|
function daemonMetaPath(tenant) {
|
|
7766
|
-
return
|
|
8129
|
+
return path19.join(tenantStateDir(tenant), "daemon.json");
|
|
7767
8130
|
}
|
|
7768
8131
|
function runnerManifestPath(tenant) {
|
|
7769
|
-
return
|
|
8132
|
+
return path19.join(tenantStateDir(tenant), "runner.json");
|
|
7770
8133
|
}
|
|
7771
8134
|
function readTunnelState(tenant) {
|
|
7772
8135
|
const p = statePath(tenant);
|
|
7773
|
-
if (!
|
|
8136
|
+
if (!fs18.existsSync(p)) return null;
|
|
7774
8137
|
try {
|
|
7775
|
-
return JSON.parse(
|
|
8138
|
+
return JSON.parse(fs18.readFileSync(p, "utf8"));
|
|
7776
8139
|
} catch {
|
|
7777
8140
|
return null;
|
|
7778
8141
|
}
|
|
7779
8142
|
}
|
|
7780
8143
|
function writeTunnelState(tenant, state) {
|
|
7781
|
-
|
|
8144
|
+
fs18.mkdirSync(tenantStateDir(tenant), { recursive: true });
|
|
7782
8145
|
atomicWriteFileSync(statePath(tenant), JSON.stringify(state, null, 2), 384);
|
|
7783
8146
|
}
|
|
7784
8147
|
function clearTunnelState(tenant) {
|
|
7785
8148
|
try {
|
|
7786
|
-
|
|
8149
|
+
fs18.unlinkSync(statePath(tenant));
|
|
7787
8150
|
} catch {
|
|
7788
8151
|
}
|
|
7789
8152
|
}
|
|
7790
8153
|
function readDaemonMeta(tenant) {
|
|
7791
8154
|
const p = daemonMetaPath(tenant);
|
|
7792
|
-
if (!
|
|
8155
|
+
if (!fs18.existsSync(p)) return null;
|
|
7793
8156
|
try {
|
|
7794
|
-
return JSON.parse(
|
|
8157
|
+
return JSON.parse(fs18.readFileSync(p, "utf8"));
|
|
7795
8158
|
} catch {
|
|
7796
8159
|
return null;
|
|
7797
8160
|
}
|
|
7798
8161
|
}
|
|
7799
8162
|
function writeDaemonMeta(tenant, meta) {
|
|
7800
|
-
|
|
8163
|
+
fs18.mkdirSync(tenantStateDir(tenant), { recursive: true });
|
|
7801
8164
|
atomicWriteFileSync(daemonMetaPath(tenant), JSON.stringify(meta, null, 2), 384);
|
|
7802
8165
|
}
|
|
7803
8166
|
function clearDaemonMeta(tenant) {
|
|
7804
8167
|
try {
|
|
7805
|
-
|
|
8168
|
+
fs18.unlinkSync(daemonMetaPath(tenant));
|
|
7806
8169
|
} catch {
|
|
7807
8170
|
}
|
|
7808
8171
|
}
|
|
@@ -7822,7 +8185,7 @@ function tailscaleBinPath() {
|
|
|
7822
8185
|
}
|
|
7823
8186
|
function resolveBrewBin(name) {
|
|
7824
8187
|
const brew = `/opt/homebrew/bin/${name}`;
|
|
7825
|
-
if (
|
|
8188
|
+
if (fs18.existsSync(brew)) return brew;
|
|
7826
8189
|
try {
|
|
7827
8190
|
const p = execFileSync13("which", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
7828
8191
|
if (p) return p;
|
|
@@ -7831,7 +8194,7 @@ function resolveBrewBin(name) {
|
|
|
7831
8194
|
throw new Error(`${name} not found \u2014 install the standalone binary: brew install tailscale`);
|
|
7832
8195
|
}
|
|
7833
8196
|
function daemonState(tenant) {
|
|
7834
|
-
if (!
|
|
8197
|
+
if (!fs18.existsSync(socketPath(tenant))) return { backendState: "Down" };
|
|
7835
8198
|
try {
|
|
7836
8199
|
const out = execFileSync13(
|
|
7837
8200
|
tailscaleBinPath(),
|
|
@@ -7905,8 +8268,8 @@ async function ensureDaemon(tenant, opts) {
|
|
|
7905
8268
|
}
|
|
7906
8269
|
}
|
|
7907
8270
|
const dir = tenantStateDir(tenant);
|
|
7908
|
-
|
|
7909
|
-
const log =
|
|
8271
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
8272
|
+
const log = fs18.openSync(logPath(tenant), "a");
|
|
7910
8273
|
try {
|
|
7911
8274
|
const child = spawn2(
|
|
7912
8275
|
tailscaledBinPath(),
|
|
@@ -7926,7 +8289,7 @@ async function ensureDaemon(tenant, opts) {
|
|
|
7926
8289
|
child.unref();
|
|
7927
8290
|
if (child.pid) writeDaemonMeta(tenant, { socksPort: opts.socksPort, pid: child.pid });
|
|
7928
8291
|
} finally {
|
|
7929
|
-
|
|
8292
|
+
fs18.closeSync(log);
|
|
7930
8293
|
}
|
|
7931
8294
|
for (let i = 0; i < 40; i++) {
|
|
7932
8295
|
await sleep(250);
|
|
@@ -7952,7 +8315,7 @@ function stopDaemon(tenant) {
|
|
|
7952
8315
|
}
|
|
7953
8316
|
for (const p of [socketPath(tenant)]) {
|
|
7954
8317
|
try {
|
|
7955
|
-
|
|
8318
|
+
fs18.unlinkSync(p);
|
|
7956
8319
|
} catch {
|
|
7957
8320
|
}
|
|
7958
8321
|
}
|
|
@@ -8097,7 +8460,7 @@ async function assertTunnelPortsFree(tenant, ports) {
|
|
|
8097
8460
|
async function ensureForwarders(state) {
|
|
8098
8461
|
const prior = readTunnelState(state.tenant);
|
|
8099
8462
|
const expected = { socksPort: state.socksPort, ports: state.tunnels.map((t) => t.localPort) };
|
|
8100
|
-
const runnerAlive = prior?.managed === "launchd" ?
|
|
8463
|
+
const runnerAlive = prior?.managed === "launchd" ? fs18.existsSync(launchAgentPath(state.tenant)) : pidAlive(prior?.supervisorPid);
|
|
8101
8464
|
if (prior && runnerAlive && tunnelTargetsEqual(prior.tunnels, state.tunnels) && // A prior supervisor spawned for a DIFFERENT SOCKS port still binds the same
|
|
8102
8465
|
// local ports but dials a dead upstream — reusing it silently strands every
|
|
8103
8466
|
// tunnel. The port must match, and the runner must PROVE it is
|
|
@@ -8122,7 +8485,7 @@ async function ensureForwarders(state) {
|
|
|
8122
8485
|
if (launchdAvailable()) {
|
|
8123
8486
|
state.managed = "launchd";
|
|
8124
8487
|
writeTunnelState(state.tenant, state);
|
|
8125
|
-
if (
|
|
8488
|
+
if (path19.isAbsolute(meshBin) && installLaunchAgent(state.tenant, meshBin)) {
|
|
8126
8489
|
if ((await awaitOwnership(state.tenant, expected, 8e3)).ok) return 0;
|
|
8127
8490
|
uninstallLaunchAgent(state.tenant);
|
|
8128
8491
|
await assertTunnelPortsFree(state.tenant, expected.ports);
|
|
@@ -8250,7 +8613,7 @@ function forwardersNeedingRebind(targets, listening) {
|
|
|
8250
8613
|
return targets.filter((t) => !listening.has(t.localPort));
|
|
8251
8614
|
}
|
|
8252
8615
|
function launchAgentPath(tenant) {
|
|
8253
|
-
return
|
|
8616
|
+
return path19.join(os4.homedir(), "Library", "LaunchAgents", `${launchAgentLabel(tenant)}.plist`);
|
|
8254
8617
|
}
|
|
8255
8618
|
function guiDomain() {
|
|
8256
8619
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -8272,22 +8635,22 @@ function installLaunchAgent(tenant, meshBin) {
|
|
|
8272
8635
|
try {
|
|
8273
8636
|
let realMeshBin = meshBin;
|
|
8274
8637
|
try {
|
|
8275
|
-
realMeshBin =
|
|
8638
|
+
realMeshBin = fs18.realpathSync(meshBin);
|
|
8276
8639
|
} catch {
|
|
8277
8640
|
}
|
|
8278
8641
|
let pathEnv = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
8279
8642
|
try {
|
|
8280
|
-
pathEnv = `${
|
|
8643
|
+
pathEnv = `${path19.dirname(tailscaledBinPath())}:${pathEnv}`;
|
|
8281
8644
|
} catch {
|
|
8282
8645
|
}
|
|
8283
|
-
|
|
8284
|
-
|
|
8646
|
+
fs18.mkdirSync(path19.dirname(plistPath), { recursive: true });
|
|
8647
|
+
fs18.writeFileSync(
|
|
8285
8648
|
plistPath,
|
|
8286
8649
|
renderLaunchAgentPlist({
|
|
8287
8650
|
tenant,
|
|
8288
8651
|
meshBin: realMeshBin,
|
|
8289
8652
|
nodeBin: process.execPath,
|
|
8290
|
-
logPath:
|
|
8653
|
+
logPath: path19.join(tenantStateDir(tenant), "runner.log"),
|
|
8291
8654
|
pathEnv
|
|
8292
8655
|
})
|
|
8293
8656
|
);
|
|
@@ -8308,7 +8671,7 @@ function uninstallLaunchAgent(tenant) {
|
|
|
8308
8671
|
} catch {
|
|
8309
8672
|
}
|
|
8310
8673
|
try {
|
|
8311
|
-
|
|
8674
|
+
fs18.unlinkSync(plistPath);
|
|
8312
8675
|
} catch {
|
|
8313
8676
|
}
|
|
8314
8677
|
}
|
|
@@ -8948,12 +9311,12 @@ function registerVpnCommands(program) {
|
|
|
8948
9311
|
}
|
|
8949
9312
|
|
|
8950
9313
|
// packages/mesh-cli/src/commands/login.ts
|
|
8951
|
-
var CONFIG_DIR =
|
|
8952
|
-
process.env.XDG_CONFIG_HOME ??
|
|
9314
|
+
var CONFIG_DIR = path20.join(
|
|
9315
|
+
process.env.XDG_CONFIG_HOME ?? path20.join(process.env.HOME ?? "~", ".config"),
|
|
8953
9316
|
"mesh"
|
|
8954
9317
|
);
|
|
8955
|
-
var CONFIG_FILE =
|
|
8956
|
-
var CREDENTIALS_FILE =
|
|
9318
|
+
var CONFIG_FILE = path20.join(CONFIG_DIR, "config.json");
|
|
9319
|
+
var CREDENTIALS_FILE = path20.join(CONFIG_DIR, "credentials.json");
|
|
8957
9320
|
var REDIRECT_PORT = 9876;
|
|
8958
9321
|
var REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
|
|
8959
9322
|
var BASE_SCOPES = "openid email profile offline_access urn:zitadel:iam:org:project:id:zitadel:aud";
|
|
@@ -8969,9 +9332,9 @@ function withDiscoveredFields(config, data) {
|
|
|
8969
9332
|
return out;
|
|
8970
9333
|
}
|
|
8971
9334
|
function readConfig() {
|
|
8972
|
-
if (!
|
|
9335
|
+
if (!fs19.existsSync(CONFIG_FILE)) return {};
|
|
8973
9336
|
try {
|
|
8974
|
-
return JSON.parse(
|
|
9337
|
+
return JSON.parse(fs19.readFileSync(CONFIG_FILE, "utf-8"));
|
|
8975
9338
|
} catch {
|
|
8976
9339
|
return {};
|
|
8977
9340
|
}
|
|
@@ -8979,15 +9342,15 @@ function readConfig() {
|
|
|
8979
9342
|
function writeContextConfig(context, config) {
|
|
8980
9343
|
const existing = readConfig();
|
|
8981
9344
|
existing[context] = config;
|
|
8982
|
-
|
|
8983
|
-
|
|
9345
|
+
fs19.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
9346
|
+
fs19.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
8984
9347
|
}
|
|
8985
9348
|
function clearContextConfig(context) {
|
|
8986
9349
|
const existing = readConfig();
|
|
8987
9350
|
if (!(context in existing)) return;
|
|
8988
9351
|
delete existing[context];
|
|
8989
|
-
|
|
8990
|
-
|
|
9352
|
+
fs19.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
9353
|
+
fs19.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
8991
9354
|
}
|
|
8992
9355
|
function getContextConfig(context) {
|
|
8993
9356
|
const config = readConfig();
|
|
@@ -9094,9 +9457,9 @@ function discoverConfigFromWellKnown(domain, contextKey = domain, opts = {}) {
|
|
|
9094
9457
|
return discoverConfig(domain, contextKey, opts);
|
|
9095
9458
|
}
|
|
9096
9459
|
function readAllCredentials() {
|
|
9097
|
-
if (!
|
|
9460
|
+
if (!fs19.existsSync(CREDENTIALS_FILE)) return {};
|
|
9098
9461
|
try {
|
|
9099
|
-
return JSON.parse(
|
|
9462
|
+
return JSON.parse(fs19.readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
9100
9463
|
} catch {
|
|
9101
9464
|
return {};
|
|
9102
9465
|
}
|
|
@@ -9105,21 +9468,21 @@ function readCredentials(context) {
|
|
|
9105
9468
|
return readAllCredentials()[context] ?? null;
|
|
9106
9469
|
}
|
|
9107
9470
|
var atomicWriteCounter = 0;
|
|
9108
|
-
function atomicWriteFileSync(
|
|
9109
|
-
const tmpPath = `${
|
|
9471
|
+
function atomicWriteFileSync(path21, data, mode) {
|
|
9472
|
+
const tmpPath = `${path21}.${process.pid}.${atomicWriteCounter++}.tmp`;
|
|
9110
9473
|
try {
|
|
9111
|
-
|
|
9112
|
-
|
|
9474
|
+
fs19.writeFileSync(tmpPath, data, { mode });
|
|
9475
|
+
fs19.renameSync(tmpPath, path21);
|
|
9113
9476
|
} catch (err) {
|
|
9114
9477
|
try {
|
|
9115
|
-
|
|
9478
|
+
fs19.unlinkSync(tmpPath);
|
|
9116
9479
|
} catch {
|
|
9117
9480
|
}
|
|
9118
9481
|
throw err;
|
|
9119
9482
|
}
|
|
9120
9483
|
}
|
|
9121
9484
|
function writeCredentials(context, creds) {
|
|
9122
|
-
|
|
9485
|
+
fs19.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
9123
9486
|
const all = readAllCredentials();
|
|
9124
9487
|
all[context] = creds;
|
|
9125
9488
|
atomicWriteFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), 384);
|
|
@@ -9127,8 +9490,8 @@ function writeCredentials(context, creds) {
|
|
|
9127
9490
|
function clearCredentials(context) {
|
|
9128
9491
|
const all = readAllCredentials();
|
|
9129
9492
|
delete all[context];
|
|
9130
|
-
|
|
9131
|
-
|
|
9493
|
+
fs19.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
9494
|
+
fs19.writeFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), {
|
|
9132
9495
|
mode: 384
|
|
9133
9496
|
});
|
|
9134
9497
|
}
|
|
@@ -9398,7 +9761,7 @@ function loginTimeoutMessage(context) {
|
|
|
9398
9761
|
}
|
|
9399
9762
|
function isRemoteEnvironment() {
|
|
9400
9763
|
if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES) return true;
|
|
9401
|
-
if (
|
|
9764
|
+
if (fs19.existsSync("/.dockerenv")) return true;
|
|
9402
9765
|
if (process.env.SSH_CLIENT || process.env.SSH_TTY) return true;
|
|
9403
9766
|
if (!process.stdout.isTTY || !process.stdin.isTTY) return true;
|
|
9404
9767
|
return false;
|
|
@@ -9652,9 +10015,9 @@ function refuseReservedRegistryContext(context, verb) {
|
|
|
9652
10015
|
}
|
|
9653
10016
|
async function hintRegistryIfMissing() {
|
|
9654
10017
|
try {
|
|
9655
|
-
const { readRegistrySession } = await import("./mesh.registry-identity-
|
|
10018
|
+
const { readRegistrySession } = await import("./mesh.registry-identity-ONGUJQAY.js");
|
|
9656
10019
|
if (readRegistrySession()) return;
|
|
9657
|
-
const { probeRegistryToken: probeRegistryToken2 } = await import("./mesh.auth-preflight-
|
|
10020
|
+
const { probeRegistryToken: probeRegistryToken2 } = await import("./mesh.auth-preflight-JO5G7SOF.js");
|
|
9658
10021
|
const probe = await probeRegistryToken2();
|
|
9659
10022
|
if (probe.state === "missing" || probe.state === "expired") {
|
|
9660
10023
|
logInfo(REGISTRY_SEPARATE_HINT);
|
|
@@ -9786,12 +10149,12 @@ export AWS_REGION=${shellSingleQuote(args.region)}
|
|
|
9786
10149
|
`;
|
|
9787
10150
|
}
|
|
9788
10151
|
function resolveUserAwsConfigPath(env = process.env) {
|
|
9789
|
-
return env.AWS_CONFIG_FILE ??
|
|
10152
|
+
return env.AWS_CONFIG_FILE ?? path20.join(env.HOME ?? "~", ".aws", "config");
|
|
9790
10153
|
}
|
|
9791
10154
|
function canWriteDir(dir) {
|
|
9792
10155
|
try {
|
|
9793
|
-
|
|
9794
|
-
|
|
10156
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
10157
|
+
fs19.accessSync(dir, fs19.constants.W_OK);
|
|
9795
10158
|
return true;
|
|
9796
10159
|
} catch {
|
|
9797
10160
|
return false;
|
|
@@ -9799,9 +10162,9 @@ function canWriteDir(dir) {
|
|
|
9799
10162
|
}
|
|
9800
10163
|
function resolveAwsConfigTarget(env = process.env) {
|
|
9801
10164
|
if (env.AWS_CONFIG_FILE) return { configPath: env.AWS_CONFIG_FILE, redirected: false };
|
|
9802
|
-
const real =
|
|
9803
|
-
if (canWriteDir(
|
|
9804
|
-
return { configPath:
|
|
10165
|
+
const real = path20.join(env.HOME ?? "~", ".aws", "config");
|
|
10166
|
+
if (canWriteDir(path20.dirname(real))) return { configPath: real, redirected: false };
|
|
10167
|
+
return { configPath: path20.join(CONFIG_DIR, "aws-config"), redirected: true };
|
|
9805
10168
|
}
|
|
9806
10169
|
function resolveRoleOrExplain(opts, config) {
|
|
9807
10170
|
const role = opts.role ?? process.env.MESH_AWS_ROLE ?? config.defaultRole;
|
|
@@ -9933,10 +10296,10 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
9933
10296
|
const sanitized = context.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
9934
10297
|
const profileName = `mesh-${sanitized}`;
|
|
9935
10298
|
const { configPath, redirected } = resolveAwsConfigTarget();
|
|
9936
|
-
|
|
9937
|
-
const existedBefore =
|
|
9938
|
-
const existing = existedBefore ?
|
|
9939
|
-
const mode = existedBefore ?
|
|
10299
|
+
fs19.mkdirSync(path20.dirname(configPath), { recursive: true });
|
|
10300
|
+
const existedBefore = fs19.existsSync(configPath);
|
|
10301
|
+
const existing = existedBefore ? fs19.readFileSync(configPath, "utf-8") : "";
|
|
10302
|
+
const mode = existedBefore ? fs19.statSync(configPath).mode & 511 : 384;
|
|
9940
10303
|
atomicWriteFileSync(
|
|
9941
10304
|
configPath,
|
|
9942
10305
|
upsertManagedAwsConfigSection(
|
|
@@ -10077,6 +10440,7 @@ export {
|
|
|
10077
10440
|
seedMockIdps,
|
|
10078
10441
|
reconcileRegistryFromZitadel,
|
|
10079
10442
|
detectLocalTenant,
|
|
10443
|
+
removeLocalAppSpiceDBs,
|
|
10080
10444
|
removeAllAliasContainers,
|
|
10081
10445
|
resolveStackOption,
|
|
10082
10446
|
worktreeStackName,
|