@keystrokehq/cli 0.0.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 (122) hide show
  1. package/AGENTS-blurb.md +123 -0
  2. package/LICENSE +42 -0
  3. package/README.md +177 -0
  4. package/THIRD_PARTY_NOTICES.md +16 -0
  5. package/bin/keystroke.mjs +107 -0
  6. package/dist/_manifest-JSRE3H8k.mjs +385 -0
  7. package/dist/agent-bundle-package-DWV6B_5q-BtV7Xycc.mjs +2344 -0
  8. package/dist/agent-manifest-CDnbkR2f.mjs +245 -0
  9. package/dist/agents-CZJGxVqV.mjs +228 -0
  10. package/dist/api-keys-D2lgguuY.mjs +40 -0
  11. package/dist/auth-DN2VusyU.mjs +59 -0
  12. package/dist/auth.handler-CT1BQUvu.mjs +340 -0
  13. package/dist/browser-qwFrUH82.mjs +24 -0
  14. package/dist/build-agents-BmM_AsSd-BGi9wtzt.mjs +514 -0
  15. package/dist/build-metadata-BWS7uhd_-DR8gJjTX.mjs +1422 -0
  16. package/dist/build-progress-DgYKb4hB.mjs +183 -0
  17. package/dist/build-tasks-CdihpudT-D5r5HUHe.mjs +91 -0
  18. package/dist/build-workflows-CfxBnIWh-CdYPv8w2.mjs +370 -0
  19. package/dist/build.handler-4799CjWH.mjs +36 -0
  20. package/dist/chunk-CH6r78ws.mjs +37 -0
  21. package/dist/clear-cache.handler-B9tqSoSM.mjs +11 -0
  22. package/dist/clear.handler-BTIXXPTJ.mjs +42 -0
  23. package/dist/clear.handler-BydlX-zE.mjs +11 -0
  24. package/dist/commander-DfTVqQ-3.mjs +133 -0
  25. package/dist/concurrency-gXn9Rw8x-DNl2YtrS.mjs +20 -0
  26. package/dist/connect-BUXkeH0F.mjs +43 -0
  27. package/dist/connect.handler-CYel9cy6.mjs +430 -0
  28. package/dist/constants-CPpPdSNg.mjs +8 -0
  29. package/dist/context-T7HZuB97.mjs +138 -0
  30. package/dist/credential-env-map-CI8yWHVy.mjs +28 -0
  31. package/dist/credential-schema-mismatch-BKo5PjcQ.mjs +76 -0
  32. package/dist/credentials-CvmjU0lK.mjs +171 -0
  33. package/dist/credentials-OfVHOtG3.mjs +151216 -0
  34. package/dist/current-deployment-workflow-poHt27i3.mjs +94 -0
  35. package/dist/current.handler-B8zKzfPp.mjs +21 -0
  36. package/dist/delete.handler-bAu1iXVQ.mjs +17 -0
  37. package/dist/deploy-7Jjls436.mjs +26 -0
  38. package/dist/deploy-BOPIpRWm.mjs +74 -0
  39. package/dist/deploy-progress-BmGUNFKg.mjs +70 -0
  40. package/dist/deploy.handler-BAzgiNhd.mjs +370 -0
  41. package/dist/detect-env-access-CwkOYeYM-D_BCZqV6.mjs +209 -0
  42. package/dist/diff-utils-NEfcjqxt.mjs +185 -0
  43. package/dist/diff.handler-Du7SY8K4.mjs +47 -0
  44. package/dist/dist-BkJUoBiG.mjs +1116 -0
  45. package/dist/dist-CUK7yBM0.mjs +308 -0
  46. package/dist/env-91KwMKov.mjs +140 -0
  47. package/dist/env.handler-BAzBuMzQ.mjs +277 -0
  48. package/dist/error-boundary-VL-JLfIa.mjs +34 -0
  49. package/dist/file-metadata-D1vm-XY2.mjs +191 -0
  50. package/dist/get-intrinsic-zLxwtrLK.mjs +658 -0
  51. package/dist/import-module-CV84H5fZ-B_CBCmb4.mjs +1747 -0
  52. package/dist/init-DpMCotSK.mjs +45 -0
  53. package/dist/init.handler-CPRnif52.mjs +585 -0
  54. package/dist/inspect.handler-DT_cD036.mjs +146 -0
  55. package/dist/integration-catalog-Bt-L3GjF.mjs +104 -0
  56. package/dist/integrations-DlatPK4W.mjs +79 -0
  57. package/dist/keystroke.d.mts +3 -0
  58. package/dist/keystroke.mjs +707 -0
  59. package/dist/layout-CbMtQ2tm.mjs +67 -0
  60. package/dist/list-enrichment-y-cwizLr.mjs +189 -0
  61. package/dist/list.handler-BTWvCyjA.mjs +52 -0
  62. package/dist/list.handler-CWF_Dj15.mjs +24 -0
  63. package/dist/list.handler-CZ6G2x_G.mjs +75 -0
  64. package/dist/list.handler-DWaQkJaR.mjs +51 -0
  65. package/dist/list.handler-DqbFcBW7.mjs +180 -0
  66. package/dist/list.handler-lq3ZGAn4.mjs +104 -0
  67. package/dist/logs-BEg9L5l8.mjs +28 -0
  68. package/dist/logs.handler-6hoMBzqw.mjs +35 -0
  69. package/dist/logs.handler-BD_dXiL1.mjs +231 -0
  70. package/dist/metadata-layout-GUYIUo0i-_aG2zjue.mjs +5877 -0
  71. package/dist/normalize-path-CojS-CgQ-DLCOvnD1.mjs +20 -0
  72. package/dist/options-CeaTcFxP.mjs +43 -0
  73. package/dist/org-xLzBtt2_.mjs +41 -0
  74. package/dist/output-DM4b7KgY.mjs +72 -0
  75. package/dist/oxc-B3KI3rf_-n9d1hKNq.mjs +119 -0
  76. package/dist/paused.handler-BMFm9Cff.mjs +94 -0
  77. package/dist/project-config-D1qsQlO7.mjs +107 -0
  78. package/dist/projects-CHkRE9rS.mjs +1574 -0
  79. package/dist/projects-Cjb7sovS.mjs +30 -0
  80. package/dist/read-credential-keys-77a91T8M-KA0Iw0Z1.mjs +9 -0
  81. package/dist/register.handler-BPCdor1_.mjs +86 -0
  82. package/dist/requirements.handler-DPXdSks3.mjs +201 -0
  83. package/dist/resolve-project-DDJ29sCF.mjs +35 -0
  84. package/dist/rolldown-runtime-twds-ZHy-BWWzu8VG.mjs +15 -0
  85. package/dist/run-polling-CAgFRdK3.mjs +20 -0
  86. package/dist/runs-D9hNLb9A.mjs +259 -0
  87. package/dist/schedule-BXx3uXwr.mjs +1142 -0
  88. package/dist/schema-17qMfNyI.mjs +18 -0
  89. package/dist/schema-display-CgmeKigW.mjs +130 -0
  90. package/dist/schemas-CDib1RhE.mjs +125 -0
  91. package/dist/skills-sync.handler-DIy8GR16.mjs +34 -0
  92. package/dist/skills.command-CrjI2dN9.mjs +35 -0
  93. package/dist/skills.handler-Bz8bJKql.mjs +9 -0
  94. package/dist/source-analysis-Cj-ADyu--BJQcFPCG.mjs +144 -0
  95. package/dist/spinner-progress-DMVwgqO9.mjs +173 -0
  96. package/dist/src-C0X6u_Mw.mjs +1340 -0
  97. package/dist/src-eHwu-Gfw.mjs +369 -0
  98. package/dist/status.handler-BO4nwvWn.mjs +101 -0
  99. package/dist/switch.handler-D_9213Vf.mjs +51 -0
  100. package/dist/sync-BL_Mo5st.mjs +39 -0
  101. package/dist/sync-keystroke-agent-skills-Kx_H7UTd.mjs +70 -0
  102. package/dist/sync.handler-BUFPdzWz.mjs +82 -0
  103. package/dist/task-B2sZMaZu.mjs +8 -0
  104. package/dist/task-target-build-CBeCKbu2.mjs +432 -0
  105. package/dist/task-target-deploy-C5X-USeR.mjs +4 -0
  106. package/dist/task-target-deploy-CA6elFpF-BEr4gkol.mjs +271 -0
  107. package/dist/task-target-deploy-runner.d.mts +3 -0
  108. package/dist/task-target-deploy-runner.mjs +202 -0
  109. package/dist/test-BHTgR3UA.mjs +698 -0
  110. package/dist/test.handler-BcPQ8b74.mjs +13 -0
  111. package/dist/trigger-artifacts-DQPbQNqC-B4yeeFBY.mjs +239 -0
  112. package/dist/trigger-manifest-CY7brZeg.mjs +30 -0
  113. package/dist/try-deploy.handler-DqybNhXx.mjs +490 -0
  114. package/dist/upload-CkU--iDC.mjs +207 -0
  115. package/dist/upload.handler-DCtiznQp.mjs +441 -0
  116. package/dist/utils-CywxCDM7.mjs +14 -0
  117. package/dist/validate.handler-DOcTaJL0.mjs +280 -0
  118. package/dist/workflow-build-DBQaBfnn.mjs +1819 -0
  119. package/dist/workflow-bundler-BPiqVscj-X1PFFAuP.mjs +167 -0
  120. package/dist/workflows-g9z87AJJ.mjs +799 -0
  121. package/dist/writer-BG8poUm3-BbXlU2kI.mjs +426 -0
  122. package/package.json +87 -0
@@ -0,0 +1,1422 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { i as __toESM } from "./chunk-CH6r78ws.mjs";
4
+ import { n as FileMetadataSchema, t as FILE_METADATA_SCHEMA_VERSION } from "./file-metadata-D1vm-XY2.mjs";
5
+ import { d as getMetadataRoot, l as createMetadataOutputFile, n as BUILD_OUTPUT_DIR_NAME, u as getFileMetadataPath } from "./layout-CbMtQ2tm.mjs";
6
+ import { l as require_out, s as sha256String, t as BASE_IGNORE_PATTERNS } from "./metadata-layout-GUYIUo0i-_aG2zjue.mjs";
7
+ import { a as literalString, c as readStringProperty, i as literalNumber, l as unwrapExpression, n as identifierName, o as parseSourceFile, r as isNode, s as readPropertyValue, t as getFirstObjectArgument, u as visitNode } from "./oxc-B3KI3rf_-n9d1hKNq.mjs";
8
+ import { a as getLocalModuleSpecifier, f as resolveLocalModulePath, l as removeEmptyMetadataDirectories, o as getVariableDeclarators, s as isExportedVariableStatement } from "./source-analysis-Cj-ADyu--BJQcFPCG.mjs";
9
+ import { n as parseRelativeFilePath, r as toRelativeFilePath, t as normalizeRelativeFilePath } from "./normalize-path-CojS-CgQ-DLCOvnD1.mjs";
10
+ import { t as RESERVED_NAMESPACES } from "./constants-CPpPdSNg.mjs";
11
+ import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
12
+ import path from "node:path";
13
+ import { performance } from "node:perf_hooks";
14
+ //#region ../../packages/workflow-builder/dist/build-metadata-BWS7uhd_.mjs
15
+ var import_out = /* @__PURE__ */ __toESM(require_out(), 1);
16
+ const IGNORED_IDENTIFIERS = new Set([
17
+ "undefined",
18
+ "null",
19
+ "NaN",
20
+ "Infinity",
21
+ "globalThis",
22
+ "arguments",
23
+ "console",
24
+ "Promise",
25
+ "Math",
26
+ "JSON",
27
+ "Date",
28
+ "Error",
29
+ "Array",
30
+ "Object",
31
+ "String",
32
+ "Number",
33
+ "Boolean",
34
+ "Map",
35
+ "Set",
36
+ "WeakMap",
37
+ "WeakSet",
38
+ "Symbol",
39
+ "RegExp",
40
+ "parseInt",
41
+ "parseFloat",
42
+ "isNaN",
43
+ "isFinite",
44
+ "setTimeout",
45
+ "setInterval",
46
+ "clearTimeout",
47
+ "clearInterval",
48
+ "fetch",
49
+ "Buffer",
50
+ "process",
51
+ "URL",
52
+ "URLSearchParams",
53
+ "AbortController",
54
+ "AbortSignal",
55
+ "TextEncoder",
56
+ "TextDecoder",
57
+ "crypto",
58
+ "performance",
59
+ "queueMicrotask",
60
+ "structuredClone",
61
+ "atob",
62
+ "btoa"
63
+ ]);
64
+ const IGNORED_IMPORT_SPECIFIERS = new Set(["zod", "zod/v4"]);
65
+ const IGNORED_IMPORT_PREFIXES = ["@keystroke/workflow-core"];
66
+ function analyzeCapturedVariables(sourceFile, definitionAnalysis, relativeFilePath, imports) {
67
+ const result = /* @__PURE__ */ new Map();
68
+ const fileScopeConsts = collectFileScopeConsts(sourceFile);
69
+ const importBindings = buildImportBindingsFromEntries(imports);
70
+ const definitionNames = new Set(definitionAnalysis.localDefinitions.keys());
71
+ const ignoredImportNames = /* @__PURE__ */ new Set();
72
+ for (const binding of importBindings.values()) if (isIgnoredImport(binding.moduleSpecifier)) ignoredImportNames.add(binding.localName);
73
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
74
+ const varName = identifierName(declaration.id);
75
+ if (!varName || !isNode(declaration.init)) continue;
76
+ const localDef = definitionAnalysis.localDefinitions.get(varName);
77
+ if (!localDef || localDef.kind !== "workflow" && localDef.kind !== "operation" && localDef.kind !== "agent") continue;
78
+ const configObject = getFirstObjectArgument(declaration.init);
79
+ if (!configObject) continue;
80
+ const runFn = findRunProperty(configObject);
81
+ if (!runFn) continue;
82
+ const functionScopeNames = collectFunctionScopeNames(runFn);
83
+ const referencedIdentifiers = collectReferencedIdentifiers(runFn);
84
+ const captured = /* @__PURE__ */ new Map();
85
+ for (const identifier of referencedIdentifiers) {
86
+ if (IGNORED_IDENTIFIERS.has(identifier) || functionScopeNames.has(identifier) || definitionNames.has(identifier) || ignoredImportNames.has(identifier) || captured.has(identifier)) continue;
87
+ const fileScopeConst = fileScopeConsts.get(identifier);
88
+ if (fileScopeConst) {
89
+ captured.set(identifier, buildLocalConstVariable(fileScopeConst, sourceFile, relativeFilePath));
90
+ continue;
91
+ }
92
+ const importBinding = importBindings.get(identifier);
93
+ if (importBinding) captured.set(identifier, buildImportVariable(importBinding, relativeFilePath));
94
+ }
95
+ if (captured.size > 0) result.set(varName, Array.from(captured.values()));
96
+ }
97
+ return result;
98
+ }
99
+ function collectFileScopeConsts(sourceFile) {
100
+ const consts = /* @__PURE__ */ new Map();
101
+ for (const statement of sourceFile.statements) {
102
+ const declaration = statement.type === "VariableDeclaration" ? statement : statement.type === "ExportNamedDeclaration" && isNode(statement.declaration) ? statement.declaration : null;
103
+ if (!declaration || declaration.type !== "VariableDeclaration" || declaration.kind !== "const") continue;
104
+ for (const variable of getVariableDeclarators(statement)) {
105
+ const name = identifierName(variable.id);
106
+ if (!name || !isNode(variable.init)) continue;
107
+ const { line } = sourceFile.getLineAndCharacterOfPosition(isNode(variable.id) ? variable.id.start : variable.start);
108
+ consts.set(name, {
109
+ name,
110
+ initializer: variable.init,
111
+ line: line + 1
112
+ });
113
+ }
114
+ }
115
+ return consts;
116
+ }
117
+ function buildImportBindingsFromEntries(imports) {
118
+ const bindings = /* @__PURE__ */ new Map();
119
+ for (const entry of imports) {
120
+ if (entry.isNamespaceImport && entry.namespaceName) {
121
+ bindings.set(entry.namespaceName, {
122
+ localName: entry.namespaceName,
123
+ importedName: "*",
124
+ moduleSpecifier: entry.moduleSpecifier
125
+ });
126
+ continue;
127
+ }
128
+ for (const name of entry.importNames) bindings.set(name, {
129
+ localName: name,
130
+ importedName: name,
131
+ moduleSpecifier: entry.moduleSpecifier
132
+ });
133
+ }
134
+ return bindings;
135
+ }
136
+ function findRunProperty(objectLiteral) {
137
+ const runValue = readPropertyValue(objectLiteral, "run");
138
+ if (runValue?.type === "ArrowFunctionExpression" || runValue?.type === "FunctionExpression" || runValue?.type === "FunctionDeclaration") return runValue;
139
+ return null;
140
+ }
141
+ function collectFunctionScopeNames(fn) {
142
+ const names = /* @__PURE__ */ new Set();
143
+ for (const param of Array.isArray(fn.params) ? fn.params : []) collectBindingNames(param, names);
144
+ if (isNode(fn.body) && fn.body.type === "BlockStatement") collectLocalDeclarations(fn.body, names);
145
+ return names;
146
+ }
147
+ function collectBindingNames(name, names) {
148
+ if (!isNode(name)) return;
149
+ const identifier = identifierName(name);
150
+ if (identifier) {
151
+ names.add(identifier);
152
+ return;
153
+ }
154
+ if (name.type === "ObjectPattern") {
155
+ for (const property of Array.isArray(name.properties) ? name.properties : []) if (isNode(property) && property.type === "Property") collectBindingNames(property.value, names);
156
+ else if (isNode(property) && property.type === "RestElement") collectBindingNames(property.argument, names);
157
+ return;
158
+ }
159
+ if (name.type === "ArrayPattern") {
160
+ for (const element of Array.isArray(name.elements) ? name.elements : []) collectBindingNames(element, names);
161
+ return;
162
+ }
163
+ if (name.type === "AssignmentPattern") {
164
+ collectBindingNames(name.left, names);
165
+ return;
166
+ }
167
+ if (name.type === "RestElement") collectBindingNames(name.argument, names);
168
+ }
169
+ function collectLocalDeclarations(block, names) {
170
+ visitNode(block, (node) => {
171
+ if (node.type === "VariableDeclarator") collectBindingNames(node.id, names);
172
+ if ((node.type === "FunctionDeclaration" || node.type === "ClassDeclaration") && isNode(node.id)) {
173
+ const name = identifierName(node.id);
174
+ if (name) names.add(name);
175
+ return false;
176
+ }
177
+ if (node !== block && (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression")) return false;
178
+ if (node.type === "CatchClause") collectBindingNames(node.param, names);
179
+ });
180
+ }
181
+ function collectReferencedIdentifiers(fn) {
182
+ const identifiers = /* @__PURE__ */ new Set();
183
+ const body = fn.body;
184
+ if (!isNode(body)) return identifiers;
185
+ visitNode(body, (node, parent) => {
186
+ const identifier = identifierName(node);
187
+ if (!identifier) return;
188
+ if (parent?.type === "MemberExpression" && parent.computed === false && parent.property === node) return;
189
+ if (parent?.type === "Property" && parent.key === node && parent.value !== node) return;
190
+ identifiers.add(identifier);
191
+ });
192
+ return identifiers;
193
+ }
194
+ function buildLocalConstVariable(fileScopeConst, sourceFile, relativeFilePath) {
195
+ const { valueType, value, sourceText } = classifyInitializer(fileScopeConst.initializer, sourceFile);
196
+ const resolvable = value !== void 0;
197
+ return {
198
+ name: fileScopeConst.name,
199
+ ...value !== void 0 ? { value } : {},
200
+ ...sourceText !== void 0 ? { sourceText } : {},
201
+ valueType,
202
+ resolvable,
203
+ source: "local-const",
204
+ declaration: {
205
+ filePath: relativeFilePath,
206
+ line: fileScopeConst.line
207
+ }
208
+ };
209
+ }
210
+ function buildImportVariable(binding, relativeFilePath) {
211
+ const source = binding.moduleSpecifier.startsWith(".") || binding.moduleSpecifier.startsWith("/") ? "relative-import" : "package-import";
212
+ return {
213
+ name: binding.localName,
214
+ valueType: "unknown",
215
+ resolvable: false,
216
+ source,
217
+ importPath: binding.moduleSpecifier,
218
+ declaration: {
219
+ filePath: relativeFilePath,
220
+ line: 1
221
+ }
222
+ };
223
+ }
224
+ function classifyInitializer(node, sourceFile) {
225
+ const unwrapped = unwrapExpression(node);
226
+ const stringValue = literalString(unwrapped);
227
+ if (stringValue !== void 0) return {
228
+ valueType: "string",
229
+ value: stringValue
230
+ };
231
+ const numberValue = literalNumber(unwrapped);
232
+ if (numberValue !== void 0) return {
233
+ valueType: "number",
234
+ value: numberValue
235
+ };
236
+ if (unwrapped.type === "Literal" && typeof unwrapped.value === "boolean") return {
237
+ valueType: "boolean",
238
+ value: unwrapped.value
239
+ };
240
+ if (unwrapped.type === "UnaryExpression" && unwrapped.operator === "-" && isNode(unwrapped.argument)) {
241
+ const operand = literalNumber(unwrapped.argument);
242
+ if (operand !== void 0) return {
243
+ valueType: "number",
244
+ value: -operand
245
+ };
246
+ }
247
+ if (unwrapped.type === "ObjectExpression") return {
248
+ valueType: "object",
249
+ sourceText: truncateSourceText(sourceFile.getText(unwrapped))
250
+ };
251
+ if (unwrapped.type === "ArrayExpression") return {
252
+ valueType: "array",
253
+ sourceText: truncateSourceText(sourceFile.getText(unwrapped))
254
+ };
255
+ if (unwrapped.type === "ArrowFunctionExpression" || unwrapped.type === "FunctionExpression") return {
256
+ valueType: "function",
257
+ sourceText: truncateSourceText(sourceFile.getText(unwrapped))
258
+ };
259
+ return {
260
+ valueType: "unknown",
261
+ sourceText: truncateSourceText(sourceFile.getText(unwrapped))
262
+ };
263
+ }
264
+ function truncateSourceText(text, maxLength = 200) {
265
+ const trimmed = text.replace(/\s+/g, " ").trim();
266
+ if (trimmed.length <= maxLength) return trimmed;
267
+ return `${trimmed.slice(0, maxLength - 3)}...`;
268
+ }
269
+ function isIgnoredImport(moduleSpecifier) {
270
+ if (IGNORED_IMPORT_SPECIFIERS.has(moduleSpecifier)) return true;
271
+ return IGNORED_IMPORT_PREFIXES.some((prefix) => moduleSpecifier.startsWith(prefix));
272
+ }
273
+ const CORE_IMPORT_PREFIX$1 = "@keystroke/workflow-core";
274
+ function analyzeCredentials(sourceFile) {
275
+ const localCredentialSetIds = collectLocalCredentialSetIds(sourceFile, collectImportedCoreSymbols$1(sourceFile));
276
+ const credentials = /* @__PURE__ */ new Map();
277
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
278
+ if (!isNode(declaration.init)) continue;
279
+ collectCredentialRefsFromExpression(declaration.init, localCredentialSetIds, credentials);
280
+ }
281
+ return Array.from(credentials.values()).sort((left, right) => left.resolvedId.localeCompare(right.resolvedId));
282
+ }
283
+ function collectImportedCoreSymbols$1(sourceFile) {
284
+ const importedSymbols = new Map([["CredentialSet", "CredentialSet"]]);
285
+ for (const statement of sourceFile.statements) {
286
+ if (statement.type !== "ImportDeclaration") continue;
287
+ if (!literalString(statement.source)?.startsWith(CORE_IMPORT_PREFIX$1) || !Array.isArray(statement.specifiers)) continue;
288
+ for (const element of statement.specifiers) {
289
+ if (!isNode(element) || element.type !== "ImportSpecifier") continue;
290
+ const localName = identifierName(element.local);
291
+ const importedName = identifierName(element.imported);
292
+ if (localName && importedName) importedSymbols.set(localName, importedName);
293
+ }
294
+ }
295
+ return importedSymbols;
296
+ }
297
+ function collectLocalCredentialSetIds(sourceFile, importedCoreSymbols) {
298
+ const credentialSetIds = /* @__PURE__ */ new Map();
299
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
300
+ const localName = identifierName(declaration.id);
301
+ if (!localName || !isNode(declaration.init)) continue;
302
+ const initializer = unwrapExpression(declaration.init);
303
+ if (initializer.type !== "NewExpression") continue;
304
+ const calleeName = identifierName(initializer.callee);
305
+ if (!calleeName || importedCoreSymbols.get(calleeName) !== "CredentialSet") continue;
306
+ const configObject = getFirstObjectArgument(initializer);
307
+ const credentialSetId = readStringProperty(configObject, "id");
308
+ const namespace = readStringProperty(configObject, "namespace");
309
+ if (namespace && RESERVED_NAMESPACES.includes(namespace)) throw new Error(`CredentialSet "${credentialSetId ?? localName}": namespace "${namespace}" is reserved for official Keystroke integrations. Use a different namespace or remove it.`);
310
+ if (credentialSetId) {
311
+ const resolvedId = namespace ? `${namespace}:${credentialSetId}` : credentialSetId;
312
+ credentialSetIds.set(localName, resolvedId);
313
+ }
314
+ }
315
+ return credentialSetIds;
316
+ }
317
+ function collectCredentialRefsFromExpression(expression, localCredentialSetIds, credentials) {
318
+ const unwrapped = unwrapExpression(expression);
319
+ if (unwrapped.type === "NewExpression") {
320
+ addCredentialRefsFromConfigObject(getFirstObjectArgument(unwrapped), localCredentialSetIds, credentials);
321
+ return;
322
+ }
323
+ if (unwrapped.type === "CallExpression" && isNode(unwrapped.callee)) {
324
+ if (unwrapped.callee.type !== "MemberExpression") return;
325
+ for (const argument of Array.isArray(unwrapped.arguments) ? unwrapped.arguments : []) {
326
+ const objectArgument = isNode(argument) ? unwrapExpression(argument) : null;
327
+ if (objectArgument?.type === "ObjectExpression") addCredentialRefsFromConfigObject(objectArgument, localCredentialSetIds, credentials);
328
+ }
329
+ }
330
+ }
331
+ function addCredentialRefsFromConfigObject(objectLiteral, localCredentialSetIds, credentials) {
332
+ const credentialArray = unwrapArrayLiteral(readPropertyValue(objectLiteral, "credentialSets"));
333
+ if (!credentialArray) return;
334
+ for (const element of credentialArray.elements ?? []) {
335
+ if (!isNode(element)) continue;
336
+ const identifier = identifierName(element);
337
+ if (identifier) {
338
+ const resolvedId = localCredentialSetIds.get(identifier);
339
+ if (resolvedId) credentials.set(resolvedId, { resolvedId });
340
+ continue;
341
+ }
342
+ const unwrapped = unwrapExpression(element);
343
+ if (unwrapped.type === "NewExpression") {
344
+ const configObject = getFirstObjectArgument(unwrapped);
345
+ const credentialSetId = readStringProperty(configObject, "id");
346
+ if (credentialSetId) {
347
+ const namespace = readStringProperty(configObject, "namespace");
348
+ const resolvedId = namespace ? `${namespace}:${credentialSetId}` : credentialSetId;
349
+ credentials.set(resolvedId, { resolvedId });
350
+ }
351
+ }
352
+ }
353
+ }
354
+ function unwrapArrayLiteral(expression) {
355
+ if (!expression) return null;
356
+ const unwrapped = unwrapExpression(expression);
357
+ return unwrapped.type === "ArrayExpression" && Array.isArray(unwrapped.elements) ? unwrapped : null;
358
+ }
359
+ function createSourceLocation(sourceFile, node, relativeFilePath) {
360
+ const start = sourceFile.getLineAndCharacterOfPosition(node.start);
361
+ const end = sourceFile.getLineAndCharacterOfPosition(node.end);
362
+ return {
363
+ filePath: relativeFilePath,
364
+ line: start.line + 1,
365
+ column: start.character + 1,
366
+ endLine: end.line + 1,
367
+ endColumn: end.character + 1
368
+ };
369
+ }
370
+ const CORE_IMPORT_PREFIX = "@keystroke/workflow-core";
371
+ const IMPORTED_CORE_SYMBOLS = new Set([
372
+ "Workflow",
373
+ "Step",
374
+ "Tool",
375
+ "Operation",
376
+ "Agent",
377
+ "CredentialSet",
378
+ "Sandbox",
379
+ "McpServer",
380
+ "CronTrigger",
381
+ "WebhookTrigger",
382
+ "PollingTrigger",
383
+ "cronTrigger",
384
+ "webhookTrigger",
385
+ "pollingTrigger"
386
+ ]);
387
+ function analyzeDefinitions(sourceFile, relativeFilePath) {
388
+ const importedCoreSymbols = collectImportedCoreSymbols(sourceFile);
389
+ const importedTriggerBindings = collectImportedTriggerBindings(sourceFile);
390
+ const localDefinitions = /* @__PURE__ */ new Map();
391
+ const definitions = {
392
+ workflows: [],
393
+ operations: [],
394
+ agents: [],
395
+ triggers: { defined: [] },
396
+ credentialSets: [],
397
+ sandboxes: [],
398
+ mcpServers: []
399
+ };
400
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
401
+ const localName = identifierName(declaration.id);
402
+ if (!localName || !isNode(declaration.init)) continue;
403
+ const recognizedDefinition = getRecognizedDefinition(declaration.init, importedCoreSymbols, importedTriggerBindings);
404
+ if (!recognizedDefinition) continue;
405
+ localDefinitions.set(localName, recognizedDefinition);
406
+ if (!isExportedVariableStatement(statement)) continue;
407
+ addExportedDefinition({
408
+ definitions,
409
+ exportName: localName,
410
+ record: recognizedDefinition,
411
+ relativeFilePath,
412
+ sourceFile
413
+ });
414
+ }
415
+ for (const statement of sourceFile.statements) {
416
+ if (statement.type !== "ExportNamedDeclaration" || !Array.isArray(statement.specifiers)) continue;
417
+ for (const specifier of statement.specifiers) {
418
+ if (!isNode(specifier) || specifier.type !== "ExportSpecifier") continue;
419
+ const localName = identifierName(specifier.local);
420
+ const exportName = identifierName(specifier.exported);
421
+ if (!localName || !exportName) continue;
422
+ const localDefinition = localDefinitions.get(localName);
423
+ if (!localDefinition) continue;
424
+ addExportedDefinition({
425
+ definitions,
426
+ exportName,
427
+ record: localDefinition,
428
+ relativeFilePath,
429
+ sourceFile
430
+ });
431
+ }
432
+ }
433
+ for (const statement of sourceFile.statements) {
434
+ if (statement.type !== "ExportDefaultDeclaration" || !isNode(statement.declaration)) continue;
435
+ const record = getDefaultExportDefinition(statement.declaration, importedCoreSymbols, importedTriggerBindings, localDefinitions);
436
+ if (!record) continue;
437
+ addExportedDefinition({
438
+ definitions,
439
+ exportName: "default",
440
+ record,
441
+ relativeFilePath,
442
+ sourceFile
443
+ });
444
+ }
445
+ return {
446
+ definitions,
447
+ localDefinitions
448
+ };
449
+ }
450
+ function collectImportedCoreSymbols(sourceFile) {
451
+ const importedSymbols = new Map([...IMPORTED_CORE_SYMBOLS].map((symbol) => [symbol, symbol]));
452
+ for (const statement of sourceFile.statements) {
453
+ if (statement.type !== "ImportDeclaration") continue;
454
+ if (!literalString(statement.source)?.startsWith(CORE_IMPORT_PREFIX) || !Array.isArray(statement.specifiers)) continue;
455
+ for (const element of statement.specifiers) {
456
+ if (!isNode(element) || element.type !== "ImportSpecifier") continue;
457
+ const importedName = identifierName(element.imported);
458
+ const localName = identifierName(element.local);
459
+ if (!importedName || !localName || !IMPORTED_CORE_SYMBOLS.has(importedName)) continue;
460
+ importedSymbols.set(localName, importedName);
461
+ }
462
+ }
463
+ return importedSymbols;
464
+ }
465
+ function collectImportedTriggerBindings(sourceFile) {
466
+ const importedBindings = /* @__PURE__ */ new Map();
467
+ for (const statement of sourceFile.statements) {
468
+ if (statement.type !== "ImportDeclaration") continue;
469
+ const moduleSpecifier = literalString(statement.source);
470
+ if (!moduleSpecifier || !moduleSpecifier.endsWith("/triggers") && moduleSpecifier !== "./triggers" || !Array.isArray(statement.specifiers)) continue;
471
+ for (const element of statement.specifiers) {
472
+ if (!isNode(element)) continue;
473
+ const localName = element.type === "ImportNamespaceSpecifier" || element.type === "ImportSpecifier" ? identifierName(element.local) : void 0;
474
+ if (localName) importedBindings.set(localName, { moduleSpecifier });
475
+ }
476
+ }
477
+ return importedBindings;
478
+ }
479
+ function getRecognizedDefinition(expression, importedCoreSymbols, importedTriggerBindings) {
480
+ const unwrappedExpression = unwrapExpression(expression);
481
+ if (unwrappedExpression.type === "NewExpression") {
482
+ const callee = isNode(unwrappedExpression.callee) ? unwrappedExpression.callee : null;
483
+ const importedSymbol = callee ? importedCoreSymbols.get(identifierName(callee) ?? "") : void 0;
484
+ if (!importedSymbol || !callee) return null;
485
+ const configObject = getFirstObjectArgument(unwrappedExpression);
486
+ const displayName = readStringProperty(configObject, "name");
487
+ if (importedSymbol === "Workflow") return {
488
+ exportEntryKind: "workflow",
489
+ kind: "workflow",
490
+ name: displayName,
491
+ sourceNode: callee
492
+ };
493
+ if (importedSymbol === "Step" || importedSymbol === "Tool" || importedSymbol === "Operation") return {
494
+ exportEntryKind: "operation",
495
+ kind: "operation",
496
+ name: displayName,
497
+ sourceNode: callee
498
+ };
499
+ if (importedSymbol === "Agent") return {
500
+ exportEntryKind: "agent",
501
+ kind: "agent",
502
+ name: displayName,
503
+ sourceNode: callee
504
+ };
505
+ if (importedSymbol === "CredentialSet") {
506
+ const id = readStringProperty(configObject, "id");
507
+ const namespace = readStringProperty(configObject, "namespace");
508
+ return {
509
+ exportEntryKind: "credential-set",
510
+ kind: "credential-set",
511
+ name: displayName,
512
+ resolvedCredentialSetId: id ? namespace ? `${namespace}:${id}` : id : void 0,
513
+ sourceNode: callee
514
+ };
515
+ }
516
+ if (importedSymbol === "Sandbox") return {
517
+ exportEntryKind: "sandbox",
518
+ kind: "sandbox",
519
+ name: displayName,
520
+ sourceNode: callee
521
+ };
522
+ if (importedSymbol === "McpServer") return {
523
+ exportEntryKind: "mcp-server",
524
+ kind: "mcp-server",
525
+ name: displayName,
526
+ sourceNode: callee
527
+ };
528
+ if (importedSymbol === "CronTrigger" || importedSymbol === "WebhookTrigger" || importedSymbol === "PollingTrigger") return {
529
+ exportEntryKind: "trigger",
530
+ kind: "trigger",
531
+ name: displayName,
532
+ triggerType: importedSymbol === "CronTrigger" ? "cron" : importedSymbol === "WebhookTrigger" ? "webhook" : "polling",
533
+ sourceNode: callee
534
+ };
535
+ }
536
+ if (unwrappedExpression.type === "CallExpression") {
537
+ const callee = isNode(unwrappedExpression.callee) ? unwrappedExpression.callee : null;
538
+ const importedSymbol = callee ? importedCoreSymbols.get(identifierName(callee) ?? "") : void 0;
539
+ if (callee && (importedSymbol === "cronTrigger" || importedSymbol === "webhookTrigger" || importedSymbol === "pollingTrigger")) return {
540
+ exportEntryKind: "trigger",
541
+ kind: "trigger",
542
+ name: readStringProperty(getFirstObjectArgument(unwrappedExpression), "name"),
543
+ triggerType: importedSymbol === "cronTrigger" ? "cron" : importedSymbol === "webhookTrigger" ? "webhook" : "polling",
544
+ sourceNode: callee
545
+ };
546
+ if (callee) {
547
+ const importedTriggerCall = getImportedTriggerCall(callee, importedTriggerBindings);
548
+ if (importedTriggerCall) return {
549
+ exportEntryKind: "trigger",
550
+ kind: "trigger",
551
+ name: readStringProperty(getFirstObjectArgument(unwrappedExpression), "name"),
552
+ triggerType: inferImportedTriggerType(importedTriggerCall),
553
+ sourceNode: callee
554
+ };
555
+ }
556
+ }
557
+ return null;
558
+ }
559
+ function getDefaultExportDefinition(expression, importedCoreSymbols, importedTriggerBindings, localDefinitions) {
560
+ const localName = identifierName(expression);
561
+ if (localName) return localDefinitions.get(localName) ?? null;
562
+ return getRecognizedDefinition(expression, importedCoreSymbols, importedTriggerBindings);
563
+ }
564
+ function getImportedTriggerCall(expression, importedTriggerBindings) {
565
+ const directBindingName = identifierName(expression);
566
+ if (directBindingName) {
567
+ const importedBinding = importedTriggerBindings.get(directBindingName);
568
+ return importedBinding ? {
569
+ bindingName: directBindingName,
570
+ moduleSpecifier: importedBinding.moduleSpecifier
571
+ } : null;
572
+ }
573
+ if (expression.type === "MemberExpression" && expression.computed === false) {
574
+ const objectName = identifierName(expression.object);
575
+ const memberName = identifierName(expression.property);
576
+ const importedBinding = objectName ? importedTriggerBindings.get(objectName) : void 0;
577
+ if (objectName && importedBinding) return {
578
+ bindingName: objectName,
579
+ ...memberName ? { memberName } : {},
580
+ moduleSpecifier: importedBinding.moduleSpecifier
581
+ };
582
+ }
583
+ return null;
584
+ }
585
+ function inferImportedTriggerType(triggerCall) {
586
+ const bindingName = triggerCall.bindingName.toLowerCase();
587
+ const memberName = triggerCall.memberName?.toLowerCase();
588
+ if (bindingName === "polling" || memberName?.includes("poll")) return "polling";
589
+ return "webhook";
590
+ }
591
+ function addExportedDefinition(options) {
592
+ const source = createSourceLocation(options.sourceFile, options.record.sourceNode, options.relativeFilePath);
593
+ if (options.record.kind === "workflow") {
594
+ options.definitions.workflows.push({
595
+ name: options.record.name ?? options.exportName,
596
+ exportName: options.exportName,
597
+ source
598
+ });
599
+ return;
600
+ }
601
+ if (options.record.kind === "operation") {
602
+ options.definitions.operations.push({
603
+ name: options.record.name ?? options.exportName,
604
+ exportName: options.exportName,
605
+ source,
606
+ ...options.record.isBundle ? { isBundle: true } : {}
607
+ });
608
+ return;
609
+ }
610
+ if (options.record.kind === "agent") {
611
+ options.definitions.agents.push({
612
+ agentName: options.record.name ?? options.exportName,
613
+ exportName: options.exportName,
614
+ source
615
+ });
616
+ return;
617
+ }
618
+ if (options.record.kind === "credential-set") {
619
+ options.definitions.credentialSets.push({
620
+ credentialSetName: options.record.name ?? options.exportName,
621
+ ...options.record.resolvedCredentialSetId ? { resolvedCredentialSetId: options.record.resolvedCredentialSetId } : {},
622
+ exportName: options.exportName,
623
+ source
624
+ });
625
+ return;
626
+ }
627
+ if (options.record.kind === "sandbox") {
628
+ options.definitions.sandboxes.push({
629
+ sandboxName: options.record.name ?? options.exportName,
630
+ exportName: options.exportName,
631
+ source
632
+ });
633
+ return;
634
+ }
635
+ if (options.record.kind === "mcp-server") {
636
+ options.definitions.mcpServers.push({
637
+ mcpServerName: options.record.name ?? options.exportName,
638
+ exportName: options.exportName,
639
+ source
640
+ });
641
+ return;
642
+ }
643
+ if (options.record.kind === "trigger") options.definitions.triggers.defined.push({
644
+ name: options.record.name,
645
+ triggerType: options.record.triggerType ?? "webhook",
646
+ exportName: options.exportName,
647
+ source
648
+ });
649
+ }
650
+ function analyzeExports(sourceFile, localDefinitions) {
651
+ const exports = /* @__PURE__ */ new Map();
652
+ for (const statement of sourceFile.statements) {
653
+ if (isExportedVariableStatement(statement)) {
654
+ for (const declaration of getVariableDeclarators(statement)) {
655
+ const localName = identifierName(declaration.id);
656
+ if (!localName) continue;
657
+ const kind = localDefinitions.get(localName)?.exportEntryKind ?? "other";
658
+ exports.set(localName, kind);
659
+ }
660
+ continue;
661
+ }
662
+ if (statement.type === "ExportAllDeclaration") {
663
+ const namespaceExportName = identifierName(statement.exported);
664
+ if (namespaceExportName) {
665
+ exports.set(namespaceExportName, "other");
666
+ continue;
667
+ }
668
+ }
669
+ if (statement.type === "ExportNamedDeclaration" && Array.isArray(statement.specifiers)) {
670
+ for (const specifier of statement.specifiers) {
671
+ if (!isNode(specifier) || specifier.type !== "ExportSpecifier") continue;
672
+ const localName = identifierName(specifier.local);
673
+ const exportName = identifierName(specifier.exported);
674
+ if (!localName || !exportName) continue;
675
+ const kind = localDefinitions.get(localName)?.exportEntryKind ?? "other";
676
+ exports.set(exportName, kind);
677
+ }
678
+ continue;
679
+ }
680
+ if (statement.type === "ExportDefaultDeclaration") {
681
+ const localName = identifierName(statement.declaration);
682
+ const kind = localName ? localDefinitions.get(localName)?.exportEntryKind : void 0;
683
+ exports.set("default", kind ?? "other");
684
+ }
685
+ }
686
+ return Array.from(exports.entries()).map(([exportName, kind]) => ({
687
+ exportName,
688
+ kind
689
+ })).sort((left, right) => left.exportName.localeCompare(right.exportName));
690
+ }
691
+ async function analyzeImports(options) {
692
+ const imports = [];
693
+ for (const statement of options.sourceFile.statements) {
694
+ if (statement.type !== "ImportDeclaration") continue;
695
+ const moduleSpecifier = literalString(statement.source);
696
+ if (!moduleSpecifier) continue;
697
+ const entry = {
698
+ moduleSpecifier,
699
+ importNames: []
700
+ };
701
+ for (const specifier of Array.isArray(statement.specifiers) ? statement.specifiers : []) {
702
+ if (!isNode(specifier)) continue;
703
+ if (specifier.type === "ImportDefaultSpecifier") {
704
+ const localName = identifierName(specifier.local);
705
+ if (localName) entry.importNames.push(localName);
706
+ continue;
707
+ }
708
+ if (specifier.type === "ImportSpecifier") {
709
+ const importedName = identifierName(specifier.imported);
710
+ if (importedName) entry.importNames.push(importedName);
711
+ continue;
712
+ }
713
+ if (specifier.type === "ImportNamespaceSpecifier") {
714
+ const namespaceName = identifierName(specifier.local);
715
+ if (namespaceName) {
716
+ entry.isNamespaceImport = true;
717
+ entry.namespaceName = namespaceName;
718
+ }
719
+ }
720
+ }
721
+ const localModuleSpecifier = getLocalModuleSpecifier(moduleSpecifier);
722
+ if (localModuleSpecifier) {
723
+ const resolvedPath = await resolveLocalModulePath(options.filePath, localModuleSpecifier);
724
+ if (resolvedPath) entry.resolvedPath = toRelativeFilePath(options.projectRoot, resolvedPath);
725
+ }
726
+ entry.importNames.sort((left, right) => left.localeCompare(right));
727
+ imports.push(entry);
728
+ }
729
+ return imports.sort(compareImportEntries);
730
+ }
731
+ function compareImportEntries(left, right) {
732
+ return left.moduleSpecifier.localeCompare(right.moduleSpecifier);
733
+ }
734
+ function analyzeWorkflowDelegations(options) {
735
+ if (options.workflowDefinitions.length === 0) return [...options.workflowDefinitions];
736
+ const importedBindings = collectImportedBindings(options.sourceFile, options.imports);
737
+ const localDefinitionInfo = collectLocalDefinitionInfo(options.sourceFile, options.localDefinitions);
738
+ return options.workflowDefinitions.map((workflowDefinition) => {
739
+ const runNode = findWorkflowRunFunction(options.sourceFile, workflowDefinition.exportName);
740
+ if (!runNode) return workflowDefinition;
741
+ const delegatedAgents = /* @__PURE__ */ new Map();
742
+ const delegatedWorkflows = /* @__PURE__ */ new Map();
743
+ visitNode(runNode, (node) => {
744
+ if (node.type !== "CallExpression" || !isNode(node.callee)) return;
745
+ const callee = node.callee;
746
+ if (callee.type !== "MemberExpression" || callee.computed !== false) return;
747
+ if (identifierName(callee.property) !== "run") return;
748
+ const bindingName = identifierName(callee.object);
749
+ if (!bindingName) return;
750
+ const imported = importedBindings.get(bindingName);
751
+ const local = localDefinitionInfo.get(bindingName);
752
+ if (imported?.resolvedPath?.endsWith(".agent.ts")) delegatedAgents.set(bindingName, {
753
+ exportName: imported.importedName,
754
+ agentName: imported.importedName,
755
+ sourceFilePath: imported.resolvedPath
756
+ });
757
+ else if (imported?.resolvedPath?.endsWith(".workflow.ts")) delegatedWorkflows.set(bindingName, {
758
+ exportName: imported.importedName,
759
+ workflowName: imported.importedName,
760
+ sourceFilePath: imported.resolvedPath
761
+ });
762
+ else if (local?.agentId || (options.localDefinitions.get(bindingName)?.kind ?? "") === "agent") delegatedAgents.set(bindingName, {
763
+ exportName: bindingName,
764
+ authoredAgentId: local?.agentId,
765
+ agentName: local?.name ?? bindingName,
766
+ ...local?.sourceFilePath ? { sourceFilePath: local.sourceFilePath } : {}
767
+ });
768
+ else if (local?.workflowId || (options.localDefinitions.get(bindingName)?.kind ?? "") === "workflow") delegatedWorkflows.set(bindingName, {
769
+ exportName: bindingName,
770
+ authoredWorkflowId: local?.workflowId,
771
+ workflowName: local?.name ?? bindingName,
772
+ ...local?.sourceFilePath ? { sourceFilePath: local.sourceFilePath } : {}
773
+ });
774
+ });
775
+ return {
776
+ ...workflowDefinition,
777
+ ...delegatedAgents.size > 0 ? { delegatedAgents: [...delegatedAgents.values()].sort((a, b) => a.exportName.localeCompare(b.exportName)) } : {},
778
+ ...delegatedWorkflows.size > 0 ? { delegatedWorkflows: [...delegatedWorkflows.values()].sort((a, b) => a.exportName.localeCompare(b.exportName)) } : {}
779
+ };
780
+ });
781
+ }
782
+ function collectImportedBindings(sourceFile, imports) {
783
+ const importsByModule = new Map(imports.map((entry) => [entry.moduleSpecifier, entry]));
784
+ const bindings = /* @__PURE__ */ new Map();
785
+ for (const statement of sourceFile.statements) {
786
+ if (statement.type !== "ImportDeclaration") continue;
787
+ const moduleSpecifier = literalString(statement.source);
788
+ const entry = moduleSpecifier ? importsByModule.get(moduleSpecifier) : void 0;
789
+ if (!entry?.resolvedPath || !Array.isArray(statement.specifiers)) continue;
790
+ for (const specifier of statement.specifiers) {
791
+ if (!isNode(specifier)) continue;
792
+ if (specifier.type === "ImportDefaultSpecifier") {
793
+ const localName = identifierName(specifier.local);
794
+ if (localName) bindings.set(localName, {
795
+ localName,
796
+ importedName: "default",
797
+ resolvedPath: entry.resolvedPath
798
+ });
799
+ continue;
800
+ }
801
+ if (specifier.type === "ImportSpecifier") {
802
+ const localName = identifierName(specifier.local);
803
+ const importedName = identifierName(specifier.imported);
804
+ if (localName && importedName) bindings.set(localName, {
805
+ localName,
806
+ importedName,
807
+ resolvedPath: entry.resolvedPath
808
+ });
809
+ }
810
+ }
811
+ }
812
+ return bindings;
813
+ }
814
+ function collectLocalDefinitionInfo(sourceFile, localDefinitions) {
815
+ const info = /* @__PURE__ */ new Map();
816
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
817
+ const localName = identifierName(declaration.id);
818
+ if (!localName || !isNode(declaration.init)) continue;
819
+ const local = localDefinitions.get(localName);
820
+ if (!local) continue;
821
+ const objectLiteral = getConfigObject(declaration.init);
822
+ info.set(localName, {
823
+ name: readStringProperty(objectLiteral, "name") ?? local.name ?? localName,
824
+ workflowId: local.kind === "workflow" ? readStringProperty(objectLiteral, "id") : void 0,
825
+ agentId: local.kind === "agent" ? readStringProperty(objectLiteral, "id") : void 0
826
+ });
827
+ }
828
+ return info;
829
+ }
830
+ function findWorkflowRunFunction(sourceFile, exportName) {
831
+ for (const statement of sourceFile.statements) for (const declaration of getVariableDeclarators(statement)) {
832
+ if (identifierName(declaration.id) !== exportName || !isNode(declaration.init)) continue;
833
+ const objectLiteral = getConfigObject(declaration.init);
834
+ if (!objectLiteral) return null;
835
+ const runValue = objectLiteral.properties.filter((property) => isNode(property)).find((property) => property.type === "Property" && identifierName(property.key) === "run")?.value;
836
+ return isNode(runValue) && (runValue.type === "ArrowFunctionExpression" || runValue.type === "FunctionExpression") ? runValue : null;
837
+ }
838
+ return null;
839
+ }
840
+ function getConfigObject(expression) {
841
+ return getFirstObjectArgument(expression);
842
+ }
843
+ function computeSourceChecksum(source) {
844
+ return sha256String(source);
845
+ }
846
+ async function analyzeFile(options) {
847
+ const source = await readFile(options.filePath, "utf-8");
848
+ const relativeFilePath = options.relativeFilePath ?? toRelativeFilePath(options.projectRoot, options.filePath);
849
+ const sourceFile = parseSourceFile(options.filePath, source);
850
+ const definitionAnalysis = analyzeDefinitions(sourceFile, relativeFilePath);
851
+ const imports = await analyzeImports({
852
+ sourceFile,
853
+ filePath: options.filePath,
854
+ projectRoot: options.projectRoot
855
+ });
856
+ const exports = analyzeExports(sourceFile, definitionAnalysis.localDefinitions);
857
+ const credentials = analyzeCredentials(sourceFile);
858
+ const capturedVariablesByDefinition = analyzeCapturedVariables(sourceFile, definitionAnalysis, relativeFilePath, imports);
859
+ const definitions = {
860
+ ...definitionAnalysis.definitions,
861
+ workflows: analyzeWorkflowDelegations({
862
+ sourceFile,
863
+ imports,
864
+ workflowDefinitions: definitionAnalysis.definitions.workflows,
865
+ localDefinitions: definitionAnalysis.localDefinitions
866
+ })
867
+ };
868
+ return {
869
+ filePath: relativeFilePath,
870
+ sourceChecksum: computeSourceChecksum(source),
871
+ definitions,
872
+ imports,
873
+ exports,
874
+ credentials,
875
+ capturedVariablesByDefinition
876
+ };
877
+ }
878
+ async function restoreMetadataIndexFromCache(options) {
879
+ const restoredIndex = Object.create(null);
880
+ let cacheHits = 0;
881
+ let cacheMisses = 0;
882
+ let invalidated = false;
883
+ for (const filePath of Object.keys(options.index).sort((left, right) => left.localeCompare(right)).map(parseRelativeFilePath)) {
884
+ const metadata = options.index[filePath];
885
+ if (!metadata) continue;
886
+ const metadataHash = computeMetadataHash(metadata);
887
+ const cachedEntry = options.cacheEntries[filePath];
888
+ if (cachedEntry && cachedEntry.sourceChecksum === metadata.buildInfo.sourceChecksum && cachedEntry.metadataHash === metadataHash && cachedEntry.outputFile === createMetadataOutputFile(filePath)) {
889
+ const cachedMetadata = await loadCachedMetadataEntry(options.outputDir, cachedEntry);
890
+ if (cachedMetadata) {
891
+ restoredIndex[filePath] = cachedMetadata;
892
+ cacheHits += 1;
893
+ continue;
894
+ }
895
+ invalidated = true;
896
+ }
897
+ restoredIndex[filePath] = metadata;
898
+ cacheMisses += 1;
899
+ }
900
+ return {
901
+ index: restoredIndex,
902
+ cacheHits,
903
+ cacheMisses,
904
+ invalidated
905
+ };
906
+ }
907
+ async function loadPreservedMetadataIndex(options) {
908
+ const preservedIndex = Object.create(null);
909
+ for (const filePath of Object.keys(options.cacheEntries).sort((left, right) => left.localeCompare(right)).map(parseRelativeFilePath)) {
910
+ if (options.excludePaths.has(filePath)) continue;
911
+ const cachedEntry = options.cacheEntries[filePath];
912
+ if (!cachedEntry) continue;
913
+ const cachedMetadata = await loadCachedMetadataEntry(options.outputDir, cachedEntry);
914
+ if (cachedMetadata) preservedIndex[filePath] = cachedMetadata;
915
+ }
916
+ return preservedIndex;
917
+ }
918
+ function createMetadataCacheEntries(index) {
919
+ const entries = Object.keys(index).sort((left, right) => left.localeCompare(right));
920
+ return Object.fromEntries(entries.map((filePath) => {
921
+ const parsed = parseRelativeFilePath(filePath);
922
+ const metadata = index[parsed];
923
+ if (!metadata) throw new Error(`Expected metadata for ${filePath}`);
924
+ return [filePath, {
925
+ filePath,
926
+ sourceChecksum: metadata.buildInfo.sourceChecksum,
927
+ metadataHash: computeMetadataHash(metadata),
928
+ outputFile: createMetadataOutputFile(parsed),
929
+ updatedAt: metadata.buildInfo.generatedAt,
930
+ crossReferenceFingerprint: computeCrossReferenceFingerprint(metadata)
931
+ }];
932
+ }));
933
+ }
934
+ function computeMetadataHash(metadata) {
935
+ return sha256String(JSON.stringify(normalizeMetadataForHash(metadata)));
936
+ }
937
+ async function loadCachedMetadataEntry(outputDir, cacheEntry) {
938
+ try {
939
+ const parsed = JSON.parse(await readFile(getFileMetadataPath(outputDir, cacheEntry.filePath), "utf-8"));
940
+ const result = FileMetadataSchema.safeParse(parsed);
941
+ if (!result.success) return null;
942
+ if (computeMetadataHash(result.data) !== cacheEntry.metadataHash) return null;
943
+ return result.data;
944
+ } catch {
945
+ return null;
946
+ }
947
+ }
948
+ function computeCrossReferenceFingerprint(metadata) {
949
+ const payload = {
950
+ workflows: metadata.definitions.workflows.map((workflow) => ({
951
+ exportName: workflow.exportName,
952
+ workflowId: workflow.workflowId,
953
+ description: workflow.description
954
+ })),
955
+ stepsConsumedBy: metadata.consumption.stepsConsumedBy
956
+ };
957
+ return sha256String(JSON.stringify(payload));
958
+ }
959
+ function normalizeMetadataForHash(metadata) {
960
+ return {
961
+ ...metadata,
962
+ buildInfo: {
963
+ ...metadata.buildInfo,
964
+ generatedAt: ""
965
+ }
966
+ };
967
+ }
968
+ const METADATA_FILE_GLOBS = [
969
+ "**/*.ts",
970
+ "**/*.tsx",
971
+ "**/*.mts"
972
+ ];
973
+ const METADATA_FILE_GLOBS_WITHOUT_MTS = ["**/*.ts", "**/*.tsx"];
974
+ const METADATA_IGNORE = [
975
+ ...BASE_IGNORE_PATTERNS,
976
+ "**/tmp/**",
977
+ "**/temp/**"
978
+ ];
979
+ async function collectMetadataFiles(options) {
980
+ const resolvedProjectRoot = path.resolve(options.projectRoot);
981
+ const resolvedOutDir = options.outDir ? path.resolve(resolvedProjectRoot, options.outDir) : path.join(resolvedProjectRoot, BUILD_OUTPUT_DIR_NAME);
982
+ const relativeOutDir = path.relative(resolvedProjectRoot, resolvedOutDir).replaceAll("\\", "/");
983
+ const ignore = [...METADATA_IGNORE];
984
+ if (relativeOutDir.length > 0 && relativeOutDir !== "." && !relativeOutDir.startsWith("../") && !path.isAbsolute(relativeOutDir)) ignore.push(`${relativeOutDir}/**`);
985
+ const absolutePaths = await (0, import_out.default)(options.includeMts === false ? METADATA_FILE_GLOBS_WITHOUT_MTS : METADATA_FILE_GLOBS, {
986
+ cwd: resolvedProjectRoot,
987
+ absolute: true,
988
+ onlyFiles: true,
989
+ ignore
990
+ });
991
+ absolutePaths.sort((left, right) => left.localeCompare(right));
992
+ return absolutePaths.map((absolutePath) => ({
993
+ absolutePath,
994
+ relativePath: toRelativeFilePath(resolvedProjectRoot, absolutePath)
995
+ }));
996
+ }
997
+ function buildStepConsumptionIndex(options) {
998
+ const index = /* @__PURE__ */ new Map();
999
+ for (const artifact of options.artifacts) {
1000
+ const consumedInFile = toRelativeFilePath(options.projectRoot, artifact.workflow.resolvedFilePath);
1001
+ appendStepConsumptionEntries({
1002
+ index,
1003
+ flowGraph: artifact.flowGraph,
1004
+ workflowName: artifact.manifest.name,
1005
+ consumedInFile,
1006
+ stepNamesByFile: options.stepNamesByFile
1007
+ });
1008
+ }
1009
+ for (const entries of index.values()) entries.sort((left, right) => {
1010
+ if (left.consumedInFile !== right.consumedInFile) return left.consumedInFile.localeCompare(right.consumedInFile);
1011
+ return left.nodeId.localeCompare(right.nodeId);
1012
+ });
1013
+ return index;
1014
+ }
1015
+ function appendStepConsumptionEntries(options) {
1016
+ for (const node of options.flowGraph.nodes) {
1017
+ if (node.type !== "step" || node.data.kind !== "call") continue;
1018
+ const stepNodeData = node.data;
1019
+ const resolvedPath = stepNodeData.importSource?.resolvedPath;
1020
+ const stepName = stepNodeData.stepName;
1021
+ if (!resolvedPath || stepNodeData.callKind !== "workflow-step" || !stepName) continue;
1022
+ let normalizedResolvedPath;
1023
+ try {
1024
+ normalizedResolvedPath = normalizeRelativeFilePath(resolvedPath);
1025
+ } catch {
1026
+ continue;
1027
+ }
1028
+ const knownStepNames = options.stepNamesByFile?.get(normalizedResolvedPath);
1029
+ if (knownStepNames && !knownStepNames.has(stepName)) continue;
1030
+ const entries = options.index.get(normalizedResolvedPath) ?? [];
1031
+ entries.push({
1032
+ stepName,
1033
+ consumedInFile: options.consumedInFile,
1034
+ workflowName: options.workflowName,
1035
+ nodeId: node.id
1036
+ });
1037
+ options.index.set(normalizedResolvedPath, entries);
1038
+ }
1039
+ }
1040
+ /** Example filename suffix for each convention (used in validation messages). */
1041
+ const CONVENTION_TO_EXAMPLE_SUFFIX = {
1042
+ workflow: ".workflow.ts",
1043
+ operation: ".operation.ts",
1044
+ agent: ".agent.ts",
1045
+ trigger: ".trigger.ts",
1046
+ "credential-set": ".credential-set.ts",
1047
+ sandbox: ".sandbox.ts",
1048
+ "mcp-server": ".mcp-server.ts"
1049
+ };
1050
+ const SUFFIX_TO_CONVENTION = [
1051
+ [".workflow.ts", "workflow"],
1052
+ [".workflow.tsx", "workflow"],
1053
+ [".workflow.mts", "workflow"],
1054
+ [".operation.ts", "operation"],
1055
+ [".operation.tsx", "operation"],
1056
+ [".operation.mts", "operation"],
1057
+ [".step.ts", "operation"],
1058
+ [".step.tsx", "operation"],
1059
+ [".step.mts", "operation"],
1060
+ [".trigger.ts", "trigger"],
1061
+ [".trigger.tsx", "trigger"],
1062
+ [".trigger.mts", "trigger"],
1063
+ [".agent.ts", "agent"],
1064
+ [".agent.tsx", "agent"],
1065
+ [".agent.mts", "agent"],
1066
+ [".tool.ts", "operation"],
1067
+ [".tool.tsx", "operation"],
1068
+ [".tool.mts", "operation"],
1069
+ [".credential-set.ts", "credential-set"],
1070
+ [".credential-set.tsx", "credential-set"],
1071
+ [".credential-set.mts", "credential-set"],
1072
+ [".sandbox.ts", "sandbox"],
1073
+ [".sandbox.tsx", "sandbox"],
1074
+ [".sandbox.mts", "sandbox"],
1075
+ [".mcp-server.ts", "mcp-server"],
1076
+ [".mcp-server.tsx", "mcp-server"],
1077
+ [".mcp-server.mts", "mcp-server"]
1078
+ ];
1079
+ function detectFileConvention(filePath) {
1080
+ const lower = filePath.toLowerCase();
1081
+ for (const [suffix, convention] of SUFFIX_TO_CONVENTION) if (lower.endsWith(suffix)) return convention;
1082
+ return null;
1083
+ }
1084
+ /** Returns a representative typed suffix for docs and validation hints (e.g. `.credential-set.ts`). */
1085
+ function conventionToExampleSuffix(convention) {
1086
+ return CONVENTION_TO_EXAMPLE_SUFFIX[convention];
1087
+ }
1088
+ function synthesizeMetadataIndex(options) {
1089
+ const baseIndex = Object.create(null);
1090
+ const operationNamesByFile = /* @__PURE__ */ new Map();
1091
+ const sortedFiles = [...options.analyzedFiles].sort((left, right) => left.filePath.localeCompare(right.filePath));
1092
+ for (const analyzedFile of sortedFiles) {
1093
+ operationNamesByFile.set(analyzedFile.filePath, new Set(analyzedFile.definitions.operations.map((op) => op.name)));
1094
+ const capturedVars = analyzedFile.capturedVariablesByDefinition;
1095
+ const definitions = enrichDefinitionsWithCapturedVariables(analyzedFile.definitions, capturedVars);
1096
+ const metadata = FileMetadataSchema.parse({
1097
+ schemaVersion: FILE_METADATA_SCHEMA_VERSION,
1098
+ filePath: analyzedFile.filePath,
1099
+ fileConvention: detectFileConvention(analyzedFile.filePath),
1100
+ definitions,
1101
+ consumption: { stepsConsumedBy: [] },
1102
+ credentials: analyzedFile.credentials,
1103
+ imports: analyzedFile.imports,
1104
+ exports: analyzedFile.exports,
1105
+ buildInfo: {
1106
+ generatedAt: options.generatedAt,
1107
+ sourceChecksum: analyzedFile.sourceChecksum
1108
+ }
1109
+ });
1110
+ baseIndex[analyzedFile.filePath] = metadata;
1111
+ }
1112
+ enrichWorkflowDefinitions({
1113
+ index: baseIndex,
1114
+ artifacts: options.artifacts,
1115
+ projectRoot: options.projectRoot
1116
+ });
1117
+ const stepConsumptionIndex = buildStepConsumptionIndex({
1118
+ artifacts: options.artifacts,
1119
+ projectRoot: options.projectRoot,
1120
+ stepNamesByFile: operationNamesByFile
1121
+ });
1122
+ for (const [filePath, entries] of stepConsumptionIndex.entries()) {
1123
+ const existingMetadata = baseIndex[filePath];
1124
+ if (!existingMetadata) continue;
1125
+ baseIndex[filePath] = FileMetadataSchema.parse({
1126
+ ...existingMetadata,
1127
+ consumption: { stepsConsumedBy: entries }
1128
+ });
1129
+ }
1130
+ return {
1131
+ index: baseIndex,
1132
+ fileCount: sortedFiles.length
1133
+ };
1134
+ }
1135
+ function enrichDefinitionsWithCapturedVariables(definitions, capturedVars) {
1136
+ if (capturedVars.size === 0) return definitions;
1137
+ return {
1138
+ ...definitions,
1139
+ workflows: definitions.workflows.map((wf) => {
1140
+ const vars = capturedVars.get(wf.exportName);
1141
+ return vars?.length ? {
1142
+ ...wf,
1143
+ capturedVariables: vars
1144
+ } : wf;
1145
+ }),
1146
+ operations: definitions.operations.map((op) => {
1147
+ const vars = capturedVars.get(op.exportName);
1148
+ return vars?.length ? {
1149
+ ...op,
1150
+ capturedVariables: vars
1151
+ } : op;
1152
+ }),
1153
+ agents: definitions.agents.map((agent) => {
1154
+ const vars = capturedVars.get(agent.exportName);
1155
+ return vars?.length ? {
1156
+ ...agent,
1157
+ capturedVariables: vars
1158
+ } : agent;
1159
+ })
1160
+ };
1161
+ }
1162
+ function enrichWorkflowDefinitions(options) {
1163
+ for (const artifact of options.artifacts) {
1164
+ const relativeFilePath = toRelativeFilePath(options.projectRoot, artifact.workflow.resolvedFilePath);
1165
+ const fileMetadata = options.index[relativeFilePath];
1166
+ if (!fileMetadata) continue;
1167
+ const enrichedWorkflows = fileMetadata.definitions.workflows.map((definition) => {
1168
+ if (definition.exportName !== artifact.workflow.localExportName && definition.exportName !== artifact.workflow.exportName) return definition;
1169
+ return {
1170
+ ...definition,
1171
+ workflowId: artifact.manifest.id,
1172
+ description: artifact.manifest.description
1173
+ };
1174
+ });
1175
+ options.index[fileMetadata.filePath] = FileMetadataSchema.parse({
1176
+ ...fileMetadata,
1177
+ definitions: {
1178
+ ...fileMetadata.definitions,
1179
+ workflows: enrichedWorkflows
1180
+ }
1181
+ });
1182
+ }
1183
+ }
1184
+ const PRIMITIVE_COUNT_KEYS = [
1185
+ {
1186
+ convention: "workflow",
1187
+ countFn: (d) => d.workflows.length,
1188
+ label: "Workflow"
1189
+ },
1190
+ {
1191
+ convention: "operation",
1192
+ countFn: (d) => d.operations.length,
1193
+ label: "Operation"
1194
+ },
1195
+ {
1196
+ convention: "agent",
1197
+ countFn: (d) => d.agents.length,
1198
+ label: "Agent"
1199
+ },
1200
+ {
1201
+ convention: "trigger",
1202
+ countFn: (d) => d.triggers.defined.length,
1203
+ label: "Trigger"
1204
+ },
1205
+ {
1206
+ convention: "credential-set",
1207
+ countFn: (d) => d.credentialSets.length,
1208
+ label: "CredentialSet"
1209
+ },
1210
+ {
1211
+ convention: "sandbox",
1212
+ countFn: (d) => d.sandboxes.length,
1213
+ label: "Sandbox"
1214
+ },
1215
+ {
1216
+ convention: "mcp-server",
1217
+ countFn: (d) => d.mcpServers.length,
1218
+ label: "McpServer"
1219
+ }
1220
+ ];
1221
+ function totalPrimitiveCount(defs) {
1222
+ return PRIMITIVE_COUNT_KEYS.reduce((sum, entry) => sum + entry.countFn(defs), 0);
1223
+ }
1224
+ function countForConvention(convention, defs) {
1225
+ const entry = PRIMITIVE_COUNT_KEYS.find((e) => e.convention === convention);
1226
+ return entry ? entry.countFn(defs) : 0;
1227
+ }
1228
+ function otherPrimitiveCount(convention, defs) {
1229
+ return totalPrimitiveCount(defs) - countForConvention(convention, defs);
1230
+ }
1231
+ function validateFileConventions(analyzedFiles) {
1232
+ const violations = [];
1233
+ for (const file of analyzedFiles) {
1234
+ const convention = detectFileConvention(file.filePath);
1235
+ const defs = file.definitions;
1236
+ if (convention) {
1237
+ const ownCount = countForConvention(convention, defs);
1238
+ if (ownCount > 1) violations.push({
1239
+ filePath: file.filePath,
1240
+ level: "error",
1241
+ message: `File "${file.filePath}" uses .${convention}.ts suffix but exports ${ownCount} ${convention} primitives. Expected exactly 1.`
1242
+ });
1243
+ if (ownCount === 0) violations.push({
1244
+ filePath: file.filePath,
1245
+ level: "warning",
1246
+ message: `File "${file.filePath}" uses .${convention}.ts suffix but exports no ${convention} primitives.`
1247
+ });
1248
+ if (otherPrimitiveCount(convention, defs) > 0) violations.push({
1249
+ filePath: file.filePath,
1250
+ level: "error",
1251
+ message: `File "${file.filePath}" uses .${convention}.ts suffix but also exports primitives of other types. Each primitive type belongs in its own file.`
1252
+ });
1253
+ } else if (totalPrimitiveCount(defs) > 0) {
1254
+ const activeEntries = PRIMITIVE_COUNT_KEYS.filter((entry) => entry.countFn(defs) > 0);
1255
+ const typeLabels = activeEntries.map((entry) => entry.label);
1256
+ const firstConvention = activeEntries[0]?.convention;
1257
+ const exampleSuffix = firstConvention ? conventionToExampleSuffix(firstConvention) : ".workflow.ts";
1258
+ violations.push({
1259
+ filePath: file.filePath,
1260
+ level: "warning",
1261
+ message: `File "${file.filePath}" exports ${typeLabels.join(", ")} primitive(s) but does not use a typed suffix. Consider renaming to use the appropriate suffix (e.g. *${exampleSuffix}).`
1262
+ });
1263
+ }
1264
+ }
1265
+ return violations;
1266
+ }
1267
+ async function writeMetadataIndex(index, outputDir) {
1268
+ const outputRoot = getMetadataRoot(outputDir);
1269
+ const filePaths = Object.keys(index).sort((left, right) => left.localeCompare(right)).map(parseRelativeFilePath);
1270
+ const desiredOutputFiles = filePaths.map((filePath) => createMetadataOutputFile(filePath));
1271
+ let writtenCount = 0;
1272
+ if (filePaths.length > 0) await mkdir(outputRoot, { recursive: true });
1273
+ for (const filePath of filePaths) {
1274
+ const metadata = FileMetadataSchema.parse(index[filePath]);
1275
+ const outputPath = getFileMetadataPath(outputDir, filePath);
1276
+ const serializedMetadata = `${JSON.stringify(metadata, null, 2)}\n`;
1277
+ await mkdir(path.dirname(outputPath), { recursive: true });
1278
+ if (await readExistingMetadata(outputPath) === serializedMetadata) continue;
1279
+ await writeFile(outputPath, serializedMetadata, "utf-8");
1280
+ writtenCount += 1;
1281
+ }
1282
+ const cleanedCount = await cleanupStaleMetadataFiles(outputDir, desiredOutputFiles);
1283
+ return {
1284
+ fileCount: filePaths.length,
1285
+ writtenCount,
1286
+ cleanedCount,
1287
+ outputRoot,
1288
+ writtenPaths: desiredOutputFiles
1289
+ };
1290
+ }
1291
+ async function readExistingMetadata(filePath) {
1292
+ try {
1293
+ return await readFile(filePath, "utf-8");
1294
+ } catch {
1295
+ return null;
1296
+ }
1297
+ }
1298
+ async function cleanupStaleMetadataFiles(outputDir, desiredOutputFiles) {
1299
+ const metadataRoot = getMetadataRoot(outputDir);
1300
+ const existingMetadataFiles = await (0, import_out.default)("**/*.json", {
1301
+ cwd: metadataRoot,
1302
+ onlyFiles: true,
1303
+ absolute: false,
1304
+ suppressErrors: true
1305
+ }).catch(() => []);
1306
+ const desiredFiles = new Set(desiredOutputFiles.map((filePath) => filePath.replace(/^metadata\//u, "")));
1307
+ let cleanedCount = 0;
1308
+ for (const relativeFilePath of existingMetadataFiles) {
1309
+ if (desiredFiles.has(relativeFilePath.replaceAll("\\", "/"))) continue;
1310
+ await rm(path.join(metadataRoot, relativeFilePath), { force: true });
1311
+ cleanedCount += 1;
1312
+ }
1313
+ await removeEmptyMetadataDirectories(metadataRoot);
1314
+ return cleanedCount;
1315
+ }
1316
+ const METADATA_ANALYZE_CONCURRENCY = 8;
1317
+ async function buildMetadataArtifacts(options) {
1318
+ const rebuildDecision = options.rebuildDecision ?? {
1319
+ action: "full-recompute",
1320
+ reason: "default-build"
1321
+ };
1322
+ const metadataPhaseStartedAt = performance.now();
1323
+ options.emit({
1324
+ phase: "metadata-start",
1325
+ fileCount: 0
1326
+ });
1327
+ const metadataFiles = rebuildDecision.action === "targeted" ? await resolveTargetedMetadataFiles(options.projectRoot, rebuildDecision.affectedPaths) : await collectMetadataFiles({
1328
+ projectRoot: options.projectRoot,
1329
+ outDir: options.outputDir,
1330
+ includeMts: true
1331
+ });
1332
+ options.emit({
1333
+ phase: "metadata-scan",
1334
+ filesProcessed: 0,
1335
+ filesTotal: metadataFiles.length
1336
+ });
1337
+ const analyzedFacts = [];
1338
+ for (let batchStart = 0; batchStart < metadataFiles.length; batchStart += METADATA_ANALYZE_CONCURRENCY) {
1339
+ const batch = metadataFiles.slice(batchStart, batchStart + METADATA_ANALYZE_CONCURRENCY);
1340
+ const batchResults = await Promise.all(batch.map((metadataFile) => analyzeFile({
1341
+ filePath: metadataFile.absolutePath,
1342
+ projectRoot: options.projectRoot,
1343
+ relativeFilePath: metadataFile.relativePath
1344
+ })));
1345
+ analyzedFacts.push(...batchResults);
1346
+ options.emit({
1347
+ phase: "metadata-scan",
1348
+ filesProcessed: Math.min(batchStart + batch.length, metadataFiles.length),
1349
+ filesTotal: metadataFiles.length
1350
+ });
1351
+ }
1352
+ analyzedFacts.sort((left, right) => left.filePath.localeCompare(right.filePath));
1353
+ const conventionViolations = validateFileConventions(analyzedFacts);
1354
+ await new Promise((resolve) => {
1355
+ setImmediate(resolve);
1356
+ });
1357
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
1358
+ const synthesisResult = synthesizeMetadataIndex({
1359
+ analyzedFiles: analyzedFacts,
1360
+ artifacts: options.artifacts,
1361
+ generatedAt,
1362
+ projectRoot: options.projectRoot
1363
+ });
1364
+ const restoredFromCache = await restoreMetadataIndexFromCache({
1365
+ index: rebuildDecision.action === "targeted" ? {
1366
+ ...await loadPreservedMetadataIndex({
1367
+ outputDir: options.outputDir,
1368
+ cacheEntries: options.cacheEntries,
1369
+ excludePaths: new Set(rebuildDecision.affectedPaths)
1370
+ }),
1371
+ ...synthesisResult.index
1372
+ } : synthesisResult.index,
1373
+ outputDir: options.outputDir,
1374
+ cacheEntries: options.cacheEntries
1375
+ });
1376
+ options.emit({
1377
+ phase: "metadata-cache-stats",
1378
+ cacheHits: restoredFromCache.cacheHits,
1379
+ cacheMisses: restoredFromCache.cacheMisses,
1380
+ invalidated: options.cacheInvalidated || restoredFromCache.invalidated
1381
+ });
1382
+ const metadataWriteStartedAt = performance.now();
1383
+ const writeResult = await writeMetadataIndex(restoredFromCache.index, options.outputDir);
1384
+ options.emit({
1385
+ phase: "metadata-complete",
1386
+ fileCount: Object.keys(restoredFromCache.index).length,
1387
+ elapsedMs: performance.now() - metadataPhaseStartedAt
1388
+ });
1389
+ options.emit({
1390
+ phase: "metadata-output-complete",
1391
+ writtenCount: writeResult.writtenCount,
1392
+ cleanedCount: writeResult.cleanedCount,
1393
+ elapsedMs: performance.now() - metadataWriteStartedAt
1394
+ });
1395
+ return {
1396
+ result: {
1397
+ fileCount: writeResult.fileCount,
1398
+ writtenCount: writeResult.writtenCount,
1399
+ cleanedCount: writeResult.cleanedCount,
1400
+ outputRoot: writeResult.outputRoot
1401
+ },
1402
+ cacheEntries: createMetadataCacheEntries(restoredFromCache.index),
1403
+ conventionViolations,
1404
+ analyzedFacts
1405
+ };
1406
+ }
1407
+ async function resolveTargetedMetadataFiles(projectRoot, affectedPaths) {
1408
+ const files = [];
1409
+ for (const relativePath of affectedPaths) {
1410
+ const absolutePath = path.resolve(projectRoot, relativePath);
1411
+ try {
1412
+ if (!(await stat(absolutePath)).isFile()) continue;
1413
+ files.push({
1414
+ absolutePath,
1415
+ relativePath: parseRelativeFilePath(relativePath)
1416
+ });
1417
+ } catch {}
1418
+ }
1419
+ return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
1420
+ }
1421
+ //#endregion
1422
+ export { buildMetadataArtifacts };