@mesh-tech/mesh-cli 0.38.1 → 0.40.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 (104) hide show
  1. package/dist/bin/{mesh.auth-preflight-AQJ5TQES.js → mesh.auth-preflight-AUFQ2FGD.js} +1 -1
  2. package/dist/bin/{mesh.chunk-I6NXLXGC.js → mesh.chunk-2ASXJ5DO.js} +235 -581
  3. package/dist/bin/mesh.chunk-2ASXJ5DO.js.map +6 -0
  4. package/dist/bin/{mesh.chunk-IU2IWH6O.js → mesh.chunk-6AIG5OJM.js} +3 -3
  5. package/dist/bin/{mesh.chunk-KN4BZOYH.js → mesh.chunk-6SSOVZOI.js} +1 -1
  6. package/dist/bin/{mesh.chunk-LMYPTJ2F.js → mesh.chunk-7HTKSUWR.js} +1900 -577
  7. package/dist/bin/mesh.chunk-7HTKSUWR.js.map +6 -0
  8. package/dist/bin/{mesh.chunk-2YRWJH5X.js → mesh.chunk-F44A3ACW.js} +4 -4
  9. package/dist/bin/{mesh.chunk-2YRWJH5X.js.map → mesh.chunk-F44A3ACW.js.map} +1 -1
  10. package/dist/bin/{mesh.chunk-BF2SH6XT.js → mesh.chunk-HJGMOEUG.js} +1 -1
  11. package/dist/bin/{mesh.chunk-TZAGJTKU.js → mesh.chunk-NSQJEX36.js} +1 -1
  12. package/dist/bin/{mesh.chunk-HPYZWPRM.js → mesh.chunk-PZVI7V3J.js} +1 -1
  13. package/dist/bin/mesh.chunk-TDCH4SFQ.js +419 -0
  14. package/dist/bin/mesh.chunk-TDCH4SFQ.js.map +6 -0
  15. package/dist/bin/mesh.chunk-YE2WIY3Y.js +473 -0
  16. package/dist/bin/mesh.chunk-YE2WIY3Y.js.map +6 -0
  17. package/dist/bin/{mesh.create-app-NGXACFAU.js → mesh.create-app-Z3RKSTBO.js} +3 -3
  18. package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js → mesh.dev-token-server-XG26C6IQ.js} +2 -2
  19. package/dist/bin/mesh.generate-XSM4C6FQ.js +10 -0
  20. package/dist/bin/mesh.js +9 -8
  21. package/dist/bin/mesh.js.map +1 -1
  22. package/dist/bin/{mesh.login-7SIAKFXJ.js → mesh.login-3AWVGPCM.js} +2 -2
  23. package/dist/bin/{mesh.platform-setup-CJXE3BCP.js → mesh.platform-setup-2ZDMHRCI.js} +1 -1
  24. package/dist/bin/{mesh.program-BIDQBSSI.js → mesh.program-K5JQXEHX.js} +9 -8
  25. package/dist/bin/{mesh.registry-7P4MGXEK.js → mesh.registry-JLQYFUNN.js} +4 -4
  26. package/dist/bin/{mesh.registry-identity-JW4KO7N7.js → mesh.registry-identity-3AR27UGO.js} +3 -3
  27. package/dist/bin/mesh.registry-identity-3AR27UGO.js.map +6 -0
  28. package/dist/bin/mesh.review-command-LUVZQN2T.js +238 -0
  29. package/dist/bin/mesh.review-command-LUVZQN2T.js.map +6 -0
  30. package/dist/bin/{mesh.start-MPQFIEMH.js → mesh.start-KZ5L6SOJ.js} +2 -2
  31. package/dist/bin/{mesh.wizard-ZJE4S6PC.js → mesh.wizard-FCTT55KI.js} +7 -7
  32. package/dist/bin/mesh.workspace-membership-DI3YQGEK.js +65 -0
  33. package/dist/bin/mesh.workspace-membership-DI3YQGEK.js.map +6 -0
  34. package/dist/build-info.json +2 -2
  35. package/dist/src/commands/app-check.js +2 -2
  36. package/dist/src/commands/dev-doctor.d.ts +22 -0
  37. package/dist/src/commands/dev-doctor.js +149 -0
  38. package/dist/src/commands/dev-env-leaks.d.ts +24 -0
  39. package/dist/src/commands/dev-env-leaks.js +178 -0
  40. package/dist/src/commands/dev-launch.d.ts +17 -1
  41. package/dist/src/commands/dev-launch.js +130 -5
  42. package/dist/src/commands/dev-readiness.d.ts +49 -0
  43. package/dist/src/commands/dev-readiness.js +78 -0
  44. package/dist/src/commands/dev.d.ts +60 -0
  45. package/dist/src/commands/dev.js +574 -140
  46. package/dist/src/commands/hub/index.js +10 -7
  47. package/dist/src/commands/local/app-authz.d.ts +118 -0
  48. package/dist/src/commands/local/app-authz.js +295 -0
  49. package/dist/src/commands/local/auth-provision.d.ts +1 -0
  50. package/dist/src/commands/local/auth-provision.js +12 -1
  51. package/dist/src/commands/local/index.js +2 -0
  52. package/dist/src/commands/mcp/generate.d.ts +11 -0
  53. package/dist/src/commands/mcp/generate.js +78 -0
  54. package/dist/src/commands/mcp/index.d.ts +2 -0
  55. package/dist/src/commands/mcp/index.js +70 -0
  56. package/dist/src/commands/mcp/manifest-file.d.ts +5 -0
  57. package/dist/src/commands/mcp/manifest-file.js +24 -0
  58. package/dist/src/commands/mcp/review-command.d.ts +24 -0
  59. package/dist/src/commands/mcp/review-command.js +66 -0
  60. package/dist/src/commands/mcp/review.d.ts +41 -0
  61. package/dist/src/commands/mcp/review.js +140 -0
  62. package/dist/src/commands/mcp/workspace-membership.d.ts +7 -0
  63. package/dist/src/commands/mcp/workspace-membership.js +77 -0
  64. package/dist/src/commands/secrets/reindex.js +2 -7
  65. package/dist/src/commands/secrets/set.d.ts +22 -1
  66. package/dist/src/commands/secrets/set.js +31 -8
  67. package/dist/src/commands/temporal.js +51 -5
  68. package/dist/src/commands/temporal.js.map +2 -2
  69. package/dist/src/program.js +2 -0
  70. package/dist/src/utils/aws-auth.d.ts +4 -0
  71. package/dist/src/utils/aws-auth.js +7 -3
  72. package/dist/src/utils/dev-sessions-root.d.ts +3 -0
  73. package/dist/src/utils/dev-sessions-root.js +45 -0
  74. package/dist/src/utils/kubeconfig.js +2 -2
  75. package/dist/src/utils/tailscale.js +7 -1
  76. package/dist/src/utils/temporal-auth.js +2 -1
  77. package/dist/src/utils/workflow-fingerprint.js +23 -12
  78. package/fragments/base/index.ts.hbs +4 -1
  79. package/package.json +7 -5
  80. package/skills/mesh/SKILL.md +10 -6
  81. package/templates/api-auth/api/src/schema.ts.hbs +17 -0
  82. package/templates/api-auth/index.ts.hbs +1 -0
  83. package/templates/api-auth/package.json.hbs +3 -0
  84. package/templates/api-role-gating/api/src/schema.ts.hbs +10 -0
  85. package/templates/api-role-gating/index.ts.hbs +1 -0
  86. package/templates/external-service/index.ts.hbs +1 -0
  87. package/templates/workflow/index.ts.hbs +2 -0
  88. package/dist/bin/mesh.chunk-I6NXLXGC.js.map +0 -6
  89. package/dist/bin/mesh.chunk-LMYPTJ2F.js.map +0 -6
  90. /package/dist/bin/{mesh.auth-preflight-AQJ5TQES.js.map → mesh.auth-preflight-AUFQ2FGD.js.map} +0 -0
  91. /package/dist/bin/{mesh.chunk-IU2IWH6O.js.map → mesh.chunk-6AIG5OJM.js.map} +0 -0
  92. /package/dist/bin/{mesh.chunk-KN4BZOYH.js.map → mesh.chunk-6SSOVZOI.js.map} +0 -0
  93. /package/dist/bin/{mesh.chunk-BF2SH6XT.js.map → mesh.chunk-HJGMOEUG.js.map} +0 -0
  94. /package/dist/bin/{mesh.chunk-TZAGJTKU.js.map → mesh.chunk-NSQJEX36.js.map} +0 -0
  95. /package/dist/bin/{mesh.chunk-HPYZWPRM.js.map → mesh.chunk-PZVI7V3J.js.map} +0 -0
  96. /package/dist/bin/{mesh.create-app-NGXACFAU.js.map → mesh.create-app-Z3RKSTBO.js.map} +0 -0
  97. /package/dist/bin/{mesh.dev-token-server-YP2VBP2S.js.map → mesh.dev-token-server-XG26C6IQ.js.map} +0 -0
  98. /package/dist/bin/{mesh.login-7SIAKFXJ.js.map → mesh.generate-XSM4C6FQ.js.map} +0 -0
  99. /package/dist/bin/{mesh.platform-setup-CJXE3BCP.js.map → mesh.login-3AWVGPCM.js.map} +0 -0
  100. /package/dist/bin/{mesh.program-BIDQBSSI.js.map → mesh.platform-setup-2ZDMHRCI.js.map} +0 -0
  101. /package/dist/bin/{mesh.registry-7P4MGXEK.js.map → mesh.program-K5JQXEHX.js.map} +0 -0
  102. /package/dist/bin/{mesh.registry-identity-JW4KO7N7.js.map → mesh.registry-JLQYFUNN.js.map} +0 -0
  103. /package/dist/bin/{mesh.start-MPQFIEMH.js.map → mesh.start-KZ5L6SOJ.js.map} +0 -0
  104. /package/dist/bin/{mesh.wizard-ZJE4S6PC.js.map → mesh.wizard-FCTT55KI.js.map} +0 -0
@@ -15,13 +15,13 @@ import {
15
15
  registrySessionCandidates,
16
16
  registryTokenRoles,
17
17
  resolveRegistryIdentity
18
- } from "./mesh.chunk-BF2SH6XT.js";
19
- import "./mesh.chunk-LMYPTJ2F.js";
18
+ } from "./mesh.chunk-HJGMOEUG.js";
19
+ import "./mesh.chunk-7HTKSUWR.js";
20
20
  import "./mesh.chunk-2QIVBQX5.js";
21
21
  import "./mesh.chunk-MCBMHWXP.js";
22
22
  import "./mesh.chunk-WFFDF346.js";
23
23
  import "./mesh.chunk-IQOBTL42.js";
24
- import "./mesh.chunk-KN4BZOYH.js";
24
+ import "./mesh.chunk-6SSOVZOI.js";
25
25
  export {
26
26
  REGISTRY_ALIAS,
27
27
  REGISTRY_CREDENTIAL_KEY,
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "mappings": "",
5
+ "names": []
6
+ }
@@ -0,0 +1,238 @@
1
+ import {
2
+ classifyMcpTools,
3
+ defaultServeDir,
4
+ generateMcpServeDir,
5
+ mcpAuthHeaders,
6
+ parseMcpToolSnapshot,
7
+ readMcpManifestFile
8
+ } from "./mesh.chunk-TDCH4SFQ.js";
9
+ import {
10
+ discoverStackExternalServices,
11
+ ensureAwsCredentialsForStack,
12
+ resolveExternalServiceScope
13
+ } from "./mesh.chunk-YE2WIY3Y.js";
14
+ import "./mesh.chunk-7HTKSUWR.js";
15
+ import "./mesh.chunk-2QIVBQX5.js";
16
+ import "./mesh.chunk-MCBMHWXP.js";
17
+ import {
18
+ logInfo
19
+ } from "./mesh.chunk-WFFDF346.js";
20
+ import "./mesh.chunk-IQOBTL42.js";
21
+ import "./mesh.chunk-6SSOVZOI.js";
22
+
23
+ // packages/mesh-cli/src/commands/mcp/review-command.ts
24
+ import { SSMClient } from "@aws-sdk/client-ssm";
25
+ import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
26
+ import confirm from "@inquirer/confirm";
27
+
28
+ // packages/mesh-cli/src/commands/mcp/review.ts
29
+ import * as fs from "node:fs";
30
+ function canonical(tool) {
31
+ return JSON.stringify(tool, Object.keys(tool).sort());
32
+ }
33
+ function describeChange(before, after) {
34
+ const parts = [];
35
+ if (before.description !== after.description) parts.push("description");
36
+ if (JSON.stringify(before.inputSchema) !== JSON.stringify(after.inputSchema))
37
+ parts.push("input schema");
38
+ if (JSON.stringify(before.outputSchema) !== JSON.stringify(after.outputSchema))
39
+ parts.push("output schema");
40
+ if (JSON.stringify(before.annotations) !== JSON.stringify(after.annotations))
41
+ parts.push("annotations (read/write classification)");
42
+ return parts.length > 0 ? parts.join(", ") : "other fields";
43
+ }
44
+ function diffSnapshot(current, fetched, enabledOps = [], classification = {}) {
45
+ const before = new Map(current.map((t) => [t.name, t]));
46
+ const after = new Map(fetched.map((t) => [t.name, t]));
47
+ const classified = {
48
+ ...classifyMcpTools(current, classification),
49
+ ...classifyMcpTools(fetched, classification)
50
+ };
51
+ const accessOf = (name) => {
52
+ const entry = classified[name];
53
+ return { access: entry.access, ...entry.suggested ? { suggested: entry.suggested } : {} };
54
+ };
55
+ const entries = [];
56
+ for (const tool of fetched) {
57
+ const prior = before.get(tool.name);
58
+ if (!prior) {
59
+ entries.push({ name: tool.name, status: "added", ...accessOf(tool.name) });
60
+ } else if (canonical(prior) !== canonical(tool)) {
61
+ entries.push({
62
+ name: tool.name,
63
+ status: "changed",
64
+ ...accessOf(tool.name),
65
+ detail: describeChange(prior, tool)
66
+ });
67
+ } else {
68
+ entries.push({ name: tool.name, status: "unchanged", ...accessOf(tool.name) });
69
+ }
70
+ }
71
+ for (const tool of current) {
72
+ if (!after.has(tool.name)) {
73
+ entries.push({ name: tool.name, status: "removed", ...accessOf(tool.name) });
74
+ }
75
+ }
76
+ const pick = (status) => entries.filter((e) => e.status === status).map((e) => e.name);
77
+ const removed = pick("removed");
78
+ return {
79
+ entries,
80
+ added: pick("added"),
81
+ changed: pick("changed"),
82
+ removed,
83
+ removedEnabled: enabledOps.filter((op) => removed.includes(op)),
84
+ clean: pick("added").length === 0 && pick("changed").length === 0 && removed.length === 0,
85
+ next: [...fetched]
86
+ };
87
+ }
88
+ async function reviewMcpCatalog(opts) {
89
+ const loaded = readMcpManifestFile(opts.manifestPath);
90
+ const { manifest } = loaded;
91
+ const report = opts.report ?? ((line) => console.log(line));
92
+ const snapshotPath = loaded.toolsPath;
93
+ const headers = mcpAuthHeaders(manifest, opts.credentials);
94
+ const fetched = parseMcpToolSnapshot(
95
+ await opts.fetchTools({
96
+ serverUrl: manifest.serverUrl,
97
+ headers,
98
+ timeoutMs: manifest.timeoutMs ?? 3e4
99
+ }),
100
+ `${manifest.serverUrl} tools/list`
101
+ );
102
+ if (fetched.length === 0) {
103
+ throw new Error(
104
+ `${manifest.name}: the server answered tools/list with an empty catalog \u2014 refusing to replace a reviewed snapshot with nothing`
105
+ );
106
+ }
107
+ const enabled = manifest.enabledOps ?? loaded.tools.map((t) => t.name);
108
+ const diff = diffSnapshot(loaded.tools, fetched, enabled, manifest.classification ?? {});
109
+ report(`${manifest.name} \u2014 ${manifest.serverUrl}`);
110
+ report(` snapshot: ${snapshotPath}`);
111
+ for (const entry of diff.entries) {
112
+ const mark = entry.status === "added" ? "+" : entry.status === "removed" ? "-" : entry.status === "changed" ? "~" : " ";
113
+ const enabledMark = enabled.includes(entry.name) ? "enabled" : "not enabled";
114
+ const suggestion = entry.suggested ? ", vendor suggests read" : "";
115
+ report(
116
+ ` ${mark} ${entry.name} [${entry.access}${suggestion}, ${enabledMark}]${entry.detail ? ` \u2014 ${entry.detail}` : ""}`
117
+ );
118
+ }
119
+ const suggestedReads = diff.entries.filter((entry) => entry.suggested === "read" && entry.status !== "removed").map((entry) => entry.name);
120
+ if (suggestedReads.length > 0) {
121
+ report(
122
+ ` the vendor marks ${suggestedReads.join(", ")} read-only (readOnlyHint). They stay unknown and need approval until the manifest's "classification" names them "read".`
123
+ );
124
+ }
125
+ if (diff.added.length > 0) {
126
+ report(
127
+ ` ${diff.added.length} new upstream tool(s) will be recorded but NOT enabled: add them to enabledOps deliberately.`
128
+ );
129
+ }
130
+ if (diff.removedEnabled.length > 0) {
131
+ throw new Error(
132
+ `${manifest.name}: the server no longer offers enabled operation(s) ${diff.removedEnabled.join(", ")} \u2014 remove them from enabledOps first, then re-review`
133
+ );
134
+ }
135
+ if (diff.clean) {
136
+ report(" no changes \u2014 the reviewed snapshot still matches the server.");
137
+ return { diff, manifest, snapshotPath, written: false };
138
+ }
139
+ if (opts.dryRun) {
140
+ report(" --dry-run: nothing written.");
141
+ return { diff, manifest, snapshotPath, written: false };
142
+ }
143
+ const approved = opts.yes === true || await (opts.confirm ?? (async () => false))(`Write the new snapshot to ${snapshotPath}?`);
144
+ if (!approved) {
145
+ report(" not written.");
146
+ return { diff, manifest, snapshotPath, written: false };
147
+ }
148
+ fs.writeFileSync(snapshotPath, `${JSON.stringify(diff.next, null, 2)}
149
+ `);
150
+ report(` wrote ${diff.next.length} reviewed tool(s).`);
151
+ const serveDir = defaultServeDir(opts.manifestPath, manifest.name);
152
+ if (fs.existsSync(serveDir)) {
153
+ const generated = generateMcpServeDir(opts.manifestPath);
154
+ report(` regenerated ${generated.written.join(", ") || "nothing"} in ${generated.serveDir}.`);
155
+ } else {
156
+ report(` run \`mesh mcp generate ${opts.manifestPath}\` to write the worker package.`);
157
+ }
158
+ return { diff, manifest, snapshotPath, written: true };
159
+ }
160
+
161
+ // packages/mesh-cli/src/commands/mcp/review-command.ts
162
+ async function resolveIntegrationCredentials(name, opts, clients = {}) {
163
+ const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
164
+ let secretId = opts.secret;
165
+ if (secretId) {
166
+ const ensure = clients.ensureCredentials ?? ensureAwsCredentialsForStack;
167
+ await ensure(resolveExternalServiceScope(opts).stack);
168
+ } else {
169
+ const { scope, services } = await discoverStackExternalServices(
170
+ opts,
171
+ clients.ssm ?? new SSMClient({ region }),
172
+ clients.ensureCredentials ? { ensureCredentials: clients.ensureCredentials } : {}
173
+ );
174
+ const service = services.get(name);
175
+ if (!service?.meta?.secretPrefix) {
176
+ throw new Error(
177
+ `No deployed ExternalService named "${name}" in ${scope.context.tenant}/${scope.platformEnv} (stack ${scope.stack ?? "unknown"}). Deploy the Integration first, or pass --secret <id>.`
178
+ );
179
+ }
180
+ secretId = service.meta.secretPrefix;
181
+ }
182
+ const response = await (clients.secretsManager ?? new SecretsManagerClient({ region })).send(
183
+ new GetSecretValueCommand({ SecretId: secretId })
184
+ );
185
+ if (!response.SecretString) {
186
+ throw new Error(
187
+ `The Integration credential at "${secretId}" is empty \u2014 populate it with \`mesh secrets set external/${name}\``
188
+ );
189
+ }
190
+ return JSON.parse(response.SecretString);
191
+ }
192
+ var fetchMcpToolsOverHttp = async ({ serverUrl, headers, timeoutMs }) => {
193
+ const { Client, StreamableHTTPClientTransport } = await import("@modelcontextprotocol/client");
194
+ const client = new Client(
195
+ { name: "mesh-mcp-review", version: "1.0.0" },
196
+ { capabilities: {}, versionNegotiation: { mode: "auto" } }
197
+ );
198
+ const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
199
+ requestInit: { headers, redirect: "error" }
200
+ });
201
+ try {
202
+ await client.connect(transport, { timeout: timeoutMs });
203
+ const result = await client.listTools(void 0, { timeout: timeoutMs });
204
+ return result.tools;
205
+ } finally {
206
+ if (transport.sessionId) await transport.terminateSession().catch(() => {
207
+ });
208
+ await client.close().catch(() => {
209
+ });
210
+ await transport.close().catch(() => {
211
+ });
212
+ }
213
+ };
214
+ async function runMcpReviewCommand(manifestPath, opts) {
215
+ const { manifest } = readMcpManifestFile(manifestPath);
216
+ const credentials = manifest.auth.kind === "shared-header" ? await resolveIntegrationCredentials(manifest.name, opts) : {};
217
+ if (manifest.auth.kind === "none") {
218
+ logInfo(`${manifest.name} declares no authentication; connecting unauthenticated.`);
219
+ }
220
+ const result = await reviewMcpCatalog({
221
+ manifestPath,
222
+ credentials,
223
+ fetchTools: fetchMcpToolsOverHttp,
224
+ ...opts.yes ? { yes: true } : {},
225
+ ...opts.dryRun ? { dryRun: true } : {},
226
+ confirm: (question) => confirm({ message: question, default: false })
227
+ });
228
+ if (result.written) {
229
+ logInfo(
230
+ "Commit the snapshot with the regenerated worker package; McpIntegration refuses a package that does not match its manifest."
231
+ );
232
+ }
233
+ }
234
+ export {
235
+ fetchMcpToolsOverHttp,
236
+ resolveIntegrationCredentials,
237
+ runMcpReviewCommand
238
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/mcp/review-command.ts", "../../src/commands/mcp/review.ts"],
4
+ "mappings": "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",
5
+ "names": []
6
+ }
@@ -4,7 +4,7 @@ import {
4
4
  reportAssembly,
5
5
  resolveCliRuntime,
6
6
  runAssemble
7
- } from "./mesh.chunk-HPYZWPRM.js";
7
+ } from "./mesh.chunk-PZVI7V3J.js";
8
8
  import {
9
9
  logError,
10
10
  logInfo,
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  homeNpmrcPath,
15
15
  parseNpmrcRegistryAuth
16
- } from "./mesh.chunk-KN4BZOYH.js";
16
+ } from "./mesh.chunk-6SSOVZOI.js";
17
17
 
18
18
  // packages/mesh-cli/src/docs/start.ts
19
19
  import { execFileSync as execFileSync2 } from "node:child_process";
@@ -2,24 +2,24 @@ import {
2
2
  bootstrapAppsRepo,
3
3
  isInsidePlatformMonorepo,
4
4
  shouldBootstrapAppsRepo
5
- } from "./mesh.chunk-2YRWJH5X.js";
5
+ } from "./mesh.chunk-F44A3ACW.js";
6
6
  import {
7
7
  PLATFORM_SETUP_DEFAULTS,
8
8
  ensureStateBucket,
9
9
  renderPlatformNextSteps,
10
10
  resolvePlatformSetup,
11
11
  scaffoldPlatformRepo
12
- } from "./mesh.chunk-TZAGJTKU.js";
12
+ } from "./mesh.chunk-NSQJEX36.js";
13
13
  import {
14
14
  RegistryNotAuthorizedError
15
- } from "./mesh.chunk-IU2IWH6O.js";
15
+ } from "./mesh.chunk-6AIG5OJM.js";
16
16
  import {
17
17
  resolveTargetRoot,
18
18
  syncSkills
19
19
  } from "./mesh.chunk-F4654EPX.js";
20
20
  import {
21
21
  readRegistrySession
22
- } from "./mesh.chunk-BF2SH6XT.js";
22
+ } from "./mesh.chunk-HJGMOEUG.js";
23
23
  import {
24
24
  detectLocalTenant,
25
25
  discoverConfigForContext,
@@ -31,7 +31,7 @@ import {
31
31
  runLoginFlow,
32
32
  tokenStillValid,
33
33
  writeMeshJson
34
- } from "./mesh.chunk-LMYPTJ2F.js";
34
+ } from "./mesh.chunk-7HTKSUWR.js";
35
35
  import "./mesh.chunk-2QIVBQX5.js";
36
36
  import "./mesh.chunk-MCBMHWXP.js";
37
37
  import {
@@ -44,7 +44,7 @@ import {
44
44
  } from "./mesh.chunk-IQOBTL42.js";
45
45
  import {
46
46
  probeRegistryToken
47
- } from "./mesh.chunk-KN4BZOYH.js";
47
+ } from "./mesh.chunk-6SSOVZOI.js";
48
48
 
49
49
  // packages/mesh-cli/src/commands/init/wizard.ts
50
50
  import * as fs from "fs";
@@ -157,7 +157,7 @@ var registryStep = {
157
157
  try {
158
158
  logInfo(REGISTRY_WHY);
159
159
  const login = ctx.seams?.runRegistryLogin ?? (async (opts) => {
160
- const { runRegistryLogin } = await import("./mesh.registry-7P4MGXEK.js");
160
+ const { runRegistryLogin } = await import("./mesh.registry-JLQYFUNN.js");
161
161
  await runRegistryLogin(void 0, opts);
162
162
  });
163
163
  await login({ device: ctx.device, profile: ctx.profile });
@@ -0,0 +1,65 @@
1
+ // packages/mesh-cli/src/commands/mcp/workspace-membership.ts
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { parse } from "yaml";
5
+ function findWorkspaceFile(from) {
6
+ let dir = from;
7
+ for (; ; ) {
8
+ const file = path.join(dir, "pnpm-workspace.yaml");
9
+ if (fs.existsSync(file)) return file;
10
+ const parent = path.dirname(dir);
11
+ if (parent === dir) return void 0;
12
+ dir = parent;
13
+ }
14
+ }
15
+ function segmentPattern(segment) {
16
+ const body = segment.split("").map(
17
+ (ch) => ch === "*" ? "[^/]*" : ch === "?" ? "[^/]" : ch.replace(/[.+^${}()|[\]\\]/g, "\\$&")
18
+ ).join("");
19
+ return new RegExp(segment.startsWith(".") ? `^${body}$` : `^(?!\\.)${body}$`);
20
+ }
21
+ function matchSegments(pattern, target) {
22
+ if (pattern.length === 0) return target.length === 0;
23
+ const [head, ...rest] = pattern;
24
+ if (head === "**") {
25
+ for (let i = 0; i <= target.length; i++) {
26
+ if (target.slice(0, i).some((s) => s.startsWith("."))) break;
27
+ if (matchSegments(rest, target.slice(i))) return true;
28
+ }
29
+ return false;
30
+ }
31
+ if (target.length === 0) return false;
32
+ return segmentPattern(head).test(target[0]) && matchSegments(rest, target.slice(1));
33
+ }
34
+ function matchesWorkspaceGlob(pattern, relDir) {
35
+ const clean = (p) => p.replace(/^\.\//, "").replace(/\/+$/, "").split("/").filter((s) => s.length > 0 && s !== ".");
36
+ return matchSegments(clean(pattern), clean(relDir.split(path.sep).join("/")));
37
+ }
38
+ function workspaceMembership(dir) {
39
+ const abs = path.resolve(dir);
40
+ const workspaceFile = findWorkspaceFile(abs);
41
+ if (!workspaceFile) return { member: false };
42
+ const root = path.dirname(workspaceFile);
43
+ const rel = path.relative(root, abs).split(path.sep).join("/");
44
+ const suggestedGlob = `${path.posix.dirname(rel)}/*`;
45
+ let globs;
46
+ try {
47
+ globs = parse(fs.readFileSync(workspaceFile, "utf8"))?.packages;
48
+ } catch {
49
+ return { workspaceFile, member: false, suggestedGlob };
50
+ }
51
+ let member = false;
52
+ for (const glob of Array.isArray(globs) ? globs : []) {
53
+ if (typeof glob !== "string") continue;
54
+ if (glob.startsWith("!")) {
55
+ if (matchesWorkspaceGlob(glob.slice(1), rel)) member = false;
56
+ } else if (matchesWorkspaceGlob(glob, rel)) {
57
+ member = true;
58
+ }
59
+ }
60
+ return { workspaceFile, member, suggestedGlob };
61
+ }
62
+ export {
63
+ matchesWorkspaceGlob,
64
+ workspaceMembership
65
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/mcp/workspace-membership.ts"],
4
+ "mappings": ";AASA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,aAAa;AAetB,SAAS,kBAAkB,MAAkC;AAC3D,MAAI,MAAM;AACV,aAAS;AACP,UAAM,OAAY,UAAK,KAAK,qBAAqB;AACjD,QAAO,cAAW,IAAI,EAAG,QAAO;AAChC,UAAM,SAAc,aAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAEA,SAAS,eAAe,SAAyB;AAC/C,QAAM,OAAO,QACV,MAAM,EAAE,EACR;AAAA,IAAI,CAAC,OACJ,OAAO,MAAM,UAAU,OAAO,MAAM,SAAS,GAAG,QAAQ,qBAAqB,MAAM;AAAA,EACrF,EACC,KAAK,EAAE;AAGV,SAAO,IAAI,OAAO,QAAQ,WAAW,GAAG,IAAI,IAAI,IAAI,MAAM,WAAW,IAAI,GAAG;AAC9E;AAEA,SAAS,cAAc,SAAmB,QAA2B;AACnE,MAAI,QAAQ,WAAW,EAAG,QAAO,OAAO,WAAW;AACnD,QAAM,CAAC,MAAM,GAAG,IAAI,IAAI;AACxB,MAAI,SAAS,MAAM;AACjB,aAAS,IAAI,GAAG,KAAK,OAAO,QAAQ,KAAK;AACvC,UAAI,OAAO,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,EAAG;AACvD,UAAI,cAAc,MAAM,OAAO,MAAM,CAAC,CAAC,EAAG,QAAO;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,eAAe,IAAK,EAAE,KAAK,OAAO,CAAC,CAAE,KAAK,cAAc,MAAM,OAAO,MAAM,CAAC,CAAC;AACtF;AAWO,SAAS,qBAAqB,SAAiB,QAAyB;AAC7E,QAAM,QAAQ,CAAC,MACb,EACG,QAAQ,SAAS,EAAE,EACnB,QAAQ,QAAQ,EAAE,EAClB,MAAM,GAAG,EACT,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,GAAG;AAC5C,SAAO,cAAc,MAAM,OAAO,GAAG,MAAM,OAAO,MAAW,QAAG,EAAE,KAAK,GAAG,CAAC,CAAC;AAC9E;AAWO,SAAS,oBAAoB,KAAkC;AACpE,QAAM,MAAW,aAAQ,GAAG;AAC5B,QAAM,gBAAgB,kBAAkB,GAAG;AAC3C,MAAI,CAAC,cAAe,QAAO,EAAE,QAAQ,MAAM;AAC3C,QAAM,OAAY,aAAQ,aAAa;AACvC,QAAM,MAAW,cAAS,MAAM,GAAG,EAAE,MAAW,QAAG,EAAE,KAAK,GAAG;AAC7D,QAAM,gBAAgB,GAAQ,WAAM,QAAQ,GAAG,CAAC;AAChD,MAAI;AACJ,MAAI;AACF,YAAS,MAAS,gBAAa,eAAe,MAAM,CAAC,GACjD;AAAA,EACN,QAAQ;AACN,WAAO,EAAE,eAAe,QAAQ,OAAO,cAAc;AAAA,EACvD;AACA,MAAI,SAAS;AACb,aAAW,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAAG;AACpD,QAAI,OAAO,SAAS,SAAU;AAC9B,QAAI,KAAK,WAAW,GAAG,GAAG;AACxB,UAAI,qBAAqB,KAAK,MAAM,CAAC,GAAG,GAAG,EAAG,UAAS;AAAA,IACzD,WAAW,qBAAqB,MAAM,GAAG,GAAG;AAC1C,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,EAAE,eAAe,QAAQ,cAAc;AAChD;",
5
+ "names": []
6
+ }
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "ac9c6b19f",
3
- "builtAt": "2026-09-30T21:30:19.767Z"
2
+ "commit": "e76bb0770",
3
+ "builtAt": "2026-10-16T01:18:50.130Z"
4
4
  }
@@ -75,7 +75,7 @@ export const APP_CHECK_RULES = {
75
75
  gate: "H",
76
76
  title: "No role grants in IaC",
77
77
  severity: "block",
78
- remediation: "remove the per-user grant map; grant roles from the Hub (App → Access → Users)",
78
+ remediation: "remove the per-user grant map; grant roles from the Hub (App → Access → Sign-in)",
79
79
  scope: "app",
80
80
  },
81
81
  POINTER_PROJECT_OVERRIDE: {
@@ -794,7 +794,7 @@ export function scanApp(root, appRel, resolution = probeResolution(root, appRel)
794
794
  for (const f of files)
795
795
  hit(code, rel(root, f), why);
796
796
  };
797
- anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC — grants belong in the Hub (Access → Users), not in a config redeploy");
797
+ anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC — grants belong in the Hub (Access → Sign-in), not in a config redeploy");
798
798
  anti("POINTER_PROJECT_OVERRIDE", RE_POINTER_OVERRIDE, "AppAuthzPointer is given a `zitadel:` override — an app binds its project with AppEnvironment's zitadelAppProjectId so every consumer of env.zitadel agrees; the override is reserved for the Hub");
799
799
  anti("PASSWORD_STORE", RE_PASSWORD, "password storage or hashing — Zitadel owns credentials; the Hub creates sign-in users with a one-time temporary password");
800
800
  anti("EMAIL_ALLOWLIST", RE_ALLOWLIST, "email allowlist standing in for roles — declare a role and let the Hub grant it");
@@ -57,6 +57,28 @@ export declare function sharesWorktreeRoot(serviceSrc: string, repoRoot: string)
57
57
  export type PortOwnership = "owned" | "foreign" | "dead";
58
58
  export declare function classifyPortListener(listenerPid: number | null, sessionPids: ReadonlySet<number>): PortOwnership;
59
59
  export declare function collectSessionPids(panePids: number[], psOut: string): Set<number>;
60
+ export type PaneLiveness = {
61
+ state: "running";
62
+ } | {
63
+ state: "exited";
64
+ exitCode: number | null;
65
+ lastActivityEpoch: number | null;
66
+ } | {
67
+ state: "unknown";
68
+ reason: string;
69
+ };
70
+ export type PaneProcessQuery = (command: string, args: string[]) => string;
71
+ export declare function paneProcessQuery(command: string, args: string[]): string;
72
+ export declare function listSessionWindowNames(sessionName: string, query: PaneProcessQuery): Set<string> | null;
73
+ export declare function classifyPaneLiveness(target: string, query: PaneProcessQuery): PaneLiveness;
74
+ export declare function classifySessionServices(sessionName: string, services: Record<string, {
75
+ command: string[];
76
+ }>, query: PaneProcessQuery): Map<string, PaneLiveness>;
77
+ export declare function describePaneExit(liveness: Extract<PaneLiveness, {
78
+ state: "exited";
79
+ }>): string;
80
+ export declare function servicesLivenessToResult(liveness: ReadonlyMap<string, PaneLiveness>): CheckResult;
81
+ export declare const servicesCheck: Check;
60
82
  export declare const tmuxCheck: Check;
61
83
  export declare const portsCheck: Check;
62
84
  export declare function isConfigStale(configMtimeMs: number, startedAtIso: string): boolean;
@@ -5,6 +5,7 @@ import * as path from "node:path";
5
5
  import { isLinkedDependencyDir, isPortFree } from "./dev.js";
6
6
  import { appUsesMeshPackages, probeRegistryToken, registryLoginFix, } from "../utils/auth-preflight.js";
7
7
  import { probeCredentials, isRemoteEnvironment } from "./login.js";
8
+ import { envLeaksCheck } from "./dev-env-leaks.js";
8
9
  export function aggregateStatus(results) {
9
10
  if (results.some((r) => r.status === "error"))
10
11
  return "error";
@@ -262,6 +263,152 @@ function sessionProcessTree(sessionName) {
262
263
  }
263
264
  return collectSessionPids(panePids, psOut);
264
265
  }
266
+ export function paneProcessQuery(command, args) {
267
+ return execFileSync(command, args, {
268
+ encoding: "utf8",
269
+ stdio: ["ignore", "pipe", "ignore"],
270
+ });
271
+ }
272
+ export function listSessionWindowNames(sessionName, query) {
273
+ try {
274
+ const raw = query("tmux", ["list-windows", "-t", sessionName, "-F", "#{window_name}"]);
275
+ return new Set(raw
276
+ .trim()
277
+ .split("\n")
278
+ .map((line) => line.trim())
279
+ .filter(Boolean));
280
+ }
281
+ catch {
282
+ return null;
283
+ }
284
+ }
285
+ function parseOptionalNumber(field) {
286
+ if (field === undefined || field === "")
287
+ return null;
288
+ const n = Number(field);
289
+ return Number.isFinite(n) ? n : null;
290
+ }
291
+ export function classifyPaneLiveness(target, query) {
292
+ let raw;
293
+ try {
294
+ raw = query("tmux", [
295
+ "display",
296
+ "-p",
297
+ "-t",
298
+ target,
299
+ "#{pane_dead}|#{pane_dead_status}|#{pane_pid}|#{window_activity}",
300
+ ]);
301
+ }
302
+ catch {
303
+ return { state: "unknown", reason: `tmux could not report the pane at '${target}'` };
304
+ }
305
+ const [dead, deadStatus, pidStr, activityStr] = raw.trim().split("|");
306
+ const lastActivityEpoch = parseOptionalNumber(activityStr);
307
+ if (dead === "1") {
308
+ return { state: "exited", exitCode: parseOptionalNumber(deadStatus), lastActivityEpoch };
309
+ }
310
+ const pid = Number(pidStr);
311
+ if (!Number.isInteger(pid) || pid <= 0) {
312
+ return { state: "unknown", reason: `tmux reported no pane pid for '${target}'` };
313
+ }
314
+ let psOut;
315
+ try {
316
+ psOut = query("ps", ["-eo", "pid=,ppid="]);
317
+ }
318
+ catch {
319
+ return paneChildrenViaPgrep(pid, lastActivityEpoch, query);
320
+ }
321
+ return collectSessionPids([pid], psOut).size > 1
322
+ ? { state: "running" }
323
+ : { state: "exited", exitCode: null, lastActivityEpoch };
324
+ }
325
+ function paneChildrenViaPgrep(pid, lastActivityEpoch, query) {
326
+ try {
327
+ return query("pgrep", ["-P", String(pid)]).trim().length > 0
328
+ ? { state: "running" }
329
+ : { state: "exited", exitCode: null, lastActivityEpoch };
330
+ }
331
+ catch (err) {
332
+ if (err.status === 1) {
333
+ return { state: "exited", exitCode: null, lastActivityEpoch };
334
+ }
335
+ return { state: "unknown", reason: "could not read the process table (ps, pgrep)" };
336
+ }
337
+ }
338
+ export function classifySessionServices(sessionName, services, query) {
339
+ const windows = listSessionWindowNames(sessionName, query);
340
+ const result = new Map();
341
+ for (const [name, svc] of Object.entries(services)) {
342
+ if (!svc.command || svc.command.length === 0)
343
+ continue;
344
+ if (windows === null) {
345
+ result.set(name, {
346
+ state: "unknown",
347
+ reason: `could not list the windows of session '${sessionName}' (tmux)`,
348
+ });
349
+ continue;
350
+ }
351
+ if (!windows.has(name)) {
352
+ result.set(name, { state: "exited", exitCode: null, lastActivityEpoch: null });
353
+ continue;
354
+ }
355
+ result.set(name, classifyPaneLiveness(`${sessionName}:${name}`, query));
356
+ }
357
+ return result;
358
+ }
359
+ export function describePaneExit(liveness) {
360
+ if (liveness.exitCode !== null)
361
+ return `exit ${liveness.exitCode}`;
362
+ if (liveness.lastActivityEpoch !== null) {
363
+ return `no live process (last output ${new Date(liveness.lastActivityEpoch * 1000).toISOString()})`;
364
+ }
365
+ return "no live process";
366
+ }
367
+ export function servicesLivenessToResult(liveness) {
368
+ const total = liveness.size;
369
+ const dead = [];
370
+ const unknown = [];
371
+ for (const [name, l] of liveness.entries()) {
372
+ if (l.state === "exited")
373
+ dead.push([name, l]);
374
+ else if (l.state === "unknown")
375
+ unknown.push([name, l]);
376
+ }
377
+ if (dead.length === 0 && unknown.length === 0) {
378
+ return { status: "ok", summary: `all ${total} service process(es) running` };
379
+ }
380
+ if (dead.length > 0) {
381
+ return {
382
+ status: "error",
383
+ summary: `${dead.length} of ${total} service process(es) not running`,
384
+ detail: dead.map(([name, l]) => `${name} — ${describePaneExit(l)}`).join("; "),
385
+ remediation: dead.length === 1
386
+ ? `mesh dev logs ${dead[0][0]}`
387
+ : "mesh dev logs <svc> for each service above, then mesh dev restart <svc>",
388
+ };
389
+ }
390
+ return {
391
+ status: "warn",
392
+ summary: `could not determine liveness for ${unknown.length} of ${total} service(s)`,
393
+ detail: unknown.map(([name, l]) => `${name} — ${l.reason}`).join("; "),
394
+ };
395
+ }
396
+ export const servicesCheck = {
397
+ id: "services",
398
+ title: "Service processes",
399
+ phases: ["ondemand"],
400
+ async run(ctx) {
401
+ const services = ctx.sessionState?.devOutput.services;
402
+ if (!services || Object.keys(services).length === 0) {
403
+ return { status: "ok", summary: "no session — services start at launch" };
404
+ }
405
+ const liveness = classifySessionServices(ctx.sessionName, services, paneProcessQuery);
406
+ if (liveness.size === 0) {
407
+ return { status: "ok", summary: "no locally-run services in this session" };
408
+ }
409
+ return servicesLivenessToResult(liveness);
410
+ },
411
+ };
265
412
  export const tmuxCheck = {
266
413
  id: "tmux",
267
414
  title: "tmux",
@@ -533,9 +680,11 @@ export const ALL_CHECKS = [
533
680
  registryCheck,
534
681
  configStalenessCheck,
535
682
  portsCheck,
683
+ servicesCheck,
536
684
  worktreeCheck,
537
685
  tunnelsCheck,
538
686
  temporalCheck,
687
+ envLeaksCheck,
539
688
  ];
540
689
  export async function runDoctor(ctx, opts) {
541
690
  const results = await runChecks(ctx, "ondemand", ALL_CHECKS);