@dereekb/dbx-cli 13.11.18 → 13.12.1

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 (156) hide show
  1. package/firebase-api-manifest/main.js +70 -9
  2. package/firebase-api-manifest/package.json +1 -1
  3. package/firestore-indexes/src/firestore-indexes-generate.d.ts +125 -0
  4. package/firestore-indexes/src/firestore-model-identity-resolver.d.ts +93 -0
  5. package/firestore-indexes/src/firestore-query-helpers.d.ts +108 -0
  6. package/firestore-indexes/src/generate-firestore-indexes-cli.d.ts +94 -0
  7. package/firestore-indexes/src/index.d.ts +7 -0
  8. package/firestore-indexes/src/model-firebase-index-analyze.d.ts +68 -0
  9. package/firestore-indexes/src/model-firebase-index-build-manifest.d.ts +123 -0
  10. package/firestore-indexes/src/model-firebase-index-extract.d.ts +246 -0
  11. package/firestore-indexes/src/model-firebase-index-runtime.d.ts +126 -0
  12. package/firestore-indexes/src/model-firebase-index-scan-config-schema.d.ts +58 -0
  13. package/firestore-indexes/src/model-firebase-index-schema.d.ts +366 -0
  14. package/generate-firestore-indexes/main.js +1 -1
  15. package/generate-firestore-indexes/package.json +1 -1
  16. package/generate-mcp-manifest/main.js +1314 -0
  17. package/generate-mcp-manifest/package.json +6 -0
  18. package/generated/firebase-models.generated.d.ts +3 -0
  19. package/index.cjs.js +45614 -640
  20. package/index.esm.js +45321 -643
  21. package/lint-cache/main.js +32 -28
  22. package/lint-cache/package.json +2 -2
  23. package/manifest-extract/index.cjs.js +169 -4
  24. package/manifest-extract/index.esm.js +169 -4
  25. package/manifest-extract/package.json +1 -1
  26. package/manifest-extract/src/lib/types.d.ts +26 -1
  27. package/package.json +14 -10
  28. package/src/lib/index.d.ts +3 -0
  29. package/src/lib/manifest/types.d.ts +155 -0
  30. package/src/lib/mcp-scan/config/config-schema.d.ts +226 -0
  31. package/src/lib/mcp-scan/config/load-config.d.ts +63 -0
  32. package/src/lib/mcp-scan/index.d.ts +16 -0
  33. package/src/lib/mcp-scan/manifest/actions-loader.d.ts +49 -0
  34. package/src/lib/mcp-scan/manifest/actions-schema.d.ts +328 -0
  35. package/src/lib/mcp-scan/manifest/core-topics.d.ts +38 -0
  36. package/src/lib/mcp-scan/manifest/css-utilities-loader.d.ts +55 -0
  37. package/src/lib/mcp-scan/manifest/css-utilities-schema.d.ts +168 -0
  38. package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-loader.d.ts +33 -0
  39. package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +133 -0
  40. package/src/lib/mcp-scan/manifest/filters-loader.d.ts +61 -0
  41. package/src/lib/mcp-scan/manifest/filters-schema.d.ts +190 -0
  42. package/src/lib/mcp-scan/manifest/forge-fields-loader.d.ts +53 -0
  43. package/src/lib/mcp-scan/manifest/forge-fields-schema.d.ts +170 -0
  44. package/src/lib/mcp-scan/manifest/index.d.ts +43 -0
  45. package/src/lib/mcp-scan/manifest/load-actions-registry.d.ts +38 -0
  46. package/src/lib/mcp-scan/manifest/load-auth-registry.d.ts +82 -0
  47. package/src/lib/mcp-scan/manifest/load-css-utilities-registry.d.ts +67 -0
  48. package/src/lib/mcp-scan/manifest/load-dbx-docs-ui-examples-registry.d.ts +45 -0
  49. package/src/lib/mcp-scan/manifest/load-filters-registry.d.ts +69 -0
  50. package/src/lib/mcp-scan/manifest/load-forge-fields-registry.d.ts +70 -0
  51. package/src/lib/mcp-scan/manifest/load-model-firebase-index-registry.d.ts +61 -0
  52. package/src/lib/mcp-scan/manifest/load-model-snapshot-fields-registry.d.ts +74 -0
  53. package/src/lib/mcp-scan/manifest/load-pipes-registry.d.ts +69 -0
  54. package/src/lib/mcp-scan/manifest/load-registry.d.ts +76 -0
  55. package/src/lib/mcp-scan/manifest/load-tokens-registry.d.ts +69 -0
  56. package/src/lib/mcp-scan/manifest/load-ui-components-registry.d.ts +70 -0
  57. package/src/lib/mcp-scan/manifest/load-utils-registry.d.ts +73 -0
  58. package/src/lib/mcp-scan/manifest/loader.d.ts +120 -0
  59. package/src/lib/mcp-scan/manifest/manifest-loader-base.d.ts +130 -0
  60. package/src/lib/mcp-scan/manifest/model-firebase-index-loader.d.ts +53 -0
  61. package/src/lib/mcp-scan/manifest/model-snapshot-fields-loader.d.ts +54 -0
  62. package/src/lib/mcp-scan/manifest/model-snapshot-fields-schema.d.ts +127 -0
  63. package/src/lib/mcp-scan/manifest/pipes-loader.d.ts +54 -0
  64. package/src/lib/mcp-scan/manifest/pipes-schema.d.ts +125 -0
  65. package/src/lib/mcp-scan/manifest/semantic-types-schema.d.ts +108 -0
  66. package/src/lib/mcp-scan/manifest/tokens-loader.d.ts +55 -0
  67. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +116 -0
  68. package/src/lib/mcp-scan/manifest/ui-components-loader.d.ts +54 -0
  69. package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +149 -0
  70. package/src/lib/mcp-scan/manifest/utils-loader.d.ts +54 -0
  71. package/src/lib/mcp-scan/manifest/utils-schema.d.ts +120 -0
  72. package/src/lib/mcp-scan/registry/actions-runtime.d.ts +173 -0
  73. package/src/lib/mcp-scan/registry/archetypes.d.ts +235 -0
  74. package/src/lib/mcp-scan/registry/auth-builtin.d.ts +59 -0
  75. package/src/lib/mcp-scan/registry/auth-runtime.d.ts +343 -0
  76. package/src/lib/mcp-scan/registry/css-utilities-runtime.d.ts +133 -0
  77. package/src/lib/mcp-scan/registry/dbx-docs-ui-examples-runtime.d.ts +58 -0
  78. package/src/lib/mcp-scan/registry/downstream-models-runtime.d.ts +93 -0
  79. package/src/lib/mcp-scan/registry/filters-runtime.d.ts +128 -0
  80. package/src/lib/mcp-scan/registry/firebase-models.d.ts +387 -0
  81. package/src/lib/mcp-scan/registry/forge-fields.d.ts +101 -0
  82. package/src/lib/mcp-scan/registry/form-fields.d.ts +203 -0
  83. package/src/lib/mcp-scan/registry/index.d.ts +165 -0
  84. package/src/lib/mcp-scan/registry/model-snapshot-fields-runtime.d.ts +138 -0
  85. package/src/lib/mcp-scan/registry/pipes-runtime.d.ts +136 -0
  86. package/src/lib/mcp-scan/registry/reserved-model-folders.d.ts +29 -0
  87. package/src/lib/mcp-scan/registry/semantic-types.d.ts +81 -0
  88. package/src/lib/mcp-scan/registry/tokens-runtime.d.ts +96 -0
  89. package/src/lib/mcp-scan/registry/ui-components-runtime.d.ts +90 -0
  90. package/src/lib/mcp-scan/registry/utils-runtime.d.ts +136 -0
  91. package/src/lib/mcp-scan/scan/_jsdoc-tagged-export/extract-base.d.ts +245 -0
  92. package/src/lib/mcp-scan/scan/actions-build-manifest.d.ts +58 -0
  93. package/src/lib/mcp-scan/scan/actions-cli.d.ts +38 -0
  94. package/src/lib/mcp-scan/scan/actions-extract.d.ts +99 -0
  95. package/src/lib/mcp-scan/scan/actions-scan-config-schema.d.ts +42 -0
  96. package/src/lib/mcp-scan/scan/auth-extract.d.ts +127 -0
  97. package/src/lib/mcp-scan/scan/build-manifest.d.ts +76 -0
  98. package/src/lib/mcp-scan/scan/cli.d.ts +60 -0
  99. package/src/lib/mcp-scan/scan/css-utilities-build-manifest.d.ts +76 -0
  100. package/src/lib/mcp-scan/scan/css-utilities-cli.d.ts +36 -0
  101. package/src/lib/mcp-scan/scan/css-utilities-extract.d.ts +187 -0
  102. package/src/lib/mcp-scan/scan/css-utilities-scan-config-schema.d.ts +57 -0
  103. package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-build-manifest.d.ts +68 -0
  104. package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-cli.d.ts +20 -0
  105. package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +160 -0
  106. package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-scan-config-schema.d.ts +56 -0
  107. package/src/lib/mcp-scan/scan/discover-downstream-packages.d.ts +76 -0
  108. package/src/lib/mcp-scan/scan/discover-firebase-packages.d.ts +58 -0
  109. package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +105 -0
  110. package/src/lib/mcp-scan/scan/extract-models/collect-inherited.d.ts +22 -0
  111. package/src/lib/mcp-scan/scan/extract-models/find-converters.d.ts +19 -0
  112. package/src/lib/mcp-scan/scan/extract-models/find-enums.d.ts +19 -0
  113. package/src/lib/mcp-scan/scan/extract-models/find-identities.d.ts +25 -0
  114. package/src/lib/mcp-scan/scan/extract-models/find-interfaces.d.ts +31 -0
  115. package/src/lib/mcp-scan/scan/extract-models/find-model-groups.d.ts +21 -0
  116. package/src/lib/mcp-scan/scan/extract-models/find-service-factories.d.ts +19 -0
  117. package/src/lib/mcp-scan/scan/extract-models/find-sub-object-consts.d.ts +20 -0
  118. package/src/lib/mcp-scan/scan/extract-models/index.d.ts +74 -0
  119. package/src/lib/mcp-scan/scan/extract-models/infer-collection-kind.d.ts +22 -0
  120. package/src/lib/mcp-scan/scan/extract-models/service-factory-constants.d.ts +6 -0
  121. package/src/lib/mcp-scan/scan/extract-models/types.d.ts +171 -0
  122. package/src/lib/mcp-scan/scan/extract.d.ts +82 -0
  123. package/src/lib/mcp-scan/scan/filters-build-manifest.d.ts +78 -0
  124. package/src/lib/mcp-scan/scan/filters-cli.d.ts +37 -0
  125. package/src/lib/mcp-scan/scan/filters-extract.d.ts +101 -0
  126. package/src/lib/mcp-scan/scan/filters-scan-config-schema.d.ts +56 -0
  127. package/src/lib/mcp-scan/scan/forge-fields-build-manifest.d.ts +78 -0
  128. package/src/lib/mcp-scan/scan/forge-fields-cli.d.ts +37 -0
  129. package/src/lib/mcp-scan/scan/forge-fields-extract.d.ts +165 -0
  130. package/src/lib/mcp-scan/scan/forge-fields-scan-config-schema.d.ts +61 -0
  131. package/src/lib/mcp-scan/scan/index.d.ts +60 -0
  132. package/src/lib/mcp-scan/scan/model-firebase-index-cli.d.ts +22 -0
  133. package/src/lib/mcp-scan/scan/model-firebase-index-dispatcher-credit.d.ts +47 -0
  134. package/src/lib/mcp-scan/scan/model-firebase-index-reference-scan.d.ts +100 -0
  135. package/src/lib/mcp-scan/scan/model-snapshot-fields-build-manifest.d.ts +79 -0
  136. package/src/lib/mcp-scan/scan/model-snapshot-fields-cli.d.ts +37 -0
  137. package/src/lib/mcp-scan/scan/model-snapshot-fields-extract.d.ts +115 -0
  138. package/src/lib/mcp-scan/scan/model-snapshot-fields-scan-config-schema.d.ts +59 -0
  139. package/src/lib/mcp-scan/scan/pipes-build-manifest.d.ts +78 -0
  140. package/src/lib/mcp-scan/scan/pipes-cli.d.ts +37 -0
  141. package/src/lib/mcp-scan/scan/pipes-extract.d.ts +90 -0
  142. package/src/lib/mcp-scan/scan/pipes-scan-config-schema.d.ts +56 -0
  143. package/src/lib/mcp-scan/scan/scan-angular-io.d.ts +89 -0
  144. package/src/lib/mcp-scan/scan/scan-cli-base.d.ts +162 -0
  145. package/src/lib/mcp-scan/scan/scan-config-schema.d.ts +44 -0
  146. package/src/lib/mcp-scan/scan/ui-components-build-manifest.d.ts +78 -0
  147. package/src/lib/mcp-scan/scan/ui-components-cli.d.ts +37 -0
  148. package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +124 -0
  149. package/src/lib/mcp-scan/scan/ui-components-scan-config-schema.d.ts +62 -0
  150. package/src/lib/mcp-scan/scan/utils-build-manifest.d.ts +78 -0
  151. package/src/lib/mcp-scan/scan/utils-cli.d.ts +37 -0
  152. package/src/lib/mcp-scan/scan/utils-extract.d.ts +103 -0
  153. package/src/lib/mcp-scan/scan/utils-scan-config-schema.d.ts +57 -0
  154. package/test/package.json +9 -9
  155. package/index.cjs.default.js +0 -1
  156. package/index.cjs.mjs +0 -2
@@ -0,0 +1,1314 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __createRequire } from 'node:module';
3
+ const require = __createRequire(import.meta.url);
4
+
5
+ // packages/dbx-cli/generate-mcp-manifest/src/generate-mcp-manifest/main.ts
6
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
7
+ import { dirname, isAbsolute, relative as relative2, resolve } from "node:path";
8
+ import { createJiti } from "jiti";
9
+
10
+ // packages/util/src/lib/auth/auth.role.ts
11
+ var AUTH_TOS_SIGNED_ROLE = "tos";
12
+ var AUTH_ONBOARDED_ROLE = "onboarded";
13
+ var AUTH_ADMIN_ROLE = "admin";
14
+ var AUTH_USER_ROLE = "user";
15
+
16
+ // packages/model/src/lib/type/type.ts
17
+ import { type } from "arktype";
18
+ var EMPTY_ARKTYPE_TYPE = type({});
19
+ function arktypeToJsonSchemaForExport(t) {
20
+ const raw = t.toJsonSchema({
21
+ fallback: {
22
+ predicate: (ctx) => ctx.base,
23
+ morph: (ctx) => ctx.base,
24
+ // `false` is a valid JSON Schema value ("matches nothing"), but arktype's TS types
25
+ // reject `false` as the fallback return — cast through `unknown` to keep runtime behavior.
26
+ default: (() => false)
27
+ }
28
+ });
29
+ return pruneFalseUnionBranches(JSON.parse(JSON.stringify(raw)));
30
+ }
31
+ function pruneFalseUnionBranches(value) {
32
+ let result = value;
33
+ if (Array.isArray(value)) {
34
+ result = value.map(pruneFalseUnionBranches);
35
+ } else if (value && typeof value === "object") {
36
+ const out = {};
37
+ for (const [key, raw] of Object.entries(value)) {
38
+ if ((key === "anyOf" || key === "oneOf") && Array.isArray(raw)) {
39
+ const filtered = raw.filter((v) => v !== false).map(pruneFalseUnionBranches);
40
+ if (filtered.length > 0) {
41
+ out[key] = filtered;
42
+ }
43
+ } else {
44
+ out[key] = pruneFalseUnionBranches(raw);
45
+ }
46
+ }
47
+ result = out;
48
+ }
49
+ return result;
50
+ }
51
+
52
+ // packages/firebase/src/lib/common/auth/oidc/oidc.error.ts
53
+ var CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = "CALL_MODEL_MISSING_OIDC_SCOPE";
54
+
55
+ // packages/firebase/src/lib/common/auth/oidc/oidc.ts
56
+ var CALL_MODEL_OIDC_SCOPE_PREFIX = "model.";
57
+ var CREATE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}create`;
58
+ var READ_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}read`;
59
+ var UPDATE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}update`;
60
+ var DELETE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}delete`;
61
+ var QUERY_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}query`;
62
+ var INVOKE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}invoke`;
63
+ var CALL_MODEL_OIDC_SCOPES = [CREATE_MODEL_OIDC_SCOPE, READ_MODEL_OIDC_SCOPE, UPDATE_MODEL_OIDC_SCOPE, DELETE_MODEL_OIDC_SCOPE, QUERY_MODEL_OIDC_SCOPE, INVOKE_MODEL_OIDC_SCOPE];
64
+ var CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = {
65
+ create: "model.create",
66
+ read: "model.read",
67
+ update: "model.update",
68
+ delete: "model.delete",
69
+ query: "model.query",
70
+ invoke: "model.invoke"
71
+ };
72
+
73
+ // packages/firebase/src/lib/model/storagefile/storagefile.upload.claims.ts
74
+ var STORAGE_FILE_UPLOAD_USER_ROLE = "uploads";
75
+
76
+ // packages/dbx-cli/src/lib/manifest/types.ts
77
+ var MCP_MANIFEST_VERSION = 1;
78
+ function mcpManifestKey(modelType, call, specifier) {
79
+ const isDefault = specifier == null || specifier === "_";
80
+ return isDefault ? `${modelType}.${call}._` : `${modelType}.${call}.${specifier}`;
81
+ }
82
+
83
+ // packages/dbx-cli/src/lib/mcp-scan/registry/auth-runtime.ts
84
+ // @__NO_SIDE_EFFECTS__
85
+ function createAuthRegistryFromEntries(input = {}) {
86
+ const roles = input.roles ?? [];
87
+ const claims = input.claims ?? [];
88
+ const scopes = input.scopes ?? [];
89
+ const apps = input.apps ?? [];
90
+ const loadedSources = input.loadedSources ?? [];
91
+ const { rolesByKey, rolesByTag } = indexRoles(roles);
92
+ const { claimsByApp, claimsByInterface, claimsByRole } = indexClaims(claims);
93
+ const scopesByName = indexScopes(scopes);
94
+ const { appsBySlug, appsByInterface } = indexApps(apps);
95
+ const registry = {
96
+ roles,
97
+ claims,
98
+ scopes,
99
+ apps,
100
+ loadedSources: [...loadedSources],
101
+ findRole(key) {
102
+ return rolesByKey.get(key.toLowerCase());
103
+ },
104
+ findRolesByTag(tag) {
105
+ return rolesByTag.get(tag.toLowerCase()) ?? [];
106
+ },
107
+ findClaim(key, app) {
108
+ let result;
109
+ if (app === void 0) {
110
+ result = claims.find((c) => c.key === key);
111
+ } else {
112
+ const inApp = claimsByApp.get(app.toLowerCase()) ?? [];
113
+ result = inApp.find((c) => c.key === key);
114
+ result ??= claims.find((c) => c.key === key && c.app === void 0);
115
+ }
116
+ return result;
117
+ },
118
+ findClaimsByApp(app) {
119
+ return claimsByApp.get(app.toLowerCase()) ?? [];
120
+ },
121
+ findClaimsByInterface(interfaceName) {
122
+ return claimsByInterface.get(interfaceName.toLowerCase()) ?? [];
123
+ },
124
+ findScope(scope) {
125
+ return scopesByName.get(scope);
126
+ },
127
+ findApp(app) {
128
+ return appsBySlug.get(app.toLowerCase());
129
+ },
130
+ findAppByInterface(interfaceName) {
131
+ return appsByInterface.get(interfaceName.toLowerCase());
132
+ },
133
+ findClaimsForRole(role) {
134
+ return claimsByRole.get(role.toLowerCase()) ?? [];
135
+ }
136
+ };
137
+ return registry;
138
+ }
139
+ function pushInto(map, key, value) {
140
+ const existing = map.get(key);
141
+ if (existing === void 0) {
142
+ map.set(key, [value]);
143
+ } else {
144
+ existing.push(value);
145
+ }
146
+ }
147
+ function indexRoles(roles) {
148
+ const rolesByKey = /* @__PURE__ */ new Map();
149
+ const rolesByTag = /* @__PURE__ */ new Map();
150
+ for (const role of roles) {
151
+ const lowered = role.role.toLowerCase();
152
+ if (!rolesByKey.has(lowered)) rolesByKey.set(lowered, role);
153
+ if (role.constName !== void 0) {
154
+ const constLowered = role.constName.toLowerCase();
155
+ if (!rolesByKey.has(constLowered)) rolesByKey.set(constLowered, role);
156
+ }
157
+ for (const tag of role.tags) {
158
+ pushInto(rolesByTag, tag.toLowerCase(), role);
159
+ }
160
+ }
161
+ return { rolesByKey, rolesByTag };
162
+ }
163
+ function indexClaims(claims) {
164
+ const claimsByApp = /* @__PURE__ */ new Map();
165
+ const claimsByInterface = /* @__PURE__ */ new Map();
166
+ const claimsByRole = /* @__PURE__ */ new Map();
167
+ for (const claim of claims) {
168
+ if (claim.app !== void 0) pushInto(claimsByApp, claim.app.toLowerCase(), claim);
169
+ if (claim.interfaceName !== void 0) pushInto(claimsByInterface, claim.interfaceName.toLowerCase(), claim);
170
+ for (const role of claim.mapping.roles) {
171
+ pushInto(claimsByRole, role.toLowerCase(), claim);
172
+ }
173
+ }
174
+ return { claimsByApp, claimsByInterface, claimsByRole };
175
+ }
176
+ function indexScopes(scopes) {
177
+ const scopesByName = /* @__PURE__ */ new Map();
178
+ for (const scope of scopes) {
179
+ if (!scopesByName.has(scope.scope)) scopesByName.set(scope.scope, scope);
180
+ }
181
+ return scopesByName;
182
+ }
183
+ function indexApps(apps) {
184
+ const appsBySlug = /* @__PURE__ */ new Map();
185
+ const appsByInterface = /* @__PURE__ */ new Map();
186
+ for (const app of apps) {
187
+ appsBySlug.set(app.app.toLowerCase(), app);
188
+ appsByInterface.set(app.claimsInterfaceName.toLowerCase(), app);
189
+ }
190
+ return { appsBySlug, appsByInterface };
191
+ }
192
+
193
+ // packages/dbx-cli/src/lib/mcp-scan/registry/auth-builtin.ts
194
+ var UTIL_ROLE_PATH = "packages/util/src/lib/auth/auth.role.ts";
195
+ var STORAGE_CLAIM_PATH = "packages/firebase/src/lib/model/storagefile/storagefile.upload.claims.ts";
196
+ var OIDC_SCOPE_PATH = "packages/firebase/src/lib/common/auth/oidc/oidc.ts";
197
+ var OIDC_SCOPE_PRE_ASSERT_PATH = "packages/firebase-server/oidc/src/lib/scope.ts";
198
+ var DEMO_CLAIMS_PATH = "components/demo-firebase/src/lib/auth/claims.ts";
199
+ var BUILTIN_AUTH_ROLES = [
200
+ {
201
+ role: AUTH_TOS_SIGNED_ROLE,
202
+ constName: "AUTH_TOS_SIGNED_ROLE",
203
+ source: "system",
204
+ sourcePath: UTIL_ROLE_PATH,
205
+ sourceLine: 26,
206
+ description: "Auth role for an account that has signed the terms of service.",
207
+ tags: ["system", "auth", "verified-user"]
208
+ },
209
+ {
210
+ role: AUTH_ONBOARDED_ROLE,
211
+ constName: "AUTH_ONBOARDED_ROLE",
212
+ source: "system",
213
+ sourcePath: UTIL_ROLE_PATH,
214
+ sourceLine: 31,
215
+ description: "Auth role for an account that has been onboarded.",
216
+ tags: ["system", "auth", "verified-user"]
217
+ },
218
+ {
219
+ role: AUTH_ADMIN_ROLE,
220
+ constName: "AUTH_ADMIN_ROLE",
221
+ source: "system",
222
+ sourcePath: UTIL_ROLE_PATH,
223
+ sourceLine: 36,
224
+ description: "Auth role for a full admin. Allowed into all sections of the app.",
225
+ tags: ["system", "auth", "privileged", "staff"]
226
+ },
227
+ {
228
+ role: AUTH_USER_ROLE,
229
+ constName: "AUTH_USER_ROLE",
230
+ source: "system",
231
+ sourcePath: UTIL_ROLE_PATH,
232
+ sourceLine: 41,
233
+ description: "Auth role for a general logged-in user.",
234
+ tags: ["system", "auth"]
235
+ },
236
+ {
237
+ role: STORAGE_FILE_UPLOAD_USER_ROLE,
238
+ constName: "STORAGE_FILE_UPLOAD_USER_ROLE",
239
+ source: "system",
240
+ sourcePath: STORAGE_CLAIM_PATH,
241
+ sourceLine: 32,
242
+ description: "Role granting storage-file uploads. Revoked when the inverse `fr` claim is set.",
243
+ tags: ["system", "storage", "uploads"]
244
+ }
245
+ ];
246
+ var BUILTIN_AUTH_CLAIMS = [
247
+ {
248
+ key: "fr",
249
+ type: "StorageFileUploadUserRestriction",
250
+ description: "Inverse claim \u2014 when set, revokes the `uploads` role from the user.",
251
+ interfaceName: "StorageFileUploadUserClaims",
252
+ sourcePath: STORAGE_CLAIM_PATH,
253
+ sourceLine: 26,
254
+ source: "system",
255
+ mapping: {
256
+ roles: [STORAGE_FILE_UPLOAD_USER_ROLE],
257
+ inverse: true,
258
+ inverseMode: "any",
259
+ customEncodeDecode: false
260
+ },
261
+ tags: ["system", "storage", "uploads", "inverse"]
262
+ }
263
+ ];
264
+ var BUILTIN_AUTH_SCOPES = CALL_MODEL_OIDC_SCOPES.map((scope) => {
265
+ const callType = (Object.entries(CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE).find(([, s]) => s === scope) ?? [])[0];
266
+ const result = {
267
+ scope,
268
+ prefix: CALL_MODEL_OIDC_SCOPE_PREFIX,
269
+ callType,
270
+ description: `Required OIDC scope for the \`callModel\` ${callType ?? "CRUD"} verb.`,
271
+ enforcedAt: [
272
+ {
273
+ path: OIDC_SCOPE_PRE_ASSERT_PATH,
274
+ line: 21,
275
+ description: "`oidcCallModelScopePreAssert` rejects requests that lack this scope."
276
+ }
277
+ ],
278
+ errorCode: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
279
+ source: "system",
280
+ sourcePath: OIDC_SCOPE_PATH,
281
+ sourceLine: 30,
282
+ apps: []
283
+ };
284
+ return result;
285
+ });
286
+ var DEMO_CLAIMS = [
287
+ {
288
+ key: "o",
289
+ type: "1",
290
+ description: "Onboarded flag \u2014 set when the user has signed TOS and completed onboarding.",
291
+ app: "demo-api",
292
+ interfaceName: "DemoApiAuthClaims",
293
+ sourcePath: DEMO_CLAIMS_PATH,
294
+ sourceLine: 18,
295
+ source: "app",
296
+ mapping: {
297
+ roles: [AUTH_TOS_SIGNED_ROLE, AUTH_ONBOARDED_ROLE],
298
+ inverse: false,
299
+ claimValue: 1,
300
+ customEncodeDecode: false
301
+ },
302
+ tags: ["onboarded", "verified-user"]
303
+ },
304
+ {
305
+ key: "a",
306
+ type: "1",
307
+ description: "Admin role \u2014 grants full access to admin-only sections of the app.",
308
+ app: "demo-api",
309
+ interfaceName: "DemoApiAuthClaims",
310
+ sourcePath: DEMO_CLAIMS_PATH,
311
+ sourceLine: 25,
312
+ source: "app",
313
+ mapping: {
314
+ roles: [AUTH_ADMIN_ROLE],
315
+ inverse: false,
316
+ claimValue: 1,
317
+ customEncodeDecode: false
318
+ },
319
+ tags: ["privileged", "staff"]
320
+ }
321
+ ];
322
+ var DEMO_APP = {
323
+ app: "demo-api",
324
+ claimsInterfaceName: "DemoApiAuthClaims",
325
+ serviceConstName: "DEMO_AUTH_CLAIMS_SERVICE",
326
+ sourcePath: DEMO_CLAIMS_PATH,
327
+ claimKeys: ["o", "a", "fr"],
328
+ scopes: [...CALL_MODEL_OIDC_SCOPES],
329
+ description: "Demo Firebase API. Inherits the `fr` storage-upload claim and adds the onboarded (`o`) and admin (`a`) flags."
330
+ };
331
+ var WORKSPACE_AUTH_CLAIMS = [...DEMO_CLAIMS];
332
+
333
+ // packages/dbx-cli/src/lib/scan-helpers/scan-io.ts
334
+ import { glob as fsGlob, readFile as nodeReadFile } from "node:fs/promises";
335
+ import { Project } from "ts-morph";
336
+ var defaultReadFile = (path) => nodeReadFile(path, "utf-8");
337
+
338
+ // packages/dbx-cli/src/lib/mcp-scan/scan/auth-extract.ts
339
+ import { Node } from "ts-morph";
340
+ var AUTH_CLAIMS_APP_TAG = "dbxAuthClaimsApp";
341
+ var AUTH_CLAIMS_SERVICE_TAG = "dbxAuthClaimsService";
342
+ var AUTH_CLAIM_MARKER = "dbxAuthClaim";
343
+ var AUTH_ROLE_TAG_TAG = "dbxAuthRoleTag";
344
+ var AUTH_ROLE_TAG = "dbxAuthRole";
345
+ var AUTH_ROLE_CLAIMS_SERVICE_FN = "authRoleClaimsService";
346
+ function extractAuthEntries(input) {
347
+ const { project, knownRoles } = input;
348
+ const warnings = [];
349
+ const localRoleConsts = collectLocalRoleConsts(project);
350
+ const mergedKnownRoles = mergeKnownRoles(knownRoles, localRoleConsts.scalars);
351
+ const taggedAppDecls = collectTaggedAppDecls(project, warnings);
352
+ const serviceMappings = collectServiceMappings(project, warnings, taggedAppDecls);
353
+ const apps = [];
354
+ const claims = [];
355
+ for (const tagged of taggedAppDecls) {
356
+ const serviceForApp = serviceMappings.get(tagged.slug.toLowerCase());
357
+ const inheritedInterfaceNames = collectInheritedInterfaceNames(tagged.decl);
358
+ const ownClaims = collectClaimsFromDecl({
359
+ decl: tagged.decl,
360
+ app: tagged.slug,
361
+ interfaceName: tagged.interfaceName,
362
+ filePath: tagged.filePath,
363
+ service: serviceForApp,
364
+ knownRoles: mergedKnownRoles,
365
+ localArrayRoles: localRoleConsts.arrays,
366
+ warnings
367
+ });
368
+ for (const claim of ownClaims) claims.push(claim);
369
+ apps.push({
370
+ app: tagged.slug,
371
+ claimsInterfaceName: tagged.interfaceName,
372
+ serviceConstName: serviceForApp?.constName,
373
+ inheritedInterfaceNames,
374
+ ownClaimKeys: ownClaims.map((c) => c.key),
375
+ filePath: tagged.filePath,
376
+ line: tagged.line
377
+ });
378
+ }
379
+ return { apps, claims, warnings };
380
+ }
381
+ function collectTaggedAppDecls(project, warnings) {
382
+ const out = [];
383
+ for (const sourceFile of project.getSourceFiles()) {
384
+ const filePath = sourceFile.getFilePath();
385
+ for (const decl of sourceFile.getInterfaces()) {
386
+ const tagged = readAppTag({ decl, filePath, warnings, interfaceName: decl.getName() });
387
+ if (tagged !== void 0) out.push(tagged);
388
+ }
389
+ for (const decl of sourceFile.getTypeAliases()) {
390
+ const tagged = readAppTag({ decl, filePath, warnings, interfaceName: decl.getName() });
391
+ if (tagged !== void 0) out.push(tagged);
392
+ }
393
+ }
394
+ return out;
395
+ }
396
+ function readAppTag(input) {
397
+ const { decl, interfaceName, filePath, warnings } = input;
398
+ const slug = readJsDocFirstTagValue(decl.getJsDocs(), AUTH_CLAIMS_APP_TAG);
399
+ let result;
400
+ if (slug.kind === "empty") {
401
+ warnings.push({ kind: "app-missing-slug", interfaceName, filePath, line: decl.getStartLineNumber() });
402
+ } else if (slug.kind === "value") {
403
+ result = {
404
+ slug: slug.text,
405
+ interfaceName,
406
+ decl,
407
+ filePath,
408
+ line: decl.getStartLineNumber()
409
+ };
410
+ }
411
+ return result;
412
+ }
413
+ function collectServiceMappings(project, warnings, appDecls) {
414
+ const knownAppSlugs = new Set(appDecls.map((a) => a.slug.toLowerCase()));
415
+ const out = /* @__PURE__ */ new Map();
416
+ for (const sourceFile of project.getSourceFiles()) {
417
+ const filePath = sourceFile.getFilePath();
418
+ for (const stmt of sourceFile.getVariableStatements()) {
419
+ if (!stmt.isExported()) continue;
420
+ processServiceStatement({ stmt, filePath, knownAppSlugs, warnings, out });
421
+ }
422
+ }
423
+ return out;
424
+ }
425
+ function processServiceStatement(input) {
426
+ const { stmt, filePath, knownAppSlugs, warnings, out } = input;
427
+ const slug = readJsDocFirstTagValue(stmt.getJsDocs(), AUTH_CLAIMS_SERVICE_TAG);
428
+ if (slug.kind === "absent") return;
429
+ const decls = stmt.getDeclarations();
430
+ if (slug.kind === "empty") {
431
+ for (const decl of decls) {
432
+ warnings.push({ kind: "service-missing-slug", constName: decl.getName(), filePath, line: decl.getStartLineNumber() });
433
+ }
434
+ return;
435
+ }
436
+ const slugText = slug.text;
437
+ const slugLower = slugText.toLowerCase();
438
+ if (!knownAppSlugs.has(slugLower)) {
439
+ for (const decl of decls) {
440
+ warnings.push({ kind: "service-without-app", constName: decl.getName(), slug: slugText, filePath, line: decl.getStartLineNumber() });
441
+ }
442
+ return;
443
+ }
444
+ for (const decl of decls) {
445
+ out.set(slugLower, {
446
+ constName: decl.getName(),
447
+ perKey: parseServiceInitializer(decl.getInitializer()),
448
+ filePath,
449
+ line: decl.getStartLineNumber()
450
+ });
451
+ }
452
+ }
453
+ function parseServiceInitializer(initializer) {
454
+ const out = /* @__PURE__ */ new Map();
455
+ const configArg = initializer === void 0 ? void 0 : resolveServiceConfigArg(initializer);
456
+ if (configArg !== void 0) {
457
+ for (const property of configArg.getProperties()) {
458
+ if (!Node.isPropertyAssignment(property)) continue;
459
+ const key = readPropertyAssignmentKey(property);
460
+ if (key === void 0) continue;
461
+ const parsed = parseRoleMappingValue(property);
462
+ out.set(key, parsed);
463
+ }
464
+ }
465
+ return out;
466
+ }
467
+ function resolveServiceConfigArg(initializer) {
468
+ let configArg;
469
+ if (Node.isCallExpression(initializer)) {
470
+ const callee = initializer.getExpression();
471
+ const calleeText = callee.getText();
472
+ if (calleeText.endsWith(AUTH_ROLE_CLAIMS_SERVICE_FN)) {
473
+ const args = initializer.getArguments();
474
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
475
+ configArg = args[0];
476
+ }
477
+ }
478
+ } else if (Node.isObjectLiteralExpression(initializer)) {
479
+ configArg = initializer;
480
+ }
481
+ return configArg;
482
+ }
483
+ function readPropertyAssignmentKey(property) {
484
+ const nameNode = property.getNameNode();
485
+ let result;
486
+ if (Node.isIdentifier(nameNode) || Node.isStringLiteral(nameNode) || Node.isNoSubstitutionTemplateLiteral(nameNode)) {
487
+ result = Node.isIdentifier(nameNode) ? nameNode.getText() : nameNode.getLiteralText();
488
+ }
489
+ return result;
490
+ }
491
+ function parseRoleMappingValue(property) {
492
+ const initializer = property.getInitializer();
493
+ let result;
494
+ if (initializer === void 0) {
495
+ result = { mapping: { roles: [], inverse: false, customEncodeDecode: false }, unresolvedRoleConsts: [] };
496
+ } else if (Node.isObjectLiteralExpression(initializer)) {
497
+ result = parseRoleMappingObject(initializer);
498
+ } else {
499
+ result = { mapping: { roles: [], inverse: false, customEncodeDecode: true }, unresolvedRoleConsts: [] };
500
+ }
501
+ return result;
502
+ }
503
+ function parseRoleMappingObject(obj) {
504
+ const rolesProperty = obj.getProperty("roles");
505
+ const inverseRolesProperty = obj.getProperty("inverseRoles");
506
+ const claimValueProperty = obj.getProperty("claimValue");
507
+ const inverseModeProperty = obj.getProperty("inverseMode");
508
+ let inverse = false;
509
+ let rawRoles = [];
510
+ if (rolesProperty !== void 0 && Node.isPropertyAssignment(rolesProperty)) {
511
+ rawRoles = readRoleListInitializer(rolesProperty.getInitializer());
512
+ } else if (inverseRolesProperty !== void 0 && Node.isPropertyAssignment(inverseRolesProperty)) {
513
+ inverse = true;
514
+ rawRoles = readRoleListInitializer(inverseRolesProperty.getInitializer());
515
+ }
516
+ const claimValue = claimValueProperty !== void 0 && Node.isPropertyAssignment(claimValueProperty) ? readClaimValueInitializer(claimValueProperty.getInitializer()) : void 0;
517
+ const inverseMode = inverseModeProperty !== void 0 && Node.isPropertyAssignment(inverseModeProperty) ? readInverseModeInitializer(inverseModeProperty.getInitializer()) : void 0;
518
+ const unresolvedRoleConsts = [];
519
+ const roleTexts = [];
520
+ for (const raw of rawRoles) {
521
+ if (!raw.resolvable) {
522
+ unresolvedRoleConsts.push(raw.text);
523
+ }
524
+ roleTexts.push(raw.text);
525
+ }
526
+ const mapping = {
527
+ roles: roleTexts,
528
+ inverse,
529
+ ...inverseMode === void 0 ? {} : { inverseMode },
530
+ ...claimValue === void 0 ? {} : { claimValue },
531
+ customEncodeDecode: false
532
+ };
533
+ return { mapping, unresolvedRoleConsts };
534
+ }
535
+ function readRoleListInitializer(initializer) {
536
+ const out = [];
537
+ if (initializer !== void 0) {
538
+ if (Node.isArrayLiteralExpression(initializer)) {
539
+ for (const element of initializer.getElements()) {
540
+ const parsed = readRoleAtom(element);
541
+ if (parsed !== void 0) out.push(parsed);
542
+ }
543
+ } else {
544
+ const parsed = readRoleAtom(initializer);
545
+ if (parsed !== void 0) out.push(parsed);
546
+ }
547
+ }
548
+ return out;
549
+ }
550
+ function readRoleAtom(node) {
551
+ let result;
552
+ if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {
553
+ result = { text: node.getLiteralText(), resolvable: true };
554
+ } else if (Node.isIdentifier(node)) {
555
+ result = { text: node.getText(), resolvable: false };
556
+ } else if (Node.isSpreadElement(node)) {
557
+ const inner = node.getExpression();
558
+ if (Node.isIdentifier(inner)) {
559
+ result = { text: inner.getText(), resolvable: false };
560
+ }
561
+ }
562
+ return result;
563
+ }
564
+ function readClaimValueInitializer(initializer) {
565
+ let result;
566
+ if (initializer === void 0) {
567
+ result = void 0;
568
+ } else if (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer)) {
569
+ result = initializer.getLiteralText();
570
+ } else if (Node.isNumericLiteral(initializer)) {
571
+ result = Number(initializer.getText());
572
+ } else if (Node.isTrueLiteral(initializer)) {
573
+ result = true;
574
+ } else if (Node.isFalseLiteral(initializer)) {
575
+ result = false;
576
+ }
577
+ return result;
578
+ }
579
+ function readInverseModeInitializer(initializer) {
580
+ let result;
581
+ if (initializer !== void 0 && (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer))) {
582
+ const text = initializer.getLiteralText();
583
+ if (text === "any" || text === "all") {
584
+ result = text;
585
+ }
586
+ }
587
+ return result;
588
+ }
589
+ function collectClaimsFromDecl(input) {
590
+ const { decl } = input;
591
+ const properties = [];
592
+ if (Node.isInterfaceDeclaration(decl)) {
593
+ for (const prop of decl.getProperties()) properties.push(prop);
594
+ } else {
595
+ const typeNode = decl.getTypeNode();
596
+ if (typeNode !== void 0) collectPropertySignaturesFromTypeNode(typeNode, properties);
597
+ }
598
+ const out = [];
599
+ for (const property of properties) {
600
+ const claim = buildClaimFromProperty({ ...input, property });
601
+ if (claim !== void 0) out.push(claim);
602
+ }
603
+ return out;
604
+ }
605
+ function collectPropertySignaturesFromTypeNode(typeNode, sink) {
606
+ if (Node.isTypeLiteral(typeNode)) {
607
+ for (const prop of typeNode.getProperties()) sink.push(prop);
608
+ } else if (Node.isIntersectionTypeNode(typeNode)) {
609
+ for (const member of typeNode.getTypeNodes()) collectPropertySignaturesFromTypeNode(member, sink);
610
+ } else if (Node.isParenthesizedTypeNode(typeNode)) {
611
+ const inner = typeNode.getTypeNode();
612
+ if (inner !== void 0) collectPropertySignaturesFromTypeNode(inner, sink);
613
+ }
614
+ }
615
+ function buildClaimFromProperty(input) {
616
+ const { property, app, interfaceName, filePath, service, knownRoles, localArrayRoles, warnings } = input;
617
+ const tagState = readPropertyTagState(property.getJsDocs());
618
+ let result;
619
+ if (tagState.hasMarker) {
620
+ const key = property.getName();
621
+ const typeText = property.getTypeNode()?.getText() ?? "unknown";
622
+ const description = tagState.summaries.join("\n\n");
623
+ const line = property.getStartLineNumber();
624
+ const parsed = service?.perKey.get(key);
625
+ if (parsed === void 0) {
626
+ warnings.push({ kind: "claim-missing-mapping", app, key, filePath, line });
627
+ }
628
+ const mapping = parsed === void 0 ? { roles: [], inverse: false, customEncodeDecode: false } : resolveMappingRoles({ mapping: parsed.mapping, unresolvedRoleConsts: parsed.unresolvedRoleConsts, knownRoles, localArrayRoles, app, key, filePath, line, warnings });
629
+ result = {
630
+ key,
631
+ type: typeText,
632
+ description,
633
+ app,
634
+ interfaceName,
635
+ tags: tagState.tags,
636
+ mapping,
637
+ filePath,
638
+ line
639
+ };
640
+ }
641
+ return result;
642
+ }
643
+ function resolveMappingRoles(input) {
644
+ const { mapping, knownRoles, localArrayRoles, app, key, filePath, line, warnings } = input;
645
+ const resolved = [];
646
+ for (const role of mapping.roles) {
647
+ if (knownRoles.has(role)) {
648
+ resolved.push(knownRoles.get(role));
649
+ } else if (input.unresolvedRoleConsts.includes(role)) {
650
+ const aggregate = resolveRoleConstByName(role, { knownRoles, localArrayRoles, seen: /* @__PURE__ */ new Set() });
651
+ if (aggregate.unresolved.length === 0) {
652
+ for (const aggregateRole of aggregate.roles) resolved.push(aggregateRole);
653
+ } else {
654
+ warnings.push({ kind: "unresolved-role-const", app, key, constName: role, filePath, line });
655
+ resolved.push(role);
656
+ }
657
+ } else {
658
+ resolved.push(role);
659
+ }
660
+ }
661
+ return { ...mapping, roles: resolved };
662
+ }
663
+ function collectLocalRoleConsts(project) {
664
+ const scalars = /* @__PURE__ */ new Map();
665
+ const arrays = /* @__PURE__ */ new Map();
666
+ for (const sourceFile of project.getSourceFiles()) {
667
+ for (const stmt of sourceFile.getVariableStatements()) {
668
+ if (!stmt.isExported()) continue;
669
+ for (const decl of stmt.getDeclarations()) {
670
+ const initializer = decl.getInitializer();
671
+ const name = decl.getName();
672
+ if (initializer === void 0) continue;
673
+ if (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer)) {
674
+ if (!scalars.has(name)) scalars.set(name, initializer.getLiteralText());
675
+ } else if (Node.isArrayLiteralExpression(initializer) && !arrays.has(name)) arrays.set(name, initializer);
676
+ }
677
+ }
678
+ }
679
+ return { scalars, arrays };
680
+ }
681
+ function mergeKnownRoles(builtins, localScalars) {
682
+ const out = /* @__PURE__ */ new Map();
683
+ for (const [name, role] of localScalars) out.set(name, role);
684
+ for (const [name, role] of builtins) out.set(name, role);
685
+ return out;
686
+ }
687
+ var EMPTY_ROLE_CONST_RESOLUTION = { roles: [], unresolved: [] };
688
+ function resolveRoleConstByName(name, ctx) {
689
+ let result;
690
+ const scalar = ctx.knownRoles.get(name);
691
+ if (scalar !== void 0) {
692
+ result = { roles: [scalar], unresolved: [] };
693
+ } else {
694
+ const arrayExpr = ctx.localArrayRoles.get(name);
695
+ if (arrayExpr === void 0) {
696
+ result = { roles: [], unresolved: [name] };
697
+ } else if (ctx.seen.has(name)) {
698
+ result = EMPTY_ROLE_CONST_RESOLUTION;
699
+ } else {
700
+ const nextSeen = new Set(ctx.seen);
701
+ nextSeen.add(name);
702
+ result = resolveRoleArrayLiteral(arrayExpr, { ...ctx, seen: nextSeen });
703
+ }
704
+ }
705
+ return result;
706
+ }
707
+ function resolveRoleArrayLiteral(arrayExpr, ctx) {
708
+ const roles = [];
709
+ const unresolved = [];
710
+ for (const element of arrayExpr.getElements()) {
711
+ const elementResult = resolveRoleArrayElement(element, ctx);
712
+ for (const role of elementResult.roles) roles.push(role);
713
+ for (const name of elementResult.unresolved) unresolved.push(name);
714
+ }
715
+ return { roles, unresolved };
716
+ }
717
+ function resolveRoleArrayElement(element, ctx) {
718
+ let result = EMPTY_ROLE_CONST_RESOLUTION;
719
+ if (Node.isStringLiteral(element) || Node.isNoSubstitutionTemplateLiteral(element)) {
720
+ result = { roles: [element.getLiteralText()], unresolved: [] };
721
+ } else if (Node.isIdentifier(element)) {
722
+ result = resolveRoleConstByName(element.getText(), ctx);
723
+ } else if (Node.isSpreadElement(element)) {
724
+ const inner = element.getExpression();
725
+ if (Node.isIdentifier(inner)) {
726
+ result = resolveRoleConstByName(inner.getText(), ctx);
727
+ } else if (Node.isArrayLiteralExpression(inner)) {
728
+ result = resolveRoleArrayLiteral(inner, ctx);
729
+ } else {
730
+ result = { roles: [], unresolved: [inner.getText()] };
731
+ }
732
+ }
733
+ return result;
734
+ }
735
+ function readPropertyTagState(jsDocs) {
736
+ const summaries = [];
737
+ const tags = [];
738
+ let hasMarker = false;
739
+ for (const jsDoc of jsDocs) {
740
+ const description = jsDoc.getDescription().trim();
741
+ if (description.length > 0) summaries.push(description);
742
+ if (consumeMarkerAndRoles(jsDoc.getTags(), tags)) hasMarker = true;
743
+ }
744
+ return { hasMarker, summaries, tags };
745
+ }
746
+ function consumeMarkerAndRoles(jsDocTags, roleSink) {
747
+ let hasMarker = false;
748
+ for (const tag of jsDocTags) {
749
+ const name = tag.getTagName();
750
+ if (name === AUTH_CLAIM_MARKER) {
751
+ hasMarker = true;
752
+ } else {
753
+ collectRoleTagPieces(name, tag, roleSink);
754
+ }
755
+ }
756
+ return hasMarker;
757
+ }
758
+ function collectRoleTagPieces(name, tag, sink) {
759
+ if (name !== AUTH_ROLE_TAG_TAG && name !== AUTH_ROLE_TAG) return;
760
+ const text = tag.getCommentText()?.trim() ?? "";
761
+ if (text.length === 0) return;
762
+ for (const piece of splitListTagText(text)) {
763
+ if (!sink.includes(piece)) sink.push(piece);
764
+ }
765
+ }
766
+ function splitListTagText(text) {
767
+ const out = [];
768
+ for (const piece of text.split(/[\s,]+/)) {
769
+ const trimmed = piece.trim();
770
+ if (trimmed.length > 0) out.push(trimmed);
771
+ }
772
+ return out;
773
+ }
774
+ function collectInheritedInterfaceNames(decl) {
775
+ const out = [];
776
+ if (Node.isInterfaceDeclaration(decl)) {
777
+ for (const ext of decl.getExtends()) {
778
+ const name = ext.getExpression().getText();
779
+ if (name.length > 0 && !out.includes(name)) out.push(name);
780
+ }
781
+ } else {
782
+ const typeNode = decl.getTypeNode();
783
+ if (typeNode !== void 0) collectInheritedInterfaceNamesFromTypeNode(typeNode, out);
784
+ }
785
+ return out;
786
+ }
787
+ function collectInheritedInterfaceNamesFromTypeNode(typeNode, sink) {
788
+ if (Node.isIntersectionTypeNode(typeNode)) {
789
+ for (const member of typeNode.getTypeNodes()) collectInheritedInterfaceNamesFromTypeNode(member, sink);
790
+ } else if (Node.isTypeReference(typeNode)) {
791
+ const text = typeNode.getTypeName().getText();
792
+ if (text.length > 0 && !sink.includes(text)) sink.push(text);
793
+ } else if (Node.isParenthesizedTypeNode(typeNode)) {
794
+ const inner = typeNode.getTypeNode();
795
+ if (inner !== void 0) collectInheritedInterfaceNamesFromTypeNode(inner, sink);
796
+ }
797
+ }
798
+ var JSDOC_TAG_ABSENT = { kind: "absent" };
799
+ var JSDOC_TAG_EMPTY = { kind: "empty" };
800
+ function readJsDocFirstTagValue(jsDocs, tagName) {
801
+ let result = JSDOC_TAG_ABSENT;
802
+ for (const jsDoc of jsDocs) {
803
+ for (const tag of jsDoc.getTags()) {
804
+ if (tag.getTagName() === tagName) {
805
+ const text = tag.getCommentText()?.trim() ?? "";
806
+ result = text.length > 0 ? { kind: "value", text } : JSDOC_TAG_EMPTY;
807
+ break;
808
+ }
809
+ }
810
+ if (result.kind !== "absent") break;
811
+ }
812
+ return result;
813
+ }
814
+
815
+ // packages/dbx-cli/src/lib/mcp-scan/manifest/load-auth-registry.ts
816
+ import { glob as fsGlob2 } from "node:fs/promises";
817
+ import { join, relative, resolve as resolvePath, sep } from "node:path";
818
+ import { Project as Project2 } from "ts-morph";
819
+ var DEFAULT_GLOB_PATTERNS = ["components/*-firebase/src/**/auth/**/*claims*.ts", "components/*-shared/src/**/auth/**/*claims*.ts", "components/*-web/src/**/auth/**/*claims*.ts", "components/*-core/src/**/auth/**/*claims*.ts", "apps/*/src/**/auth/**/*claims*.ts"];
820
+ var APP_TAG_MARKER = "@dbxAuthClaimsApp";
821
+ var SERVICE_TAG_MARKER = "@dbxAuthClaimsService";
822
+ async function loadAuthRegistry(input) {
823
+ const { cwd, readFile = defaultReadFile, extraFiles = [], skipDiscovery = false } = input;
824
+ const discovered = skipDiscovery ? [] : await discoverClaimsFiles(cwd);
825
+ const candidatePaths = mergeAndNormalisePaths(discovered, extraFiles);
826
+ const fileWarnings = [];
827
+ const project = new Project2({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
828
+ const scannedFiles = [];
829
+ for (const relPath of candidatePaths) {
830
+ const absolute = resolvePath(cwd, relPath);
831
+ let text;
832
+ try {
833
+ text = await readFile(absolute);
834
+ } catch (error) {
835
+ fileWarnings.push({ kind: "read-failed", relPath, error: error instanceof Error ? error.message : String(error) });
836
+ continue;
837
+ }
838
+ if (!hasAuthMarker(text)) continue;
839
+ try {
840
+ project.createSourceFile(absolute, text, { overwrite: true });
841
+ scannedFiles.push(relPath);
842
+ } catch (error) {
843
+ fileWarnings.push({ kind: "parse-failed", relPath, error: error instanceof Error ? error.message : String(error) });
844
+ }
845
+ }
846
+ const knownRoles = buildKnownRolesMap(BUILTIN_AUTH_ROLES);
847
+ const extractResult = scannedFiles.length === 0 ? { apps: [], claims: [], warnings: [] } : extractAuthEntries({ project, knownRoles });
848
+ const registry = composeRegistry({
849
+ cwd,
850
+ extractResult,
851
+ scannedRelFiles: scannedFiles,
852
+ builtinRoles: BUILTIN_AUTH_ROLES,
853
+ builtinClaims: BUILTIN_AUTH_CLAIMS,
854
+ builtinScopes: BUILTIN_AUTH_SCOPES
855
+ });
856
+ return {
857
+ registry,
858
+ extractWarnings: extractResult.warnings,
859
+ fileWarnings,
860
+ scannedFiles,
861
+ extractedAppCount: extractResult.apps.length,
862
+ extractedClaimCount: extractResult.claims.length
863
+ };
864
+ }
865
+ async function discoverClaimsFiles(cwd) {
866
+ const found = /* @__PURE__ */ new Set();
867
+ for (const pattern of DEFAULT_GLOB_PATTERNS) {
868
+ try {
869
+ for await (const match of fsGlob2(pattern, { cwd })) {
870
+ found.add(toPosix(match));
871
+ }
872
+ } catch {
873
+ }
874
+ }
875
+ return [...found].sort((a, b) => a.localeCompare(b));
876
+ }
877
+ function mergeAndNormalisePaths(discovered, extra) {
878
+ const out = /* @__PURE__ */ new Set();
879
+ for (const p of discovered) out.add(p);
880
+ for (const p of extra) out.add(toPosix(p));
881
+ return [...out].sort((a, b) => a.localeCompare(b));
882
+ }
883
+ function toPosix(value) {
884
+ return value.split(sep).join("/");
885
+ }
886
+ function hasAuthMarker(text) {
887
+ return text.includes(APP_TAG_MARKER) || text.includes(SERVICE_TAG_MARKER);
888
+ }
889
+ function buildKnownRolesMap(roles) {
890
+ const out = /* @__PURE__ */ new Map();
891
+ for (const role of roles) {
892
+ if (role.constName !== void 0) {
893
+ out.set(role.constName, role.role);
894
+ }
895
+ }
896
+ return out;
897
+ }
898
+ function composeRegistry(input) {
899
+ const { cwd, extractResult, scannedRelFiles, builtinRoles, builtinClaims, builtinScopes } = input;
900
+ const inheritedClaimsByInterface = indexBuiltinClaimsByInterface(builtinClaims);
901
+ const claims = [...builtinClaims];
902
+ const apps = [];
903
+ for (const claim of extractResult.claims) {
904
+ claims.push(toAuthClaimInfo({ extracted: claim, cwd }));
905
+ }
906
+ for (const app of extractResult.apps) {
907
+ apps.push(toAuthAppInfo({ extracted: app, cwd, inheritedClaimsByInterface, allBuiltinScopes: builtinScopes }));
908
+ }
909
+ const loadedSources = ["builtin:@dereekb/util", "builtin:@dereekb/firebase", ...scannedRelFiles.map((rel) => `workspace:${rel}`)];
910
+ return createAuthRegistryFromEntries({
911
+ roles: builtinRoles,
912
+ claims,
913
+ scopes: builtinScopes,
914
+ apps,
915
+ loadedSources
916
+ });
917
+ }
918
+ function indexBuiltinClaimsByInterface(builtinClaims) {
919
+ const out = /* @__PURE__ */ new Map();
920
+ for (const claim of builtinClaims) {
921
+ if (claim.interfaceName === void 0) continue;
922
+ const list = out.get(claim.interfaceName);
923
+ if (list === void 0) out.set(claim.interfaceName, [claim]);
924
+ else list.push(claim);
925
+ }
926
+ return out;
927
+ }
928
+ function toAuthClaimInfo(input) {
929
+ const { extracted, cwd } = input;
930
+ return {
931
+ key: extracted.key,
932
+ type: extracted.type,
933
+ description: extracted.description,
934
+ app: extracted.app,
935
+ interfaceName: extracted.interfaceName,
936
+ sourcePath: toRelative(extracted.filePath, cwd),
937
+ sourceLine: extracted.line,
938
+ source: "app",
939
+ mapping: extracted.mapping,
940
+ tags: extracted.tags
941
+ };
942
+ }
943
+ function toAuthAppInfo(input) {
944
+ const { extracted, cwd, inheritedClaimsByInterface, allBuiltinScopes } = input;
945
+ const inheritedKeys = [];
946
+ for (const interfaceName of extracted.inheritedInterfaceNames) {
947
+ const inherited = inheritedClaimsByInterface.get(interfaceName) ?? [];
948
+ for (const claim of inherited) {
949
+ if (!inheritedKeys.includes(claim.key)) inheritedKeys.push(claim.key);
950
+ }
951
+ }
952
+ const claimKeys = [];
953
+ for (const key of extracted.ownClaimKeys) {
954
+ if (!claimKeys.includes(key)) claimKeys.push(key);
955
+ }
956
+ for (const key of inheritedKeys) {
957
+ if (!claimKeys.includes(key)) claimKeys.push(key);
958
+ }
959
+ return {
960
+ app: extracted.app,
961
+ claimsInterfaceName: extracted.claimsInterfaceName,
962
+ serviceConstName: extracted.serviceConstName,
963
+ sourcePath: toRelative(extracted.filePath, cwd),
964
+ claimKeys,
965
+ scopes: allBuiltinScopes.map((s) => s.scope)
966
+ };
967
+ }
968
+ function toRelative(absolutePath, cwd) {
969
+ const isAbsolute2 = absolutePath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(absolutePath);
970
+ const target = isAbsolute2 ? absolutePath : resolvePath(cwd, absolutePath);
971
+ const rel = relative(cwd, target);
972
+ return toPosix(rel.length > 0 ? rel : absolutePath);
973
+ }
974
+
975
+ // packages/dbx-cli/generate-mcp-manifest/src/generate-mcp-manifest/render.ts
976
+ function renderMcpManifest(input, now = /* @__PURE__ */ new Date()) {
977
+ const tools = {};
978
+ for (const entry of input.apiManifest) {
979
+ if (entry.verb === "standalone") {
980
+ continue;
981
+ }
982
+ const key = mcpManifestKey(entry.model, entry.verb, entry.specifier);
983
+ tools[key] = buildToolEntry(entry);
984
+ }
985
+ const models = input.modelManifest != null && input.modelManifest.length > 0 ? input.modelManifest.map(projectModelEntry) : void 0;
986
+ const auth = input.auth == null ? void 0 : projectAuthSection(input.auth.registry, input.auth.app);
987
+ const base = { version: MCP_MANIFEST_VERSION, generatedAt: now.toISOString(), tools };
988
+ const result = {
989
+ ...base,
990
+ ...models == null ? {} : { models },
991
+ ...auth == null ? {} : { auth }
992
+ };
993
+ return result;
994
+ }
995
+ function projectAuthSection(registry, appSlug) {
996
+ const primary = registry.findApp(appSlug);
997
+ if (primary == null) {
998
+ return void 0;
999
+ }
1000
+ const apps = [projectAuthApp(primary)];
1001
+ const ownedKeys = new Set(primary.claimKeys);
1002
+ const claims = registry.claims.filter((claim) => ownedKeys.has(claim.key)).map(projectAuthClaim);
1003
+ return {
1004
+ app: apps[0],
1005
+ apps,
1006
+ claims
1007
+ };
1008
+ }
1009
+ function projectAuthApp(info) {
1010
+ return {
1011
+ app: info.app,
1012
+ claimsInterfaceName: info.claimsInterfaceName,
1013
+ serviceConstName: info.serviceConstName ?? "",
1014
+ claimKeys: info.claimKeys,
1015
+ scopes: info.scopes,
1016
+ ...info.description == null ? {} : { description: info.description }
1017
+ };
1018
+ }
1019
+ function projectAuthClaim(info) {
1020
+ const mapping = {
1021
+ roles: info.mapping.roles,
1022
+ inverse: info.mapping.inverse,
1023
+ customEncodeDecode: info.mapping.customEncodeDecode,
1024
+ ...info.mapping.inverseMode == null ? {} : { inverseMode: info.mapping.inverseMode },
1025
+ ...info.mapping.claimValue == null ? {} : { claimValue: info.mapping.claimValue }
1026
+ };
1027
+ return {
1028
+ key: info.key,
1029
+ description: info.description,
1030
+ type: info.type,
1031
+ source: info.source,
1032
+ mapping,
1033
+ tags: info.tags,
1034
+ ...info.app == null ? {} : { app: info.app },
1035
+ ...info.interfaceName == null ? {} : { interfaceName: info.interfaceName }
1036
+ };
1037
+ }
1038
+ function projectModelEntry(entry) {
1039
+ const projected = {
1040
+ modelType: entry.modelType,
1041
+ modelName: entry.modelName,
1042
+ identityConst: entry.identityConst,
1043
+ collectionPrefix: entry.collectionPrefix,
1044
+ sourcePackage: entry.sourcePackage,
1045
+ sourceFile: entry.sourceFile,
1046
+ fields: entry.fields.map(projectModelField),
1047
+ ...entry.modelGroup == null ? {} : { modelGroup: entry.modelGroup },
1048
+ ...entry.parentIdentityConst == null ? {} : { parentIdentityConst: entry.parentIdentityConst },
1049
+ ...entry.description == null ? {} : { description: entry.description },
1050
+ ...entry.read == null ? {} : { read: entry.read },
1051
+ ...entry.serviceFactory == null ? {} : { serviceFactory: entry.serviceFactory }
1052
+ };
1053
+ return projected;
1054
+ }
1055
+ function projectModelField(field) {
1056
+ const projected = {
1057
+ name: field.name,
1058
+ longName: field.longName,
1059
+ optional: field.optional,
1060
+ ...field.tsType == null ? {} : { tsType: field.tsType },
1061
+ ...field.description == null ? {} : { description: field.description },
1062
+ ...field.enumRef == null ? {} : { enumRef: field.enumRef },
1063
+ ...field.syncFlag == null ? {} : { syncFlag: field.syncFlag },
1064
+ ...field.nestedFields == null ? {} : { nestedFields: field.nestedFields.map(projectModelField) },
1065
+ ...field.nestedIsArray == null ? {} : { nestedIsArray: field.nestedIsArray }
1066
+ };
1067
+ return projected;
1068
+ }
1069
+ function buildToolEntry(entry) {
1070
+ const description = buildDescription(entry);
1071
+ const inputSchema = buildInputSchema(entry);
1072
+ const outputSchema = buildOutputSchema(entry);
1073
+ const result = {};
1074
+ if (description != null) result.description = description;
1075
+ if (inputSchema != null) result.inputSchema = inputSchema;
1076
+ if (outputSchema != null) result.outputSchema = outputSchema;
1077
+ return result;
1078
+ }
1079
+ function buildDescription(entry) {
1080
+ const parts = [entry.description, entry.paramsTypeDescription].filter((value) => typeof value === "string" && value.length > 0);
1081
+ return parts.length === 0 ? void 0 : parts.join("\n\n");
1082
+ }
1083
+ function buildInputSchema(entry) {
1084
+ const validator = entry.paramsValidator;
1085
+ const baseSchema = validator == null ? void 0 : safeToJsonSchema(validator);
1086
+ const hasFields = entry.paramsFields != null && entry.paramsFields.length > 0;
1087
+ if (baseSchema == null && !hasFields) {
1088
+ return void 0;
1089
+ }
1090
+ const schema = baseSchema == null ? { type: "object" } : cloneJsonObject(baseSchema);
1091
+ const properties = ensureProperties(schema);
1092
+ if (entry.paramsFields != null) {
1093
+ for (const field of entry.paramsFields) {
1094
+ mergeFieldIntoProperties(properties, field);
1095
+ }
1096
+ }
1097
+ return schema;
1098
+ }
1099
+ function buildOutputSchema(entry) {
1100
+ const hasFields = entry.resultFields != null && entry.resultFields.length > 0;
1101
+ const hasDescription = typeof entry.resultTypeDescription === "string" && entry.resultTypeDescription.length > 0;
1102
+ if (!hasFields && !hasDescription) {
1103
+ return void 0;
1104
+ }
1105
+ const schema = { type: "object" };
1106
+ if (hasDescription) {
1107
+ schema["description"] = entry.resultTypeDescription;
1108
+ }
1109
+ if (hasFields) {
1110
+ const properties = {};
1111
+ for (const field of entry.resultFields) {
1112
+ properties[field.name] = buildPropertyFromField(field);
1113
+ }
1114
+ schema["properties"] = properties;
1115
+ }
1116
+ return schema;
1117
+ }
1118
+ function ensureProperties(schema) {
1119
+ const existing = schema["properties"];
1120
+ let properties;
1121
+ if (existing != null && typeof existing === "object") {
1122
+ properties = existing;
1123
+ } else {
1124
+ properties = {};
1125
+ schema["properties"] = properties;
1126
+ }
1127
+ return properties;
1128
+ }
1129
+ function mergeFieldIntoProperties(properties, field) {
1130
+ const existing = properties[field.name];
1131
+ const target = existing != null && typeof existing === "object" ? existing : {};
1132
+ if (target["description"] == null && field.description != null && field.description.length > 0) {
1133
+ target["description"] = field.description;
1134
+ }
1135
+ if (target["type"] == null) {
1136
+ const inferred = inferJsonSchemaType(field.typeText);
1137
+ if (inferred != null) {
1138
+ target["type"] = inferred;
1139
+ }
1140
+ }
1141
+ properties[field.name] = target;
1142
+ }
1143
+ function buildPropertyFromField(field) {
1144
+ const property = {};
1145
+ if (field.description != null && field.description.length > 0) {
1146
+ property["description"] = field.description;
1147
+ }
1148
+ const inferred = inferJsonSchemaType(field.typeText);
1149
+ if (inferred != null) {
1150
+ property["type"] = inferred;
1151
+ }
1152
+ return property;
1153
+ }
1154
+ function inferJsonSchemaType(typeText) {
1155
+ if (typeText == null) {
1156
+ return void 0;
1157
+ }
1158
+ const trimmed = typeText.trim();
1159
+ let result;
1160
+ if (trimmed === "string") {
1161
+ result = "string";
1162
+ } else if (trimmed === "number") {
1163
+ result = "number";
1164
+ } else if (trimmed === "boolean") {
1165
+ result = "boolean";
1166
+ } else if (trimmed.endsWith("[]")) {
1167
+ result = "array";
1168
+ } else if (trimmed.startsWith("Maybe<") && trimmed.endsWith(">")) {
1169
+ result = inferJsonSchemaType(trimmed.slice("Maybe<".length, -1));
1170
+ } else {
1171
+ result = void 0;
1172
+ }
1173
+ return result;
1174
+ }
1175
+ function safeToJsonSchema(validator) {
1176
+ let result;
1177
+ try {
1178
+ const schema = arktypeToJsonSchemaForExport(validator);
1179
+ if (schema != null && typeof schema === "object") {
1180
+ result = schema;
1181
+ }
1182
+ } catch {
1183
+ result = void 0;
1184
+ }
1185
+ return result;
1186
+ }
1187
+ function cloneJsonObject(value) {
1188
+ return structuredClone(value);
1189
+ }
1190
+
1191
+ // packages/dbx-cli/generate-mcp-manifest/src/generate-mcp-manifest/main.ts
1192
+ var WORKSPACE_ROOT = process.cwd();
1193
+ async function main() {
1194
+ const flags = parseFlags(process.argv.slice(2));
1195
+ if (flags.input == null || flags.output == null) {
1196
+ printUsageAndExit();
1197
+ return;
1198
+ }
1199
+ const inputPath = resolveWorkspacePath(flags.input);
1200
+ const outputPath = resolveWorkspacePath(flags.output);
1201
+ if (!existsSync(inputPath)) {
1202
+ const hint = flags.regenerateInput ? "The --regenerate-input flag is reserved for a future revision; for now run the upstream generator manually." : "Pass --regenerate-input is reserved; for now run the upstream generator manually.";
1203
+ console.error(`generate-mcp-manifest: input not found: ${relative2(WORKSPACE_ROOT, inputPath)}
1204
+ ${hint}
1205
+ Expected output of \`nx run <cli>:generate-api-manifest\`.`);
1206
+ process.exit(1);
1207
+ }
1208
+ const loaded = await loadManifest(inputPath);
1209
+ const auth = await maybeLoadAuth(flags);
1210
+ const rendered = renderMcpManifest({ ...loaded, ...auth == null ? {} : { auth } });
1211
+ const serialized = `${JSON.stringify(rendered, null, 2)}
1212
+ `;
1213
+ ensureOutputDir(dirname(outputPath));
1214
+ const tmpPath = `${outputPath}.tmp`;
1215
+ writeFileSync(tmpPath, serialized);
1216
+ renameSync(tmpPath, outputPath);
1217
+ const modelCount = rendered.models?.length ?? 0;
1218
+ const authCount = rendered.auth?.claims.length ?? 0;
1219
+ console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputPath)} \u2014 ${Object.keys(rendered.tools).length} tools, ${modelCount} models, ${authCount} auth claims`);
1220
+ }
1221
+ async function maybeLoadAuth(flags) {
1222
+ if (flags.app == null || flags.claimsInputs.length === 0) {
1223
+ return void 0;
1224
+ }
1225
+ const extraFiles = flags.claimsInputs.map((p) => relative2(WORKSPACE_ROOT, resolveWorkspacePath(p)));
1226
+ const result = await loadAuthRegistry({ cwd: WORKSPACE_ROOT, extraFiles, skipDiscovery: true });
1227
+ for (const warning of result.fileWarnings) {
1228
+ console.error(`[generate-mcp-manifest] auth file warning (${warning.kind}): ${warning.relPath} \u2014 ${warning.error}`);
1229
+ }
1230
+ for (const warning of result.extractWarnings) {
1231
+ console.error(`[generate-mcp-manifest] auth extract warning: ${JSON.stringify(warning)}`);
1232
+ }
1233
+ return { registry: result.registry, app: flags.app };
1234
+ }
1235
+ async function loadManifest(path) {
1236
+ const alias = loadTsconfigPathAliases();
1237
+ const jiti = createJiti(import.meta.url, { interopDefault: true, alias });
1238
+ const loaded = await jiti.import(path);
1239
+ const namedApi = Object.entries(loaded).find(([key]) => key.endsWith("_API_MANIFEST"));
1240
+ const fallback = loaded["default"];
1241
+ const apiManifest = namedApi?.[1] ?? fallback;
1242
+ if (apiManifest == null || !Array.isArray(apiManifest)) {
1243
+ throw new Error(`generate-mcp-manifest: ${path} does not export an *_API_MANIFEST array or a default export of CliApiManifest.`);
1244
+ }
1245
+ const namedModel = Object.entries(loaded).find(([key]) => key.endsWith("_MODEL_MANIFEST"));
1246
+ const modelManifestValue = namedModel?.[1];
1247
+ const modelManifest = Array.isArray(modelManifestValue) ? modelManifestValue : void 0;
1248
+ return { apiManifest, modelManifest };
1249
+ }
1250
+ function loadTsconfigPathAliases() {
1251
+ const tsconfigPath = resolve(WORKSPACE_ROOT, "tsconfig.base.json");
1252
+ let result = {};
1253
+ if (existsSync(tsconfigPath)) {
1254
+ const raw = readFileSync(tsconfigPath, "utf8");
1255
+ const stripped = raw.replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/(^|[^:])\/\/[^\n]*/g, "$1");
1256
+ const parsed = JSON.parse(stripped);
1257
+ const paths = parsed.compilerOptions?.paths ?? {};
1258
+ result = Object.fromEntries(Object.entries(paths).flatMap(([key, values]) => values.length > 0 ? [[key, resolve(WORKSPACE_ROOT, values[0])]] : []));
1259
+ }
1260
+ return result;
1261
+ }
1262
+ function ensureOutputDir(outputDir) {
1263
+ if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
1264
+ }
1265
+ function resolveWorkspacePath(value) {
1266
+ return isAbsolute(value) ? value : resolve(WORKSPACE_ROOT, value);
1267
+ }
1268
+ function parseFlags(argv) {
1269
+ let input;
1270
+ let output;
1271
+ let app;
1272
+ const claimsInputs = [];
1273
+ let regenerateInput = false;
1274
+ for (const arg of argv) {
1275
+ if (arg === "--regenerate-input") {
1276
+ regenerateInput = true;
1277
+ } else if (arg.startsWith("--input=")) {
1278
+ input = arg.slice("--input=".length);
1279
+ } else if (arg.startsWith("--output=")) {
1280
+ output = arg.slice("--output=".length);
1281
+ } else if (arg.startsWith("--app=")) {
1282
+ app = arg.slice("--app=".length);
1283
+ } else if (arg.startsWith("--claims-input=")) {
1284
+ claimsInputs.push(arg.slice("--claims-input=".length));
1285
+ }
1286
+ }
1287
+ return { input, output, app, claimsInputs, regenerateInput };
1288
+ }
1289
+ function printUsageAndExit() {
1290
+ console.error(String.raw`generate-mcp-manifest
1291
+
1292
+ Usage:
1293
+ node dist/packages/dbx-cli/generate-mcp-manifest/main.js \
1294
+ --input=<path-to-api.manifest.generated.ts> \
1295
+ --output=<path-to-mcp.manifest.json>
1296
+
1297
+ Required flags:
1298
+ --input=<path> Path to the API manifest TS file (workspace-relative ok).
1299
+ --output=<path> Path to the MCP manifest JSON to write (workspace-relative ok).
1300
+
1301
+ Optional:
1302
+ --regenerate-input Reserved for a future revision.
1303
+ --app=<slug> Host app slug whose auth catalog to project (e.g. demo-api).
1304
+ When set alongside --claims-input, emits an \`auth\` section.
1305
+ --claims-input=<path> Path to a claims.ts module. Repeatable. Skips workspace
1306
+ discovery — only the explicit paths are scanned.`);
1307
+ process.exit(1);
1308
+ }
1309
+ try {
1310
+ await main();
1311
+ } catch (e) {
1312
+ console.error(e);
1313
+ process.exit(1);
1314
+ }