@mesh-tech/mesh-cli 0.38.0 → 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.
Files changed (55) hide show
  1. package/dist/bin/{mesh.auth-preflight-AQJ5TQES.js → mesh.auth-preflight-JO5G7SOF.js} +1 -1
  2. package/dist/bin/{mesh.chunk-LMYPTJ2F.js → mesh.chunk-2ZJR5LGN.js} +573 -209
  3. package/dist/bin/mesh.chunk-2ZJR5LGN.js.map +6 -0
  4. package/dist/bin/{mesh.chunk-BF2SH6XT.js → mesh.chunk-C2TKCYHP.js} +1 -1
  5. package/dist/bin/{mesh.chunk-HPYZWPRM.js → mesh.chunk-C4ZM7DM2.js} +1 -1
  6. package/dist/bin/{mesh.chunk-I6NXLXGC.js → mesh.chunk-CNLSVKYY.js} +17 -15
  7. package/dist/bin/mesh.chunk-CNLSVKYY.js.map +6 -0
  8. package/dist/bin/{mesh.chunk-TZAGJTKU.js → mesh.chunk-O6WRZU4X.js} +1 -1
  9. package/dist/bin/{mesh.chunk-KN4BZOYH.js → mesh.chunk-OSV3EZQ3.js} +1 -1
  10. package/dist/bin/{mesh.chunk-2YRWJH5X.js → mesh.chunk-UI6EFAMD.js} +4 -4
  11. package/dist/bin/{mesh.chunk-2YRWJH5X.js.map → mesh.chunk-UI6EFAMD.js.map} +1 -1
  12. package/dist/bin/{mesh.chunk-IU2IWH6O.js → mesh.chunk-WODQO6TP.js} +3 -3
  13. package/dist/bin/{mesh.create-app-NGXACFAU.js → mesh.create-app-SOHBL75U.js} +3 -3
  14. package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js → mesh.dev-token-server-SS7QEQOQ.js} +2 -2
  15. package/dist/bin/mesh.js +8 -8
  16. package/dist/bin/{mesh.login-7SIAKFXJ.js → mesh.login-6VPMWHHU.js} +2 -2
  17. package/dist/bin/{mesh.platform-setup-CJXE3BCP.js → mesh.platform-setup-DJJB2PWL.js} +1 -1
  18. package/dist/bin/{mesh.program-BIDQBSSI.js → mesh.program-Y4HUNBFP.js} +8 -8
  19. package/dist/bin/{mesh.registry-7P4MGXEK.js → mesh.registry-5P4ZLFLJ.js} +4 -4
  20. package/dist/bin/{mesh.registry-identity-JW4KO7N7.js → mesh.registry-identity-ONGUJQAY.js} +3 -3
  21. package/dist/bin/{mesh.start-MPQFIEMH.js → mesh.start-5CPWOHDJ.js} +2 -2
  22. package/dist/bin/{mesh.wizard-ZJE4S6PC.js → mesh.wizard-FNRTSGVN.js} +7 -7
  23. package/dist/build-info.json +2 -2
  24. package/dist/src/commands/app-check.js +2 -2
  25. package/dist/src/commands/dev.js +35 -0
  26. package/dist/src/commands/local/app-authz.d.ts +118 -0
  27. package/dist/src/commands/local/app-authz.js +295 -0
  28. package/dist/src/commands/local/auth-provision.d.ts +1 -0
  29. package/dist/src/commands/local/auth-provision.js +12 -1
  30. package/dist/src/commands/local/index.js +2 -0
  31. package/dist/src/commands/temporal.js +16 -1
  32. package/dist/src/commands/temporal.js.map +2 -2
  33. package/fragments/base/index.ts.hbs +1 -1
  34. package/package.json +2 -2
  35. package/templates/api-auth/api/src/schema.ts.hbs +17 -0
  36. package/templates/api-auth/package.json.hbs +3 -0
  37. package/templates/api-role-gating/api/src/schema.ts.hbs +10 -0
  38. package/templates/apps-repo/gitignore +2 -0
  39. package/dist/bin/mesh.chunk-I6NXLXGC.js.map +0 -6
  40. package/dist/bin/mesh.chunk-LMYPTJ2F.js.map +0 -6
  41. /package/dist/bin/{mesh.auth-preflight-AQJ5TQES.js.map → mesh.auth-preflight-JO5G7SOF.js.map} +0 -0
  42. /package/dist/bin/{mesh.chunk-BF2SH6XT.js.map → mesh.chunk-C2TKCYHP.js.map} +0 -0
  43. /package/dist/bin/{mesh.chunk-HPYZWPRM.js.map → mesh.chunk-C4ZM7DM2.js.map} +0 -0
  44. /package/dist/bin/{mesh.chunk-TZAGJTKU.js.map → mesh.chunk-O6WRZU4X.js.map} +0 -0
  45. /package/dist/bin/{mesh.chunk-KN4BZOYH.js.map → mesh.chunk-OSV3EZQ3.js.map} +0 -0
  46. /package/dist/bin/{mesh.chunk-IU2IWH6O.js.map → mesh.chunk-WODQO6TP.js.map} +0 -0
  47. /package/dist/bin/{mesh.create-app-NGXACFAU.js.map → mesh.create-app-SOHBL75U.js.map} +0 -0
  48. /package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js.map → mesh.dev-token-server-SS7QEQOQ.js.map} +0 -0
  49. /package/dist/bin/{mesh.login-7SIAKFXJ.js.map → mesh.login-6VPMWHHU.js.map} +0 -0
  50. /package/dist/bin/{mesh.platform-setup-CJXE3BCP.js.map → mesh.platform-setup-DJJB2PWL.js.map} +0 -0
  51. /package/dist/bin/{mesh.program-BIDQBSSI.js.map → mesh.program-Y4HUNBFP.js.map} +0 -0
  52. /package/dist/bin/{mesh.registry-7P4MGXEK.js.map → mesh.registry-5P4ZLFLJ.js.map} +0 -0
  53. /package/dist/bin/{mesh.registry-identity-JW4KO7N7.js.map → mesh.registry-identity-ONGUJQAY.js.map} +0 -0
  54. /package/dist/bin/{mesh.start-MPQFIEMH.js.map → mesh.start-5CPWOHDJ.js.map} +0 -0
  55. /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-KN4BZOYH.js";
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 fs18 from "fs";
57
- import * as path19 from "path";
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 fs17 from "node:fs";
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 path18 from "node:path";
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 fs15 from "node:fs";
1240
- import * as path16 from "node:path";
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 fs14 from "fs";
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 path15 from "path";
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
- await api(
3248
- pat,
3249
- "PUT",
3250
- `/management/v1/projects/${projectId}/apps/${existing.id}/oidc_config`,
3251
- config,
3252
- orgId
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/publish-docs.ts
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 * as fs11 from "node:fs";
3559
- import * as path11 from "node:path";
3560
- import { execFileSync as execFileSync6 } from "node:child_process";
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(path20, body) {
3713
- const res = await fetch(`${HOST_HTTP}${path20}`, {
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 ${path20}: ${res.status} ${parsed.message ?? ""}`.trim()), { status: res.status });
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: path11.resolve(appRoot, docs.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 = path11.resolve(appRoot, modulePath);
4218
+ const file = path12.resolve(appRoot, modulePath);
3887
4219
  let mod;
3888
4220
  if (/\.m?js$/.test(file)) {
3889
- mod = await import(pathToFileURL(file).href);
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(fs11.readFileSync(docs.config, "utf8"), docs.config);
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 = fs11.readFileSync(built.specPath, "utf8");
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: createHash2("sha256").update(specText).digest("hex"),
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 fs12 from "fs";
4237
- import * as path12 from "path";
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
- fs12.writeFileSync(path12.join(dir, `${name}.env`), renderDockerEnvFile(env), { mode: 384 });
4261
- const workdir = path12.posix.join("/workspace", service.src === "." ? "" : service.src);
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
- ` - '${path12.dirname(shipper)}:/mesh-assets:ro'`,
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 = path12.join(dir, "compose.yml");
4287
- fs12.writeFileSync(composePath, blocks.join("\n") + "\n");
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", path12.join(sessionDir(sessionName), "compose.yml"), ...args];
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 fs13 from "node:fs";
4697
+ import * as fs14 from "node:fs";
4366
4698
  import * as net2 from "node:net";
4367
- import * as path13 from "node:path";
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 = path13.resolve(serviceSrc);
4536
- const root = path13.resolve(repoRoot);
4537
- return src === root || src.startsWith(root + path13.sep);
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 = path13.join(ctx.appRoot, `Pulumi.${ctx.stack}.yaml`);
4694
- if (!fs13.existsSync(configPath)) {
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 = fs13.statSync(configPath).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 path14 from "node:path";
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}#${path14.resolve(appRoot)}`);
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 = path14.resolve(gitRunner(["rev-parse", "--show-toplevel"], cwd));
5262
+ worktreeRoot = path15.resolve(gitRunner(["rev-parse", "--show-toplevel"], cwd));
4931
5263
  const commonDir = gitRunner(["rev-parse", "--git-common-dir"], cwd);
4932
- const primaryRoot = path14.resolve(path14.dirname(path14.resolve(cwd, commonDir)));
5264
+ const primaryRoot = path15.resolve(path15.dirname(path15.resolve(cwd, commonDir)));
4933
5265
  isPrimary = worktreeRoot === primaryRoot;
4934
5266
  } catch {
4935
5267
  return {
4936
- worktreeRoot: path14.resolve(cwd),
5268
+ worktreeRoot: path15.resolve(cwd),
4937
5269
  isPrimary: true,
4938
- slug: sanitizeSlug(path14.basename(path14.resolve(cwd))),
4939
- hash: worktreeHash(path14.resolve(cwd)),
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 slug = sanitizeSlug(path14.basename(worktreeRoot));
5277
+ const slug2 = sanitizeSlug(path15.basename(worktreeRoot));
4946
5278
  const hash = worktreeHash(worktreeRoot);
4947
- const token = isPrimary ? "" : `${slug}-${hash}`;
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(path15.sep) ? wt.worktreeRoot : wt.worktreeRoot + path15.sep;
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 = path15.resolve(appRoot, rebaseServiceSrc(s.src, monorepoRoot));
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 = path15.join(os3.tmpdir(), "mesh-dev-sessions");
5017
- if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
5018
- return path15.join(dir, `${sessionName}.json`);
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
- fs14.writeFileSync(getSessionStatePath(sessionName), JSON.stringify(state, null, 2));
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 (!fs14.existsSync(filePath)) return null;
5357
+ if (!fs15.existsSync(filePath)) return null;
5026
5358
  try {
5027
- return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
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 path15.join(os3.tmpdir(), "mesh-dev-sessions", sessionName);
5383
+ return path16.join(os3.tmpdir(), "mesh-dev-sessions", sessionName);
5052
5384
  }
5053
5385
  function getServiceEnvFilePath(sessionName, serviceName) {
5054
- return path15.join(getSessionEnvDir(sessionName), envFileName(serviceName));
5386
+ return path16.join(getSessionEnvDir(sessionName), envFileName(serviceName));
5055
5387
  }
5056
5388
  function removeSessionState(sessionName) {
5057
5389
  try {
5058
- fs14.unlinkSync(getSessionStatePath(sessionName));
5390
+ fs15.unlinkSync(getSessionStatePath(sessionName));
5059
5391
  } catch {
5060
5392
  }
5061
5393
  try {
5062
- fs14.rmSync(getSessionEnvDir(sessionName), { recursive: true, force: true });
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 = path15.parse(dir).root;
5522
+ const root = path16.parse(dir).root;
5191
5523
  while (dir !== root) {
5192
- if (fs14.existsSync(path15.join(dir, "pnpm-workspace.yaml")) || fs14.existsSync(path15.join(dir, "turbo.json"))) {
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 = path15.join(dir, ".git");
5196
- if (fs14.existsSync(gitPath)) {
5527
+ const gitPath = path16.join(dir, ".git");
5528
+ if (fs15.existsSync(gitPath)) {
5197
5529
  return dir;
5198
5530
  }
5199
- dir = path15.dirname(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 = fs14.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
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: path15.relative(monorepoRoot, appDir),
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 = path15.join(monorepoRoot, searchDir);
5219
- if (!fs14.existsSync(base)) continue;
5220
- for (const tenant of fs14.readdirSync(base)) {
5221
- const appsDir = path15.join(base, tenant, "apps");
5222
- if (!fs14.existsSync(appsDir) || !fs14.statSync(appsDir).isDirectory()) continue;
5223
- for (const app of fs14.readdirSync(appsDir)) {
5224
- const appDir = path15.join(appsDir, app);
5225
- if (!fs14.existsSync(path15.join(appDir, "Pulumi.yaml"))) continue;
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 = path15.join(monorepoRoot, "apps");
5231
- if (fs14.existsSync(flatAppsDir) && fs14.statSync(flatAppsDir).isDirectory()) {
5232
- for (const app of fs14.readdirSync(flatAppsDir)) {
5233
- const appDir = path15.join(flatAppsDir, app);
5234
- if (!fs14.statSync(appDir).isDirectory()) continue;
5235
- if (!fs14.existsSync(path15.join(appDir, "Pulumi.yaml"))) continue;
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 = fs14.readdirSync(appDir).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
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 = fs14.readFileSync(path15.join(appDir, stackConfigs[0]), "utf-8");
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 = path15.resolve(appPath);
5254
- if (fs14.existsSync(path15.join(resolved, "Pulumi.yaml"))) return resolved;
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 = path15.resolve(mono, appPath);
5258
- if (fs14.existsSync(path15.join(fromMono, "Pulumi.yaml"))) return fromMono;
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 = path15.parse(dir).root;
5596
+ const root = path16.parse(dir).root;
5265
5597
  while (dir !== root) {
5266
- if (fs14.existsSync(path15.join(dir, "Pulumi.yaml"))) {
5598
+ if (fs15.existsSync(path16.join(dir, "Pulumi.yaml"))) {
5267
5599
  return dir;
5268
5600
  }
5269
- dir = path15.dirname(dir);
5601
+ dir = path16.dirname(dir);
5270
5602
  }
5271
5603
  return process.cwd();
5272
5604
  }
5273
5605
  function getProjectName(appRoot) {
5274
- const yamlPath = path15.join(appRoot, "Pulumi.yaml");
5275
- if (!fs14.existsSync(yamlPath)) return path15.basename(appRoot);
5276
- const content = fs14.readFileSync(yamlPath, "utf-8");
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() ?? path15.basename(appRoot);
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 = fs14.readdirSync(appRoot).filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml");
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 = path15.join(opts.sessionScratchDir, "aws-config");
5746
+ const configPath = path16.join(opts.sessionScratchDir, "aws-config");
5415
5747
  const region = awsEnv.AWS_REGION ?? awsEnv.AWS_DEFAULT_REGION ?? "";
5416
- fs14.mkdirSync(opts.sessionScratchDir, { recursive: true });
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 = fs14.existsSync(configPath) ? fs14.readFileSync(configPath, "utf8") : "";
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 = fs14.readFileSync(file, "utf-8");
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 = path15.join(monorepoRoot, rel);
5483
- return fs14.existsSync(rebased) ? rebased : src;
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 = path15.dirname(dir)) {
5820
+ for (let dir = serviceDir; ; dir = path16.dirname(dir)) {
5489
5821
  try {
5490
- const manifest = JSON.parse(fs14.readFileSync(path15.join(dir, "package.json"), "utf8"));
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 = path15.dirname(dir);
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 = path15.dirname(dir)) {
5834
+ for (let dir = appRoot; ; dir = path16.dirname(dir)) {
5503
5835
  try {
5504
- if (fs14.realpathSync(path15.join(dir, "node_modules", name)) === pkgRoot) return true;
5836
+ if (fs15.realpathSync(path16.join(dir, "node_modules", name)) === pkgRoot) return true;
5505
5837
  } catch {
5506
5838
  }
5507
- const parent = path15.dirname(dir);
5839
+ const parent = path16.dirname(dir);
5508
5840
  if (parent === dir) return false;
5509
5841
  }
5510
5842
  }
5511
5843
  function monorepoRelativeSrc(src) {
5512
- if (!path15.isAbsolute(src)) return src.replace(/^\.\//, "") || ".";
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 (fs14.existsSync(existing)) {
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 ?? path15.basename(appRoot);
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 ?? path15.basename(appRoot);
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 = path15.join(caddyDir, "Caddyfile");
6010
- if (!fs14.existsSync(caddyDir)) fs14.mkdirSync(caddyDir, { recursive: true });
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
- fs14.writeFileSync(caddyfile, caddyLines.join("\n"));
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 = path15.resolve(appRoot, rebaseServiceSrc(service.src, monorepoRoot));
6229
- if (worktreeRoot && fs14.existsSync(serviceDir)) {
6230
- const wtRootWithSep = worktreeRoot.endsWith(path15.sep) ? worktreeRoot : worktreeRoot + path15.sep;
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 (!fs14.existsSync(serviceDir)) {
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
- spawnSync("tmux", ["switch-client", "-t", sessionName], { stdio: "inherit" });
6663
+ spawnSync2("tmux", ["switch-client", "-t", sessionName], { stdio: "inherit" });
6332
6664
  } else {
6333
- spawnSync("tmux", ["attach", "-t", sessionName], { stdio: "inherit" });
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 (fs14.existsSync(envFilePath)) {
6485
- const serviceDir = path15.resolve(appRoot, rebaseServiceSrc(service.src, findMonorepoRoot()));
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-YP2VBP2S.js");
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
- fs14.readFileSync(path15.join(appRoot, "package.json"), "utf-8")
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(fs14.readFileSync(path15.join(appRoot, "package.json"), "utf-8"));
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 = path15.resolve(appRoot, service.src);
7181
+ const serviceDir = path16.resolve(appRoot, service.src);
6819
7182
  const env = service.env ?? (service.env = {});
6820
7183
  const serviceEnv = {
6821
- ...readDotEnvFile(path15.join(serviceDir, ".env")),
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) => fs14.existsSync(path15.join(appRoot, dir, "package.json"))
7373
+ (dir) => fs15.existsSync(path16.join(appRoot, dir, "package.json"))
7011
7374
  );
7012
- if (monorepoRoot && path15.resolve(appRoot) === path15.resolve(monorepoRoot) && !looksLikeStandaloneApp) {
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 ?? path16.join(CONFIG_DIR, "tailscale", "port-registry.json");
7807
+ const p = registryPath ?? path17.join(CONFIG_DIR, "tailscale", "port-registry.json");
7445
7808
  let reg;
7446
7809
  try {
7447
- reg = JSON.parse(fs15.readFileSync(p, "utf8"));
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
- fs15.mkdirSync(path16.dirname(p), { recursive: true });
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 fs16 from "node:fs";
7929
+ import * as fs17 from "node:fs";
7567
7930
  import * as net5 from "node:net";
7568
- import * as path17 from "node:path";
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 (!fs16.existsSync(file)) return null;
7937
+ if (!fs17.existsSync(file)) return null;
7575
7938
  try {
7576
- return JSON.parse(fs16.readFileSync(file, "utf8"));
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
- fs16.mkdirSync(path17.dirname(file), { recursive: true });
7583
- fs16.writeFileSync(file, JSON.stringify(manifest, null, 2), { mode: 384 });
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
- fs16.unlinkSync(file);
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 path18.join(CONFIG_DIR, "tailscale", tenant);
8117
+ return path19.join(CONFIG_DIR, "tailscale", tenant);
7755
8118
  }
7756
8119
  function socketPath(tenant) {
7757
- return path18.join(tenantStateDir(tenant), "tailscaled.sock");
8120
+ return path19.join(tenantStateDir(tenant), "tailscaled.sock");
7758
8121
  }
7759
8122
  function logPath(tenant) {
7760
- return path18.join(tenantStateDir(tenant), "tailscaled.log");
8123
+ return path19.join(tenantStateDir(tenant), "tailscaled.log");
7761
8124
  }
7762
8125
  function statePath(tenant) {
7763
- return path18.join(tenantStateDir(tenant), "tunnel-state.json");
8126
+ return path19.join(tenantStateDir(tenant), "tunnel-state.json");
7764
8127
  }
7765
8128
  function daemonMetaPath(tenant) {
7766
- return path18.join(tenantStateDir(tenant), "daemon.json");
8129
+ return path19.join(tenantStateDir(tenant), "daemon.json");
7767
8130
  }
7768
8131
  function runnerManifestPath(tenant) {
7769
- return path18.join(tenantStateDir(tenant), "runner.json");
8132
+ return path19.join(tenantStateDir(tenant), "runner.json");
7770
8133
  }
7771
8134
  function readTunnelState(tenant) {
7772
8135
  const p = statePath(tenant);
7773
- if (!fs17.existsSync(p)) return null;
8136
+ if (!fs18.existsSync(p)) return null;
7774
8137
  try {
7775
- return JSON.parse(fs17.readFileSync(p, "utf8"));
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
- fs17.mkdirSync(tenantStateDir(tenant), { recursive: true });
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
- fs17.unlinkSync(statePath(tenant));
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 (!fs17.existsSync(p)) return null;
8155
+ if (!fs18.existsSync(p)) return null;
7793
8156
  try {
7794
- return JSON.parse(fs17.readFileSync(p, "utf8"));
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
- fs17.mkdirSync(tenantStateDir(tenant), { recursive: true });
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
- fs17.unlinkSync(daemonMetaPath(tenant));
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 (fs17.existsSync(brew)) return brew;
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 (!fs17.existsSync(socketPath(tenant))) return { backendState: "Down" };
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
- fs17.mkdirSync(dir, { recursive: true });
7909
- const log = fs17.openSync(logPath(tenant), "a");
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
- fs17.closeSync(log);
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
- fs17.unlinkSync(p);
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" ? fs17.existsSync(launchAgentPath(state.tenant)) : pidAlive(prior?.supervisorPid);
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 (path18.isAbsolute(meshBin) && installLaunchAgent(state.tenant, meshBin)) {
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 path18.join(os4.homedir(), "Library", "LaunchAgents", `${launchAgentLabel(tenant)}.plist`);
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 = fs17.realpathSync(meshBin);
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 = `${path18.dirname(tailscaledBinPath())}:${pathEnv}`;
8643
+ pathEnv = `${path19.dirname(tailscaledBinPath())}:${pathEnv}`;
8281
8644
  } catch {
8282
8645
  }
8283
- fs17.mkdirSync(path18.dirname(plistPath), { recursive: true });
8284
- fs17.writeFileSync(
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: path18.join(tenantStateDir(tenant), "runner.log"),
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
- fs17.unlinkSync(plistPath);
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 = path19.join(
8952
- process.env.XDG_CONFIG_HOME ?? path19.join(process.env.HOME ?? "~", ".config"),
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 = path19.join(CONFIG_DIR, "config.json");
8956
- var CREDENTIALS_FILE = path19.join(CONFIG_DIR, "credentials.json");
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 (!fs18.existsSync(CONFIG_FILE)) return {};
9335
+ if (!fs19.existsSync(CONFIG_FILE)) return {};
8973
9336
  try {
8974
- return JSON.parse(fs18.readFileSync(CONFIG_FILE, "utf-8"));
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
- fs18.mkdirSync(CONFIG_DIR, { recursive: true });
8983
- fs18.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
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
- fs18.mkdirSync(CONFIG_DIR, { recursive: true });
8990
- fs18.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
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 (!fs18.existsSync(CREDENTIALS_FILE)) return {};
9460
+ if (!fs19.existsSync(CREDENTIALS_FILE)) return {};
9098
9461
  try {
9099
- return JSON.parse(fs18.readFileSync(CREDENTIALS_FILE, "utf-8"));
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(path20, data, mode) {
9109
- const tmpPath = `${path20}.${process.pid}.${atomicWriteCounter++}.tmp`;
9471
+ function atomicWriteFileSync(path21, data, mode) {
9472
+ const tmpPath = `${path21}.${process.pid}.${atomicWriteCounter++}.tmp`;
9110
9473
  try {
9111
- fs18.writeFileSync(tmpPath, data, { mode });
9112
- fs18.renameSync(tmpPath, path20);
9474
+ fs19.writeFileSync(tmpPath, data, { mode });
9475
+ fs19.renameSync(tmpPath, path21);
9113
9476
  } catch (err) {
9114
9477
  try {
9115
- fs18.unlinkSync(tmpPath);
9478
+ fs19.unlinkSync(tmpPath);
9116
9479
  } catch {
9117
9480
  }
9118
9481
  throw err;
9119
9482
  }
9120
9483
  }
9121
9484
  function writeCredentials(context, creds) {
9122
- fs18.mkdirSync(CONFIG_DIR, { recursive: true });
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
- fs18.mkdirSync(CONFIG_DIR, { recursive: true });
9131
- fs18.writeFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), {
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 (fs18.existsSync("/.dockerenv")) return true;
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-JW4KO7N7.js");
10018
+ const { readRegistrySession } = await import("./mesh.registry-identity-ONGUJQAY.js");
9656
10019
  if (readRegistrySession()) return;
9657
- const { probeRegistryToken: probeRegistryToken2 } = await import("./mesh.auth-preflight-AQJ5TQES.js");
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 ?? path19.join(env.HOME ?? "~", ".aws", "config");
10152
+ return env.AWS_CONFIG_FILE ?? path20.join(env.HOME ?? "~", ".aws", "config");
9790
10153
  }
9791
10154
  function canWriteDir(dir) {
9792
10155
  try {
9793
- fs18.mkdirSync(dir, { recursive: true });
9794
- fs18.accessSync(dir, fs18.constants.W_OK);
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 = path19.join(env.HOME ?? "~", ".aws", "config");
9803
- if (canWriteDir(path19.dirname(real))) return { configPath: real, redirected: false };
9804
- return { configPath: path19.join(CONFIG_DIR, "aws-config"), redirected: true };
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
- fs18.mkdirSync(path19.dirname(configPath), { recursive: true });
9937
- const existedBefore = fs18.existsSync(configPath);
9938
- const existing = existedBefore ? fs18.readFileSync(configPath, "utf-8") : "";
9939
- const mode = existedBefore ? fs18.statSync(configPath).mode & 511 : 384;
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,