@mesh-tech/mesh-cli 0.15.0 → 0.17.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 (106) hide show
  1. package/dist/bin/mesh.js +11663 -10745
  2. package/dist/bin/mesh.js.map +4 -4
  3. package/dist/build-info.json +2 -2
  4. package/dist/src/commands/agent-api-client.d.ts +17 -0
  5. package/dist/src/commands/agent-api-client.d.ts.map +1 -1
  6. package/dist/src/commands/agent-api-client.js +22 -0
  7. package/dist/src/commands/agent-api-client.js.map +1 -1
  8. package/dist/src/commands/app-check.d.ts +97 -0
  9. package/dist/src/commands/app-check.d.ts.map +1 -0
  10. package/dist/src/commands/app-check.js +502 -0
  11. package/dist/src/commands/app-check.js.map +1 -0
  12. package/dist/src/commands/create-app.d.ts +24 -0
  13. package/dist/src/commands/create-app.d.ts.map +1 -1
  14. package/dist/src/commands/create-app.js +27 -1
  15. package/dist/src/commands/create-app.js.map +1 -1
  16. package/dist/src/commands/dev-doctor.d.ts +28 -12
  17. package/dist/src/commands/dev-doctor.d.ts.map +1 -1
  18. package/dist/src/commands/dev-doctor.js +16 -7
  19. package/dist/src/commands/dev-doctor.js.map +1 -1
  20. package/dist/src/commands/dev.d.ts +22 -0
  21. package/dist/src/commands/dev.d.ts.map +1 -1
  22. package/dist/src/commands/dev.js +124 -9
  23. package/dist/src/commands/dev.js.map +1 -1
  24. package/dist/src/commands/local/docker-runner.d.ts.map +1 -1
  25. package/dist/src/commands/local/docker-runner.js +2 -4
  26. package/dist/src/commands/local/docker-runner.js.map +1 -1
  27. package/dist/src/commands/local/hub-local.d.ts.map +1 -1
  28. package/dist/src/commands/local/hub-local.js +2 -3
  29. package/dist/src/commands/local/hub-local.js.map +1 -1
  30. package/dist/src/commands/local/seed-zitadel.d.ts.map +1 -1
  31. package/dist/src/commands/local/seed-zitadel.js +108 -109
  32. package/dist/src/commands/local/seed-zitadel.js.map +1 -1
  33. package/dist/src/commands/local/stack.d.ts.map +1 -1
  34. package/dist/src/commands/local/stack.js +4 -9
  35. package/dist/src/commands/local/stack.js.map +1 -1
  36. package/dist/src/commands/site.d.ts +3 -0
  37. package/dist/src/commands/site.d.ts.map +1 -0
  38. package/dist/src/commands/site.js +277 -0
  39. package/dist/src/commands/site.js.map +1 -0
  40. package/dist/src/commands/temporal.d.ts +25 -0
  41. package/dist/src/commands/temporal.d.ts.map +1 -1
  42. package/dist/src/commands/temporal.js +2459 -732
  43. package/dist/src/commands/temporal.js.map +7 -1
  44. package/dist/src/commands/vcs/clone.d.ts.map +1 -1
  45. package/dist/src/commands/vcs/clone.js +4 -4
  46. package/dist/src/commands/vcs/clone.js.map +1 -1
  47. package/dist/src/commands/vcs/common.d.ts +34 -0
  48. package/dist/src/commands/vcs/common.d.ts.map +1 -1
  49. package/dist/src/commands/vcs/common.js +52 -0
  50. package/dist/src/commands/vcs/common.js.map +1 -1
  51. package/dist/src/commands/vcs/index.d.ts.map +1 -1
  52. package/dist/src/commands/vcs/index.js +7 -1
  53. package/dist/src/commands/vcs/index.js.map +1 -1
  54. package/dist/src/commands/vcs/propose.d.ts +22 -0
  55. package/dist/src/commands/vcs/propose.d.ts.map +1 -1
  56. package/dist/src/commands/vcs/propose.js +25 -5
  57. package/dist/src/commands/vcs/propose.js.map +1 -1
  58. package/dist/src/commands/vcs/rm.d.ts +18 -0
  59. package/dist/src/commands/vcs/rm.d.ts.map +1 -0
  60. package/dist/src/commands/vcs/rm.js +88 -0
  61. package/dist/src/commands/vcs/rm.js.map +1 -0
  62. package/dist/src/program.d.ts.map +1 -1
  63. package/dist/src/program.js +4 -0
  64. package/dist/src/program.js.map +1 -1
  65. package/dist/src/utils/aws-auth.d.ts +10 -0
  66. package/dist/src/utils/aws-auth.d.ts.map +1 -1
  67. package/dist/src/utils/aws-auth.js +35 -0
  68. package/dist/src/utils/aws-auth.js.map +1 -1
  69. package/dist/src/utils/cache-home.d.ts +25 -0
  70. package/dist/src/utils/cache-home.d.ts.map +1 -0
  71. package/dist/src/utils/cache-home.js +36 -0
  72. package/dist/src/utils/cache-home.js.map +1 -0
  73. package/dist/src/utils/temporal-codec.js +207 -294
  74. package/dist/src/utils/temporal-codec.js.map +7 -1
  75. package/fragments/base/index.ts.hbs +1 -1
  76. package/fragments/service/api/package.json.hbs +1 -0
  77. package/fragments/service/api/src/index.ts.hbs +6 -1
  78. package/fragments/temporal/worker/package.json.hbs +1 -0
  79. package/fragments/temporal/worker/src/activities.ts.hbs +6 -5
  80. package/fragments/temporal/worker/src/worker.ts.hbs +7 -1
  81. package/package.json +12 -3
  82. package/skills/core/SKILL.md +15 -3
  83. package/templates/api-auth/AGENTS.md.hbs +6 -0
  84. package/templates/api-auth/README.md.hbs +17 -8
  85. package/templates/api-auth/api/package.json.hbs +2 -1
  86. package/templates/api-auth/api/{src → scripts}/seed-authz.ts.hbs +3 -3
  87. package/templates/api-auth/api/src/index.ts.hbs +20 -9
  88. package/templates/api-auth/index.ts.hbs +79 -25
  89. package/templates/api-auth/package.json.hbs +2 -0
  90. package/templates/api-role-gating/api/package.json.hbs +1 -0
  91. package/templates/api-role-gating/api/src/main.ts.hbs +6 -1
  92. package/templates/api-role-gating/index.ts.hbs +1 -1
  93. package/templates/api-role-gating/package.json.hbs +1 -0
  94. package/templates/external-service/api/package.json.hbs +2 -0
  95. package/templates/external-service/api/src/index.ts.hbs +7 -2
  96. package/templates/external-service/index.ts.hbs +1 -1
  97. package/templates/external-service/package.json.hbs +1 -0
  98. package/templates/workflow/api/package.json.hbs +1 -0
  99. package/templates/workflow/api/src/index.ts.hbs +10 -5
  100. package/templates/workflow/index.ts.hbs +1 -1
  101. package/templates/workflow/package.json.hbs +1 -0
  102. package/templates/workflow/worker/package.json.hbs +1 -0
  103. package/templates/workflow/worker/src/activities.ts.hbs +7 -3
  104. package/templates/workflow/worker/src/encryption-codec.ts +3 -2
  105. package/templates/workflow/worker/src/worker.ts.hbs +7 -1
  106. package/templates/workflow/worker/src/workflows.ts.hbs +3 -3
@@ -0,0 +1,502 @@
1
+ /**
2
+ * `mesh app check` — the Mesh app contract, enforced.
3
+ *
4
+ * Scans a tenant-apps checkout and reports every mechanical violation of the
5
+ * contract in `@mesh-tech/app-kit`'s `apps` skill
6
+ * (`skills/apps/references/app-contract.md`): a browser UI served from inside
7
+ * the API process, an app that never registers with the Hub, app-written
8
+ * browser sessions where the platform auth proxy belongs, logging that
9
+ * bypasses `@mesh-tech/logger`, and an app whose people, roles or keys the
10
+ * Hub cannot see. `mesh create-app` runs it on every scaffold, tenant CI runs
11
+ * it beside `mesh skills sync --check`, and the platform reviewer runs the
12
+ * same command — one detector, one exit code.
13
+ *
14
+ * Three layers, like `mesh dev doctor`:
15
+ * 1. Pure scanners (`scanApp`, `scanRepo`) — filesystem in, `Finding[]` out.
16
+ * 2. One `Check` per contract code, on the dev-doctor engine (`runChecks`).
17
+ * 3. The command: resolves the repo root, picks the apps, renders, exits.
18
+ *
19
+ * The patterns are deliberately shape-based: `apps/<app>/index.ts` (the Pulumi
20
+ * program) is the contract's single point of truth, and a service directory is
21
+ * a UI if its name says so. A hit is a fact to READ, not a verdict — the ruling
22
+ * detail and every gate's owning primitive live in the contract reference.
23
+ *
24
+ * @example
25
+ * ```bash
26
+ * mesh app check # every apps/* in this repo
27
+ * mesh app check apps/funding-portal # one app
28
+ * mesh app check --json # stable shape for CI
29
+ * ```
30
+ */
31
+ import * as fs from "node:fs";
32
+ import * as path from "node:path";
33
+ import { logError, logInfo } from "../utils/index.js";
34
+ import { MeshCliError, emitJsonPayload } from "../utils/errors.js";
35
+ import { resolveTargetRoot } from "./skills.js";
36
+ import { ICONS, aggregateStatus, jsonReport, renderHuman, runChecks, } from "./dev-doctor.js";
37
+ export const APP_CHECK_RULES = {
38
+ UI_IN_API: {
39
+ gate: "0.1",
40
+ title: "UI is its own service",
41
+ severity: "block",
42
+ remediation: 'move the pages to apps/<app>/ui and declare new mesh.apps.Service("ui", { env, src: "./ui", … })',
43
+ scope: "app",
44
+ },
45
+ NO_UI_SERVICE: {
46
+ gate: "0.1",
47
+ title: "UI service declared",
48
+ severity: "block",
49
+ remediation: 'add apps/<app>/ui + new mesh.apps.Service("ui", …) in index.ts',
50
+ scope: "app",
51
+ },
52
+ NO_REGISTER: {
53
+ gate: "0.2",
54
+ title: "App registers with the Hub",
55
+ severity: "block",
56
+ remediation: "end index.ts with `export const app = env.register({ description })`",
57
+ scope: "app",
58
+ },
59
+ CUSTOM_SESSION_AUTH: {
60
+ gate: "0.3",
61
+ title: "Sign-in through the platform",
62
+ severity: "block",
63
+ remediation: 'delete the OIDC + cookie code and protect the UI service with auth: { provider: "mesh" }',
64
+ scope: "app",
65
+ },
66
+ NON_PLATFORM_LOGGER: {
67
+ gate: "0.4",
68
+ title: "Logs through @mesh-tech/logger",
69
+ severity: "block",
70
+ remediation: "import { createLogger } from \"@mesh-tech/logger\" and log through it (requestLogger for HTTP)",
71
+ scope: "app",
72
+ },
73
+ LOCAL_AUTH_LIB: {
74
+ gate: "0.5",
75
+ title: "No tenant-local auth library",
76
+ severity: "advisory",
77
+ remediation: "check whether @mesh-tech/authn already owns this; if the platform lacks it, file a platform item",
78
+ scope: "repo",
79
+ },
80
+ NO_AUTHZ_POINTER: {
81
+ gate: "H",
82
+ title: "Access surfaced through the Hub",
83
+ severity: "block",
84
+ remediation: 'publish ops-hub metadata and end index.ts with new mesh.auth.AppAuthzPointer("authz", { env, mode, opsHubMetadataRef })',
85
+ scope: "app",
86
+ },
87
+ METADATA_UNPUBLISHED: {
88
+ gate: "H",
89
+ title: "Ops-hub metadata published",
90
+ severity: "block",
91
+ remediation: "export compileOpsHubMetadata(schemaDef) with mesh.exports.Export (role-gating) or let SpiceDBSchema publish it (policy-engine)",
92
+ scope: "app",
93
+ },
94
+ IAC_GRANTS: {
95
+ gate: "H",
96
+ title: "No role grants in IaC",
97
+ severity: "block",
98
+ remediation: "remove the per-user grant map; grant roles from the Hub (App → Access → People)",
99
+ scope: "app",
100
+ },
101
+ PASSWORD_STORE: {
102
+ gate: "H",
103
+ title: "No app-side credentials",
104
+ severity: "block",
105
+ remediation: "delete the password code; the Hub creates sign-in users with a one-time temporary password",
106
+ scope: "app",
107
+ },
108
+ EMAIL_ALLOWLIST: {
109
+ gate: "H",
110
+ title: "No email allowlist for roles",
111
+ severity: "block",
112
+ remediation: "declare a role on the ZitadelAppIdentity and let the Hub grant it",
113
+ scope: "app",
114
+ },
115
+ USERS_TABLE: {
116
+ gate: "H",
117
+ title: "No hand-rolled users/roles table",
118
+ severity: "block",
119
+ remediation: "drop the table; people and roles live in Zitadel and are administered from the Hub",
120
+ scope: "app",
121
+ },
122
+ };
123
+ export const APP_CHECK_CODES = Object.keys(APP_CHECK_RULES);
124
+ // ============================================================================
125
+ // Filesystem helpers (pure over a root)
126
+ // ============================================================================
127
+ const SRC_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".jsx"]);
128
+ const SKIP_DIRS = new Set(["node_modules", "dist", "build", ".generated", "test", "tests", "__tests__", "fixtures", "scripts"]);
129
+ /** Directories under an app that are not services (docs, tooling, dev mocks, migrations). */
130
+ const NON_SERVICE_DIRS = new Set([
131
+ "docs",
132
+ "scripts",
133
+ "lib",
134
+ "libs",
135
+ "migrations",
136
+ "test",
137
+ "tests",
138
+ "node_modules",
139
+ "infra",
140
+ "mocks",
141
+ "skills",
142
+ "prisma",
143
+ ]);
144
+ function rel(root, abs) {
145
+ return path.relative(root, abs).split(path.sep).join("/");
146
+ }
147
+ function isDir(p) {
148
+ try {
149
+ return fs.statSync(p).isDirectory();
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
155
+ function readText(p) {
156
+ try {
157
+ return fs.readFileSync(p, "utf8");
158
+ }
159
+ catch {
160
+ return "";
161
+ }
162
+ }
163
+ /** Source files worth scanning: TS/JS minus tests, fixtures, generated output, tooling and dependencies. */
164
+ export function srcFiles(dir) {
165
+ const out = [];
166
+ const walk = (d) => {
167
+ let entries;
168
+ try {
169
+ entries = fs.readdirSync(d, { withFileTypes: true });
170
+ }
171
+ catch {
172
+ return;
173
+ }
174
+ for (const e of entries) {
175
+ const p = path.join(d, e.name);
176
+ if (e.isDirectory()) {
177
+ if (SKIP_DIRS.has(e.name) || e.name.startsWith("scenario"))
178
+ continue;
179
+ walk(p);
180
+ continue;
181
+ }
182
+ if (!e.isFile())
183
+ continue;
184
+ if (!SRC_EXT.has(path.extname(e.name)))
185
+ continue;
186
+ if (/\.(test|spec)\./.test(e.name) || /\.config\./.test(e.name) || e.name === "build.mjs")
187
+ continue;
188
+ out.push(p);
189
+ }
190
+ };
191
+ walk(dir);
192
+ return out.sort();
193
+ }
194
+ /** A `test-*` / `*-test` directory is a test harness (a browser fixture, a smoke client), not a deployed service. */
195
+ function isTestHarness(name) {
196
+ return name.startsWith("test-") || name.endsWith("-test");
197
+ }
198
+ /** A service directory whose NAME says "browser UI". */
199
+ export function isUiDir(name) {
200
+ return ["ui", "web", "frontend", "client", "portal-ui", "app"].includes(name) || name.endsWith("-ui");
201
+ }
202
+ /** Service directories of an app: every child dir that is not docs/tooling. */
203
+ function serviceDirs(app) {
204
+ let entries;
205
+ try {
206
+ entries = fs.readdirSync(app, { withFileTypes: true });
207
+ }
208
+ catch {
209
+ return [];
210
+ }
211
+ return entries
212
+ .filter((e) => e.isDirectory() && !NON_SERVICE_DIRS.has(e.name) && !e.name.startsWith(".") && !isTestHarness(e.name))
213
+ .map((e) => path.join(app, e.name))
214
+ .sort();
215
+ }
216
+ /**
217
+ * Grep-like scan of an app for the authorization gate: every `.ts/.tsx/.yaml/.sql`
218
+ * outside dependencies, build output and tests, with comment lines dropped —
219
+ * a comment is not a surface. Returns `path:line` + the line for each match.
220
+ */
221
+ function authzSurface(app) {
222
+ const hits = [];
223
+ const walk = (d) => {
224
+ let entries;
225
+ try {
226
+ entries = fs.readdirSync(d, { withFileTypes: true });
227
+ }
228
+ catch {
229
+ return;
230
+ }
231
+ for (const e of entries) {
232
+ const p = path.join(d, e.name);
233
+ if (e.isDirectory()) {
234
+ if (["node_modules", "dist", "build"].includes(e.name))
235
+ continue;
236
+ walk(p);
237
+ continue;
238
+ }
239
+ if (!e.isFile())
240
+ continue;
241
+ if (!/\.(ts|tsx|yaml|sql)$/.test(e.name) || /\.(test|spec)\.tsx?$/.test(e.name) || e.name.endsWith(".d.ts"))
242
+ continue;
243
+ const lines = readText(p).split("\n");
244
+ lines.forEach((text, i) => {
245
+ if (/^\s*(\/\/|\*|\/\*|#|--)/.test(text))
246
+ return;
247
+ hits.push({ file: p, line: i + 1, text });
248
+ });
249
+ }
250
+ };
251
+ walk(app);
252
+ return hits;
253
+ }
254
+ // ============================================================================
255
+ // Scanners
256
+ // ============================================================================
257
+ const RE_HTML = /from ["']hono\/(html|jsx)["']|react-dom\/server|\bc\.html\(|<!doctype html|<!DOCTYPE html/;
258
+ const RE_OIDC = /openid-client|code_verifier|authorization_code|buildAuthorizeUrl|end_session/;
259
+ const RE_COOKIE = /setCookie\(|hono\/cookie|Set-Cookie/;
260
+ const RE_LOGGER = /from ["'](pino|hono\/logger|winston|bunyan)["']|require\(["'](pino|winston|bunyan)["']\)|^[^/*]*\bconsole\.(log|info|warn|error)\(/;
261
+ const RE_EXPOSURE = /ZitadelAppIdentity|createOAuthMiddleware|createAuthorizationMiddleware|zitadelJwtScheme|createAuthzMiddleware|bearerAuth|type: *"oidc"|^\s+auth: *\{|_ROLES *= *\{|coarseRoles|ZITADEL_(AUDIENCE|CLIENT_ID)|OIDC_CLIENT_ID|zitadel\.(ApplicationOidc|ApplicationApi|Project|UserGrant)\(/;
262
+ const RE_POINTER = /AppAuthzPointer/;
263
+ const RE_METADATA = /compileOpsHubMetadata|ops-hub-metadata|SpiceDBSchema/;
264
+ const RE_IAC_GRANT = /userId: *"[0-9]{6,}"/;
265
+ const RE_PASSWORD = /(^|[^a-z])(bcrypt|argon2)|password_hash|passwordHash|hashPassword|temporaryPassword.*(send|mail)|(send|mail).*temporaryPassword/;
266
+ const RE_ALLOWLIST = /ADMIN_EMAILS|ALLOWED_EMAILS|allowedEmails|adminEmails/;
267
+ const RE_USERS_TABLE = /create table (if not exists )?"?(users|roles|user_roles|permissions)"?/i;
268
+ /** Apps in a checkout: every `apps/<name>` with a Pulumi program, or the root when it is itself an app. */
269
+ export function listApps(root) {
270
+ const apps = [];
271
+ const appsDir = path.join(root, "apps");
272
+ if (isDir(appsDir)) {
273
+ for (const e of fs.readdirSync(appsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
274
+ if (!e.isDirectory())
275
+ continue;
276
+ const d = path.join(appsDir, e.name);
277
+ if (fs.existsSync(path.join(d, "index.ts")) || fs.existsSync(path.join(d, "Pulumi.yaml")))
278
+ apps.push(rel(root, d));
279
+ }
280
+ }
281
+ if (fs.existsSync(path.join(root, "index.ts")) && fs.existsSync(path.join(root, "Pulumi.yaml")))
282
+ apps.push(".");
283
+ return apps;
284
+ }
285
+ /**
286
+ * Every app-scoped finding for one app (`appRel` is repo-relative). An app
287
+ * directory with no Pulumi program yields nothing — it is not an app.
288
+ */
289
+ export function scanApp(root, appRel) {
290
+ const app = path.resolve(root, appRel);
291
+ const index = path.join(app, "index.ts");
292
+ const findings = [];
293
+ const hit = (code, p, why) => {
294
+ findings.push({ code, path: p, why });
295
+ };
296
+ const hasProgram = fs.existsSync(index) || fs.existsSync(path.join(app, "Pulumi.yaml"));
297
+ if (!hasProgram)
298
+ return findings;
299
+ // ── Check 0 (contract gates 0.1–0.4) need the Pulumi program ─────────────
300
+ if (fs.existsSync(index)) {
301
+ const indexRel = rel(root, index);
302
+ let servesHtml = false;
303
+ let hasUiService = false;
304
+ for (const svc of serviceDirs(app)) {
305
+ const svcName = path.basename(svc);
306
+ if (isUiDir(svcName)) {
307
+ hasUiService = true;
308
+ continue;
309
+ }
310
+ for (const f of srcFiles(svc)) {
311
+ let why = "";
312
+ if (/\.(tsx|jsx)$/.test(f))
313
+ why = `JSX view file inside the ${svcName} service`;
314
+ else if (RE_HTML.test(readText(f)))
315
+ why = `renders HTML inside the ${svcName} service`;
316
+ if (why) {
317
+ servesHtml = true;
318
+ hit("UI_IN_API", rel(root, f), `${why} — a browser UI is its own mesh.apps.Service (apps/<app>/ui)`);
319
+ }
320
+ }
321
+ }
322
+ if (servesHtml && !hasUiService) {
323
+ hit("NO_UI_SERVICE", indexRel, 'the app serves HTML but declares no ui/ service — add apps/<app>/ui + new mesh.apps.Service("ui", …)');
324
+ }
325
+ if (!/env\.register\(|\.annotate\(/.test(readText(index))) {
326
+ hit("NO_REGISTER", indexRel, "never calls env.register({…}) — the Hub cannot list this app");
327
+ }
328
+ for (const svc of serviceDirs(app)) {
329
+ for (const f of srcFiles(svc)) {
330
+ const text = readText(f);
331
+ if (RE_OIDC.test(text) && RE_COOKIE.test(text)) {
332
+ hit("CUSTOM_SESSION_AUTH", rel(root, f), 'app-written OIDC + cookie session — protect the UI service with auth: { provider: "mesh" } instead');
333
+ }
334
+ }
335
+ }
336
+ for (const svc of serviceDirs(app)) {
337
+ for (const f of srcFiles(svc)) {
338
+ const lines = readText(f).split("\n");
339
+ const i = lines.findIndex((l) => RE_LOGGER.test(l) && !l.includes("mesh-tech/logger"));
340
+ if (i === -1)
341
+ continue;
342
+ hit("NON_PLATFORM_LOGGER", `${rel(root, f)}:${i + 1}`, `logs outside @mesh-tech/logger (${lines[i]?.trim() ?? ""})`);
343
+ }
344
+ }
345
+ }
346
+ // ── Check H (authorization gate) ─────────────────────────────────────────
347
+ const surface = authzSurface(app);
348
+ const first = (re) => surface.find((h) => re.test(h.text));
349
+ const exposure = first(RE_EXPOSURE);
350
+ const pointer = first(RE_POINTER);
351
+ const metadata = first(RE_METADATA);
352
+ const appLabel = appRel === "." ? "(root)" : path.basename(appRel);
353
+ if (exposure && !pointer) {
354
+ const at = surface.find((h) => RE_EXPOSURE.test(h.text) && /(^|\/)index\.ts$/.test(h.file)) ?? exposure;
355
+ hit("NO_AUTHZ_POINTER", rel(root, at.file), `${appLabel} authenticates callers or declares roles but publishes no mesh.auth.AppAuthzPointer — the Hub's Access tab is blank for this app`);
356
+ }
357
+ else if (exposure && pointer && !metadata) {
358
+ hit("METADATA_UNPUBLISHED", rel(root, pointer.file), "AppAuthzPointer present but no compiled ops-hub metadata (compileOpsHubMetadata export or SpiceDBSchema) — the Hub's create-key and role catalog 409 METADATA_UNPUBLISHED");
359
+ }
360
+ const anti = (code, re, why) => {
361
+ const files = [...new Set(surface.filter((h) => re.test(h.text)).map((h) => h.file))].sort().slice(0, 3);
362
+ for (const f of files)
363
+ hit(code, rel(root, f), why);
364
+ };
365
+ anti("IAC_GRANTS", RE_IAC_GRANT, "per-user role grant baked into IaC — grants belong in the Hub (Access → People), not in a config redeploy");
366
+ anti("PASSWORD_STORE", RE_PASSWORD, "password storage or hashing — Zitadel owns credentials; the Hub creates sign-in users with a one-time temporary password");
367
+ anti("EMAIL_ALLOWLIST", RE_ALLOWLIST, "email allowlist standing in for roles — declare a role and let the Hub grant it");
368
+ anti("USERS_TABLE", RE_USERS_TABLE, "hand-rolled users/roles table — people and roles live in Zitadel and are administered from the Hub");
369
+ return findings;
370
+ }
371
+ /** True when `dir/package.json` names a `@mesh-tech/*` package (a checkout of the platform itself). */
372
+ function isPlatformPackage(dir) {
373
+ try {
374
+ const pkg = JSON.parse(readText(path.join(dir, "package.json")));
375
+ return typeof pkg.name === "string" && pkg.name.startsWith("@mesh-tech/");
376
+ }
377
+ catch {
378
+ return false;
379
+ }
380
+ }
381
+ /** Repo-scoped findings: a tenant-local auth library that may duplicate `@mesh-tech/authn` (advisory). */
382
+ export function scanRepo(root) {
383
+ const findings = [];
384
+ for (const parent of ["libs", "packages"]) {
385
+ const dir = path.join(root, parent);
386
+ if (!isDir(dir))
387
+ continue;
388
+ for (const e of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
389
+ if (!e.isDirectory() || !e.name.includes("auth"))
390
+ continue;
391
+ const lib = path.join(dir, e.name);
392
+ // The platform's own packages (`@mesh-tech/authn`, `@mesh-tech/authz`) are the
393
+ // canon, not a tenant-local copy of it — the rule is about duplicates.
394
+ if (isPlatformPackage(lib))
395
+ continue;
396
+ const verifies = srcFiles(lib)
397
+ .filter((f) => f.endsWith(".ts"))
398
+ .some((f) => /introspect|jwtVerify|createRemoteJWKSet|from ["']jose["']/.test(readText(f)));
399
+ if (verifies) {
400
+ findings.push({
401
+ code: "LOCAL_AUTH_LIB",
402
+ path: rel(root, lib),
403
+ why: "tenant-local token verification — check whether @mesh-tech/authn already owns this",
404
+ });
405
+ }
406
+ }
407
+ }
408
+ return findings;
409
+ }
410
+ /** Turn a rule + its findings into the doctor-engine result shape. */
411
+ export function resultFor(code, findings) {
412
+ const rule = APP_CHECK_RULES[code];
413
+ const mine = findings.filter((f) => f.code === code);
414
+ if (mine.length === 0)
415
+ return { status: "ok", summary: `gate ${rule.gate}: no ${code} finding` };
416
+ return {
417
+ status: rule.severity === "block" ? "error" : "warn",
418
+ summary: `gate ${rule.gate}: ${code} × ${mine.length}`,
419
+ detail: mine.map((f) => `${f.path} — ${f.why}`).join("\n "),
420
+ remediation: rule.remediation,
421
+ };
422
+ }
423
+ /** The `Check`s for one scope: every app-scoped rule for an app, every repo-scoped rule for the repo. */
424
+ export function appContractChecks(scope) {
425
+ return APP_CHECK_CODES.filter((code) => APP_CHECK_RULES[code].scope === scope).map((code) => ({
426
+ id: code,
427
+ title: `${APP_CHECK_RULES[code].title} (${APP_CHECK_RULES[code].gate})`,
428
+ phases: ["ondemand", "preflight"],
429
+ run: async (ctx) => resultFor(code, ctx.findings),
430
+ }));
431
+ }
432
+ /**
433
+ * Run the contract over a checkout. `apps` narrows the run to the named
434
+ * repo-relative app dirs (an unknown dir is an error, not a silent pass).
435
+ */
436
+ export async function runAppCheck(root, apps) {
437
+ if (!isDir(root))
438
+ throw new MeshCliError(`no such checkout: ${root}`);
439
+ const targets = apps && apps.length > 0 ? apps.map((a) => a.replace(/\/+$/, "")) : listApps(root);
440
+ for (const a of targets) {
441
+ if (!isDir(path.resolve(root, a)))
442
+ throw new MeshCliError(`no such app dir: ${a}`);
443
+ }
444
+ const scopes = [];
445
+ for (const app of targets) {
446
+ const results = await runChecks({ root, app, findings: scanApp(root, app) }, "ondemand", appContractChecks("app"));
447
+ scopes.push({ scope: app, ...jsonReport(results) });
448
+ }
449
+ const repoResults = await runChecks({ root, app: null, findings: scanRepo(root) }, "ondemand", appContractChecks("repo"));
450
+ scopes.push({ scope: "(repo)", ...jsonReport(repoResults) });
451
+ return { root, status: aggregateStatus(scopes.map((s) => ({ status: s.status, summary: "" }))), scopes };
452
+ }
453
+ /** Human rendering: a header per scope, the doctor lines under it, failures only unless `verbose`. */
454
+ export function renderReport(report, opts) {
455
+ const out = [];
456
+ for (const scope of report.scopes) {
457
+ const shown = opts.verbose ? scope.checks : scope.checks.filter((c) => c.status !== "ok");
458
+ if (shown.length === 0 && !opts.verbose)
459
+ continue;
460
+ out.push(`${ICONS[scope.status]} ${scope.scope}`);
461
+ out.push(renderHuman(shown.map((c) => ({
462
+ check: { id: c.id, title: `${c.id} — ${APP_CHECK_RULES[c.id]?.title ?? c.id}` },
463
+ result: { status: c.status, summary: c.summary, remediation: c.remediation ?? undefined, detail: c.detail ?? undefined },
464
+ })))
465
+ .split("\n")
466
+ .map((l) => ` ${l}`)
467
+ .join("\n"));
468
+ }
469
+ out.push(`${ICONS[report.status]} app contract: ${report.status}`);
470
+ return out.filter(Boolean).join("\n");
471
+ }
472
+ // ============================================================================
473
+ // Command
474
+ // ============================================================================
475
+ export function registerAppCommands(program) {
476
+ const app = program.command("app").description("Inspect a tenant app against the Mesh app contract");
477
+ app
478
+ .command("check")
479
+ .description("Check tenant apps against the Mesh app contract (@mesh-tech/app-kit skills/apps/references/app-contract.md): a UI is its own service, the app registers with the Hub, sign-in goes through the platform proxy, logs go through @mesh-tech/logger, and people/roles/keys are surfaced through the Hub. Exit 1 on any blocking finding — the same gate mesh create-app, tenant CI and the platform reviewer run.")
480
+ .argument("[apps...]", "repo-relative app dirs to check (default: every apps/* with a Pulumi program)")
481
+ .option("--root <dir>", "checkout to scan (default: the enclosing git root)")
482
+ .option("--json", "machine-readable output (one report per app plus the repo-scoped checks)", false)
483
+ .option("--verbose", "also print the gates that passed", false)
484
+ .action(async (apps, opts) => {
485
+ const root = path.resolve(opts.root ?? resolveTargetRoot());
486
+ const report = await runAppCheck(root, apps);
487
+ if (opts.json) {
488
+ emitJsonPayload({ ok: report.status !== "error", ...report });
489
+ }
490
+ else {
491
+ if (report.scopes.length === 1)
492
+ logInfo(`no apps/* with a Pulumi program under ${root}`);
493
+ console.log(renderReport(report, { verbose: opts.verbose }));
494
+ }
495
+ if (report.status === "error") {
496
+ if (!opts.json)
497
+ logError("the app contract is not met — every ✗ above names the gate, the file and the replacement");
498
+ process.exitCode = 1;
499
+ }
500
+ });
501
+ }
502
+ //# sourceMappingURL=app-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-check.js","sourceRoot":"","sources":["../../../src/commands/app-check.ts"],"names":[],"mappings":"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"}
@@ -70,5 +70,29 @@ export declare function copyTemplate(srcDir: string, destDir: string, context: R
70
70
  * Resolve output directory for the app
71
71
  */
72
72
  export declare function resolveAppDir(cwd: string, tenant: string, name: string, test: boolean): string;
73
+ /**
74
+ * Generate a composable app from fragments
75
+ */
76
+ export declare function generateComposableApp(appDir: string, context: {
77
+ name: string;
78
+ tenant: string;
79
+ service: boolean;
80
+ database: boolean;
81
+ temporal: boolean;
82
+ bucket: boolean;
83
+ region: string;
84
+ /** True when scaffolding inside the mesh-platform monorepo → `@mesh-tech/*` = `workspace:*`. */
85
+ workspaceDeps: boolean;
86
+ /** Caret range for the lockstep `@mesh-tech/*` group (standalone repos only). */
87
+ meshRange: string;
88
+ /** Caret range for `@mesh-tech/esm-bundle`, which versions independently. */
89
+ esmBundleRange: string;
90
+ /** Literal toolchain devDep ranges for the standalone branch (no `catalog:`). */
91
+ typesNodeRange: string;
92
+ tsxRange: string;
93
+ typescriptRange: string;
94
+ /** Fully-resolved `mesh:deployerRole` ARN (or an `ACCOUNT_ID` placeholder). */
95
+ deployerRoleArn: string;
96
+ }): void;
73
97
  export declare function registerCreateAppCommand(program: Command): void;
74
98
  //# sourceMappingURL=create-app.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../../../src/commands/create-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyDpC,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc7D;AAkBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ5D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAwCvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAiC/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/E;AAwFD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAiCN;AAkBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,MAAM,CAkCR;AAgFD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8E/D"}
1
+ {"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../../../src/commands/create-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0DpC,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc7D;AAkBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ5D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAwCvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAiC/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/E;AAwFD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAiCN;AAkBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,MAAM,CAkCR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,aAAa,EAAE,OAAO,CAAC;IACvB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,eAAe,EAAE,MAAM,CAAC;CACzB,GACA,IAAI,CAkDN;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8E/D"}
@@ -16,6 +16,7 @@ import Handlebars from "handlebars";
16
16
  import { parse as parseYaml } from "yaml";
17
17
  import { logInfo, logSuccess, logError, logWarn } from "../utils/index.js";
18
18
  import { INTENT_RANGE, resolveTargetRoot, seedAppSkill, syncSkills } from "./skills.js";
19
+ import { renderReport, runAppCheck } from "./app-check.js";
19
20
  import { findPackageRoot } from "./local/stack.js";
20
21
  import { SCAFFOLD_TOOLCHAIN, resolvePublishedRange } from "../utils/scaffold-versions.js";
21
22
  // Get package root by walking up until we find package.json with our package name.
@@ -368,7 +369,7 @@ export function resolveAppDir(cwd, tenant, name, test) {
368
369
  /**
369
370
  * Generate a composable app from fragments
370
371
  */
371
- function generateComposableApp(appDir, context) {
372
+ export function generateComposableApp(appDir, context) {
372
373
  const fragmentsDir = path.join(packageRoot, "fragments");
373
374
  // 1. Always copy base fragment
374
375
  const baseDir = path.join(fragmentsDir, "base");
@@ -514,6 +515,7 @@ async function runLegacyTemplate(tenant, name, template, test) {
514
515
  process.exit(1);
515
516
  }
516
517
  autoSyncSkills(appDir, name);
518
+ await checkScaffoldContract(appDir);
517
519
  printLegacyNextSteps(appDir, template);
518
520
  }
519
521
  /** Best-effort skills sync after scaffolding (design D1): a fresh Claude
@@ -538,6 +540,29 @@ function autoSyncSkills(appDir, appName) {
538
540
  logWarn(`App skill stub skipped: ${err instanceof Error ? err.message : err}`);
539
541
  }
540
542
  }
543
+ /**
544
+ * Run `mesh app check` on the app just scaffolded. A fresh scaffold satisfies
545
+ * the Mesh app contract by construction (the template test proves it), so a
546
+ * finding here is a platform defect, not the developer's: report it loudly and
547
+ * fail the command so it cannot be missed, but leave the files in place.
548
+ */
549
+ async function checkScaffoldContract(appDir) {
550
+ const root = resolveTargetRoot(appDir);
551
+ const report = await runAppCheck(root, [path.relative(root, appDir) || "."]);
552
+ if (report.status === "ok") {
553
+ logSuccess("app contract: met (mesh app check)");
554
+ return;
555
+ }
556
+ console.log(renderReport(report, { verbose: false }));
557
+ if (report.status === "warn") {
558
+ logSuccess("app contract: met — the ⚠ lines above are advisories on the repo, not on this app (mesh app check)");
559
+ return;
560
+ }
561
+ if (report.status === "error") {
562
+ logError("the scaffold does not meet the Mesh app contract — this is a template defect; file a platform item and cite the ✗ lines above");
563
+ process.exitCode = 1;
564
+ }
565
+ }
541
566
  async function runComposable(tenant, name, primitives, test) {
542
567
  logInfo(`Creating app '${name}' for tenant '${tenant}'...`);
543
568
  logInfo(`Primitives: ${primitives.join(", ")}`);
@@ -607,6 +632,7 @@ async function runComposable(tenant, name, primitives, test) {
607
632
  }
608
633
  }
609
634
  autoSyncSkills(appDir, name);
635
+ await checkScaffoldContract(appDir);
610
636
  printComposableNextSteps(appDir, context);
611
637
  }
612
638
  function printLegacyNextSteps(appDir, _template) {