@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,1747 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { n as findProjectRoot } from "./project-config-D1qsQlO7.mjs";
4
+ import { o as DeclaredCredentialRequirementSchema } from "./schedule-BXx3uXwr.mjs";
5
+ import { a as WORKFLOW_MANIFEST_SCHEMA_VERSION, c as FlowGraphSchema, o as WorkflowCoreManifestSchema } from "./_manifest-JSRE3H8k.mjs";
6
+ import { t as runWithConcurrency } from "./concurrency-gXn9Rw8x-DNl2YtrS.mjs";
7
+ import { m as AgentManifestSchema, n as pollingTriggerManifestSchema, r as cronTriggerManifestSchema, t as webhookTriggerManifestSchema } from "./src-C0X6u_Mw.mjs";
8
+ import { createRequire } from "node:module";
9
+ import * as os from "node:os";
10
+ import * as path$1 from "node:path";
11
+ import { z } from "zod";
12
+ import { pathToFileURL } from "node:url";
13
+ import { execFile } from "node:child_process";
14
+ import { randomUUID } from "node:crypto";
15
+ import { promisify } from "node:util";
16
+ //#region ../../packages/workflow-builder/dist/import-module-CV84H5fZ.mjs
17
+ var DiscoveryError = class extends Error {
18
+ code;
19
+ filePath;
20
+ exportNames;
21
+ constructor(options) {
22
+ super(options.message);
23
+ this.name = "DiscoveryError";
24
+ this.code = options.code;
25
+ this.filePath = options.filePath;
26
+ this.exportNames = [...options.exportNames ?? []];
27
+ }
28
+ };
29
+ function createNamespaceStarExportDiscoveryError(filePath) {
30
+ return new DiscoveryError({
31
+ code: "namespace-star-export",
32
+ filePath,
33
+ message: `Unsupported namespace workflow re-export in ${filePath}. Use named re-exports instead.`
34
+ });
35
+ }
36
+ function createAmbiguousExportDiscoveryError(filePath, exportNames) {
37
+ return new DiscoveryError({
38
+ code: "ambiguous-export",
39
+ filePath,
40
+ exportNames: [...exportNames],
41
+ message: `Ambiguous workflow export(s) ${exportNames.map((exportName) => `"${exportName}"`).join(", ")} in ${filePath}`
42
+ });
43
+ }
44
+ function isDiscoveryError(error) {
45
+ return error instanceof DiscoveryError;
46
+ }
47
+ var DuplicateWorkflowIdError = class extends Error {
48
+ workflowId;
49
+ files;
50
+ bindingKeys;
51
+ constructor(options) {
52
+ super(`Duplicate workflow id "${options.workflowId}" found in ${options.files.join(" and ")}`);
53
+ this.name = "DuplicateWorkflowIdError";
54
+ this.workflowId = options.workflowId;
55
+ this.files = [...options.files];
56
+ this.bindingKeys = [...options.bindingKeys];
57
+ }
58
+ };
59
+ function isDuplicateWorkflowIdError(error) {
60
+ return error instanceof DuplicateWorkflowIdError;
61
+ }
62
+ function createDuplicateWorkflowIdFailure(artifacts, error) {
63
+ if (isDuplicateWorkflowIdError(error)) return {
64
+ kind: "duplicate-workflow-id",
65
+ workflowId: error.workflowId,
66
+ files: [...error.files],
67
+ bindingKeys: [...error.bindingKeys],
68
+ phase: "output",
69
+ error: error.message
70
+ };
71
+ const message = error instanceof Error ? error.message : String(error);
72
+ const duplicateId = message.match(/"([^"]+)"/)?.[1];
73
+ if (!duplicateId) throw new Error(`Unable to determine duplicate workflow id from error: ${message}`);
74
+ const duplicateArtifacts = artifacts.filter((artifact) => artifact.workflow.workflowId === duplicateId);
75
+ return {
76
+ kind: "duplicate-workflow-id",
77
+ workflowId: duplicateId,
78
+ files: duplicateArtifacts.map((artifact) => artifact.workflow.resolvedFilePath),
79
+ bindingKeys: duplicateArtifacts.map((artifact) => artifact.workflow.bindingKey),
80
+ phase: "output",
81
+ error: message
82
+ };
83
+ }
84
+ function createDiscoveryFailure(error) {
85
+ if (isDiscoveryError(error)) return {
86
+ kind: "discovery",
87
+ code: error.code,
88
+ filePath: error.filePath,
89
+ exportNames: [...error.exportNames],
90
+ error: error.message
91
+ };
92
+ return {
93
+ kind: "discovery",
94
+ code: "unknown-discovery-error",
95
+ filePath: "",
96
+ exportNames: [],
97
+ error: error instanceof Error ? error.message : String(error)
98
+ };
99
+ }
100
+ function createLoaderProtocolFailure(workflow, source, error, rawOutput) {
101
+ return {
102
+ kind: "loader-protocol",
103
+ workflow,
104
+ source,
105
+ error,
106
+ ...rawOutput ? { rawOutput } : {}
107
+ };
108
+ }
109
+ const LOADER_SENTINEL = "__KEYSTROKE_WORKFLOW_LOADER__::";
110
+ const OXC_PARSER_IMPORT_URL = pathToFileURL(createRequire(import.meta.url).resolve("oxc-parser")).href;
111
+ function createWorkflowLoaderScript() {
112
+ return `
113
+ import { Buffer } from 'node:buffer';
114
+ import { readdirSync } from 'node:fs';
115
+ import { readFile } from 'node:fs/promises';
116
+ import path, { dirname, join } from 'node:path';
117
+ import { performance } from 'node:perf_hooks';
118
+ import { pathToFileURL } from 'node:url';
119
+ import {
120
+ Workflow,
121
+ Agent,
122
+ Operation,
123
+ Step,
124
+ Tool,
125
+ Task,
126
+ CredentialSet,
127
+ Sandbox,
128
+ McpServer,
129
+ CronTrigger,
130
+ WebhookTrigger,
131
+ PollingTrigger,
132
+ cronTrigger,
133
+ webhookTrigger,
134
+ pollingTrigger,
135
+ } from '@keystroke/workflow-core';
136
+ import { getOfficialIntegrationMetadata } from '@keystroke/workflow-core/_runtime';
137
+ import { analyzeSuspensions, analyzeWorkflowCredentials, isTabularSchema } from '@keystroke/workflow-core/flow-graph-toolkit';
138
+ import { parseSync } from ${JSON.stringify(OXC_PARSER_IMPORT_URL)};
139
+
140
+ Object.assign(globalThis, {
141
+ Workflow,
142
+ Agent,
143
+ Operation,
144
+ Step,
145
+ Tool,
146
+ Task,
147
+ CredentialSet,
148
+ Sandbox,
149
+ McpServer,
150
+ CronTrigger,
151
+ WebhookTrigger,
152
+ PollingTrigger,
153
+ cronTrigger,
154
+ webhookTrigger,
155
+ pollingTrigger,
156
+ });
157
+
158
+ const encodedInput = process.env.KEYSTROKE_WORKFLOW_LOADER_INPUT;
159
+ const staticFlowModuleCache = new Map();
160
+ const staticFlowResolutionCache = new Map();
161
+
162
+ function serializeError(error) {
163
+ if (error instanceof Error && Array.isArray(error.issues)) {
164
+ const messages = error.issues.map(issue => issue.message).filter(Boolean);
165
+ if (messages.length > 0) {
166
+ return messages.join('; ');
167
+ }
168
+ }
169
+ return error instanceof Error ? error.message : String(error);
170
+ }
171
+
172
+ function writeJson(value) {
173
+ return new Promise((resolve, reject) => {
174
+ process.stdout.write('${LOADER_SENTINEL}' + JSON.stringify(value) + '\\n', (error) => {
175
+ if (error) {
176
+ reject(error);
177
+ return;
178
+ }
179
+
180
+ resolve();
181
+ });
182
+ });
183
+ }
184
+
185
+ async function emitError(bindingKey, phase, error) {
186
+ await writeJson({
187
+ bindingKey,
188
+ status: 'error',
189
+ phase,
190
+ error: serializeError(error),
191
+ });
192
+ }
193
+
194
+ function getCredentialRequirements(value) {
195
+ if (!value || typeof value.getCredentialRequirements !== 'function') {
196
+ return [];
197
+ }
198
+
199
+ const entries = value.getCredentialRequirements();
200
+ return Array.isArray(entries) ? entries : [];
201
+ }
202
+
203
+ function getAgentToolId(tool) {
204
+ if (tool instanceof Workflow) return tool.id;
205
+ const integrationMetadata = getOfficialIntegrationMetadata(tool);
206
+ return integrationMetadata
207
+ ? integrationMetadata.integrationId + '_' + tool.id
208
+ : tool.id;
209
+ }
210
+
211
+ /**
212
+ * Computes enrichment data for each agent tool (workflow + operation) by reaching
213
+ * into the live primitive instances. Workflow-core only emits primitive-level facts
214
+ * in toManifest(); cross-cutting enrichment (suspensions, credential conditionality,
215
+ * dispatch placement, tabular detection) is computed here.
216
+ *
217
+ * Returns a map keyed by the agent-tool-id (same id Agent.toManifest emits).
218
+ */
219
+ async function computeAgentToolEnrichments(agent) {
220
+ const tools = Array.isArray(agent.tools) ? agent.tools : [];
221
+ const enrichments = {};
222
+ if (tools.length === 0) return enrichments;
223
+
224
+ for (const tool of tools) {
225
+ const toolId = getAgentToolId(tool);
226
+
227
+ if (tool instanceof Workflow) {
228
+ const suspensions = await analyzeSuspensions(tool);
229
+ const credentials = await analyzeWorkflowCredentials(tool);
230
+ const requiredCredentialIds = new Set(
231
+ credentials.required.map((credentialSet) => credentialSet.resolvedId)
232
+ );
233
+ const enrichment = {
234
+ invocation: suspensions.hasSuspensions ? 'yield' : 'sync',
235
+ suspensions: suspensions.reasons,
236
+ outputSchemaIsTabular: isTabularSchema(tool.output),
237
+ };
238
+ if (credentials.all.length > 0) {
239
+ enrichment.credentialSets = credentials.all.map((credentialSet) => ({
240
+ id: credentialSet.resolvedId,
241
+ required: requiredCredentialIds.has(credentialSet.resolvedId),
242
+ }));
243
+ }
244
+ enrichments[toolId] = enrichment;
245
+ continue;
246
+ }
247
+
248
+ if (tool instanceof Operation) {
249
+ const integrationMetadata = getOfficialIntegrationMetadata(tool);
250
+ const enrichment = {
251
+ dispatch: integrationMetadata?.hosted ? 'host' : 'sandbox',
252
+ outputSchemaIsTabular: isTabularSchema(tool.output),
253
+ };
254
+ enrichments[toolId] = enrichment;
255
+ continue;
256
+ }
257
+ }
258
+
259
+ return enrichments;
260
+ }
261
+
262
+ async function resolveLocalModulePath(fromFilePath, specifier) {
263
+ const basePath = path.resolve(path.dirname(fromFilePath), specifier);
264
+ const candidates = [
265
+ basePath,
266
+ basePath + '.ts',
267
+ basePath + '.tsx',
268
+ basePath + '.mts',
269
+ path.join(basePath, 'index.ts'),
270
+ path.join(basePath, 'index.tsx'),
271
+ path.join(basePath, 'index.mts'),
272
+ ];
273
+
274
+ for (const candidate of candidates) {
275
+ try {
276
+ await readFile(candidate, 'utf-8');
277
+ return candidate;
278
+ } catch {
279
+ // Try the next resolution candidate.
280
+ }
281
+ }
282
+
283
+ return null;
284
+ }
285
+
286
+ function toRelativeFilePath(projectRoot, filePath) {
287
+ return path.relative(path.resolve(projectRoot), path.resolve(filePath)).replaceAll('\\\\', '/');
288
+ }
289
+
290
+ async function buildLoaderCredentialAttribution(options) {
291
+ const flowGraph = await enrichFlowGraphStepBindings(options);
292
+ const attribution = buildCredentialsByCallsiteFingerprint({
293
+ flowGraph,
294
+ credentialsByBindingName: options.credentialsByBindingName,
295
+ });
296
+
297
+ return {
298
+ flowGraph,
299
+ credentialsByCallsiteFingerprint: attribution.credentialsByCallsiteFingerprint,
300
+ credentialAttributionWarnings: attribution.warnings,
301
+ };
302
+ }
303
+
304
+ async function enrichFlowGraphStepBindings(options) {
305
+ const bindingResolutions = await collectStepBindingResolutions(options);
306
+ if (bindingResolutions.size === 0) {
307
+ return options.flowGraph;
308
+ }
309
+
310
+ return {
311
+ ...options.flowGraph,
312
+ nodes: options.flowGraph.nodes.map((node) => {
313
+ if (
314
+ node.type !== 'step' ||
315
+ node.data.kind !== 'call' ||
316
+ node.data.callKind !== 'workflow-step'
317
+ ) {
318
+ return node;
319
+ }
320
+
321
+ const bindingName = node.data.exportName ?? node.data.stepName;
322
+ const resolution = bindingResolutions.get(bindingName);
323
+ if (!resolution) {
324
+ return node;
325
+ }
326
+
327
+ return {
328
+ ...node,
329
+ data: {
330
+ ...node.data,
331
+ callKind: resolution.callKind ?? node.data.callKind,
332
+ stepName: resolution.displayName ?? node.data.stepName,
333
+ exportName: resolution.exportName,
334
+ ...(resolution.description ? { description: resolution.description } : {}),
335
+ ...(resolution.inputSchema ? { inputSchema: resolution.inputSchema } : {}),
336
+ ...(resolution.outputSchema ? { outputSchema: resolution.outputSchema } : {}),
337
+ ...(resolution.credentialSets ? { credentialSets: resolution.credentialSets } : {}),
338
+ ...(resolution.requiredOAuthScopes
339
+ ? { requiredOAuthScopes: resolution.requiredOAuthScopes }
340
+ : {}),
341
+ ...(resolution.importSource ? { importSource: resolution.importSource } : {}),
342
+ },
343
+ };
344
+ }),
345
+ };
346
+ }
347
+
348
+ async function createStaticFlowResolver(options) {
349
+ const bindingResolutions = await collectStepBindingResolutions(options);
350
+ return (candidate) => bindingResolutions.get(candidate.calleeName);
351
+ }
352
+
353
+ function buildCredentialsByCallsiteFingerprint(params) {
354
+ const credentialsByCallsiteFingerprint = {};
355
+ const matchedBindingNames = new Set();
356
+
357
+ for (const node of params.flowGraph.nodes) {
358
+ if (node.type !== 'step' || node.data.kind !== 'call' || !node.data.callsiteFingerprint) {
359
+ continue;
360
+ }
361
+
362
+ const bindingNames = [
363
+ ...new Set(
364
+ [node.data.exportName, node.data.stepName].filter(
365
+ (bindingName) => typeof bindingName === 'string'
366
+ )
367
+ ),
368
+ ];
369
+ const entries = deduplicateCredentialRequirementEntries(
370
+ bindingNames.flatMap((bindingName) => {
371
+ const boundEntries = params.credentialsByBindingName[bindingName];
372
+ if (boundEntries?.length) {
373
+ matchedBindingNames.add(bindingName);
374
+ }
375
+ return boundEntries ?? [];
376
+ })
377
+ );
378
+
379
+ if (entries.length > 0) {
380
+ credentialsByCallsiteFingerprint[node.data.callsiteFingerprint] = entries;
381
+ }
382
+ }
383
+
384
+ const warnings = Object.entries(params.credentialsByBindingName)
385
+ .filter(([bindingName]) => !matchedBindingNames.has(bindingName))
386
+ .map(([bindingName, entries]) => ({
387
+ category: 'unmatched-callsite',
388
+ bindingName,
389
+ credentialSetIds: [...new Set(entries.map((entry) => entry.resolvedCredentialSetId))].sort(),
390
+ }));
391
+
392
+ return { credentialsByCallsiteFingerprint, warnings };
393
+ }
394
+
395
+ function deduplicateCredentialRequirementEntries(entries) {
396
+ const deduped = new Map();
397
+
398
+ for (const entry of entries) {
399
+ const sortedKeys = [...(entry.credentialKeys ?? [])].sort();
400
+ const sortedStoredKeys = [...(entry.storedCredentialKeys ?? [])].sort();
401
+ const sortedOptionalAuthKeys = [...(entry.optionalCredentialKeys ?? [])].sort();
402
+ const sortedOptionalStoredKeys = [...(entry.optionalStoredCredentialKeys ?? [])].sort();
403
+ const credentialRefKey = entry.credentialRef
404
+ ? entry.credentialRef.type === 'id'
405
+ ? 'id:' + entry.credentialRef.id
406
+ : 'name:' + entry.credentialRef.name
407
+ : '';
408
+ const key = [
409
+ entry.credentialSetId,
410
+ entry.scope ?? '',
411
+ entry.alias ?? '',
412
+ credentialRefKey,
413
+ entry.schemaFingerprint ?? '',
414
+ sortedKeys.join(','),
415
+ sortedOptionalAuthKeys.join(','),
416
+ sortedStoredKeys.join(','),
417
+ sortedOptionalStoredKeys.join(','),
418
+ typeof entry.resolveCacheMs === 'number' ? String(entry.resolveCacheMs) : '',
419
+ entry.onCredentialRevoked ?? '',
420
+ ].join('|');
421
+ if (!deduped.has(key)) {
422
+ deduped.set(key, {
423
+ ...entry,
424
+ credentialKeys: sortedKeys,
425
+ ...(sortedOptionalAuthKeys.length > 0
426
+ ? { optionalCredentialKeys: sortedOptionalAuthKeys }
427
+ : {}),
428
+ ...(sortedStoredKeys.length > 0 ? { storedCredentialKeys: sortedStoredKeys } : {}),
429
+ ...(sortedOptionalStoredKeys.length > 0
430
+ ? { optionalStoredCredentialKeys: sortedOptionalStoredKeys }
431
+ : {}),
432
+ });
433
+ }
434
+ }
435
+
436
+ return [...deduped.values()];
437
+ }
438
+
439
+ async function collectStepBindingResolutions(options) {
440
+ const sourceText = await readFile(options.filePath, 'utf-8');
441
+ const sourceFile = parseSourceFile(options.filePath, sourceText);
442
+ const runCalleeNames = collectRunCalleeNames(sourceFile.program);
443
+ const exportNamesByLocalName = new Map();
444
+ const importResolutionsByLocalName = new Map();
445
+
446
+ for (const statement of sourceFile.statements) {
447
+ if (statement.type === 'ImportDeclaration') {
448
+ const moduleSpecifier = literalString(statement.source);
449
+ if (!moduleSpecifier) {
450
+ continue;
451
+ }
452
+ const importResolutions = await createImportResolutions({
453
+ filePath: options.filePath,
454
+ projectRoot: options.projectRoot,
455
+ moduleSpecifier,
456
+ statement,
457
+ runCalleeNames,
458
+ });
459
+
460
+ for (const [localName, resolution] of importResolutions.entries()) {
461
+ importResolutionsByLocalName.set(localName, resolution);
462
+ }
463
+ continue;
464
+ }
465
+
466
+ if (isExportedDeclaration(statement)) {
467
+ for (const localName of getDeclaredBindingNames(statement)) {
468
+ addExportName(exportNamesByLocalName, localName, localName);
469
+ }
470
+ continue;
471
+ }
472
+
473
+ if (statement.type === 'ExportDefaultDeclaration') {
474
+ const localName = identifierName(statement.declaration);
475
+ if (localName) {
476
+ addExportName(exportNamesByLocalName, localName, 'default');
477
+ }
478
+ continue;
479
+ }
480
+
481
+ if (
482
+ statement.type === 'ExportNamedDeclaration' &&
483
+ !statement.source &&
484
+ Array.isArray(statement.specifiers)
485
+ ) {
486
+ for (const element of statement.specifiers) {
487
+ if (!isNode(element) || element.type !== 'ExportSpecifier') {
488
+ continue;
489
+ }
490
+ const localName = identifierName(element.local);
491
+ const exportName = identifierName(element.exported);
492
+ if (localName && exportName) {
493
+ addExportName(exportNamesByLocalName, localName, exportName);
494
+ }
495
+ }
496
+ }
497
+ }
498
+
499
+ const resolutions = new Map();
500
+ for (const [localName, resolution] of importResolutionsByLocalName.entries()) {
501
+ resolutions.set(localName, resolution);
502
+ }
503
+
504
+ for (const [localName, exportNames] of exportNamesByLocalName.entries()) {
505
+ if (exportNames.size !== 1) {
506
+ continue;
507
+ }
508
+
509
+ const exportName = [...exportNames][0];
510
+ if (!exportName) {
511
+ continue;
512
+ }
513
+
514
+ const existing = resolutions.get(localName);
515
+ if (existing?.importSource) {
516
+ resolutions.set(localName, { ...existing, exportName });
517
+ continue;
518
+ }
519
+
520
+ resolutions.set(localName, { exportName });
521
+ }
522
+
523
+ return resolutions;
524
+ }
525
+
526
+ async function createImportResolutions(options) {
527
+ const resolutions = new Map();
528
+ const resolvedPath = options.moduleSpecifier.startsWith('.')
529
+ ? await resolveLocalModulePath(options.filePath, options.moduleSpecifier)
530
+ : null;
531
+ const importSourceBase = {
532
+ kind: options.moduleSpecifier.startsWith('.') ? 'local' : 'module-import',
533
+ moduleSpecifier: options.moduleSpecifier,
534
+ ...(resolvedPath
535
+ ? { resolvedPath: toRelativeFilePath(options.projectRoot, resolvedPath) }
536
+ : {}),
537
+ };
538
+
539
+ for (const specifier of Array.isArray(options.statement.specifiers)
540
+ ? options.statement.specifiers
541
+ : []) {
542
+ if (!isNode(specifier)) {
543
+ continue;
544
+ }
545
+
546
+ if (specifier.type === 'ImportDefaultSpecifier') {
547
+ const localName = identifierName(specifier.local);
548
+ if (!localName) {
549
+ continue;
550
+ }
551
+ if (!options.runCalleeNames.has(localName)) {
552
+ continue;
553
+ }
554
+ resolutions.set(localName, await enrichImportResolution({
555
+ exportName: 'default',
556
+ importSource: {
557
+ ...importSourceBase,
558
+ importName: 'default',
559
+ localName,
560
+ },
561
+ resolvedPath,
562
+ }));
563
+ continue;
564
+ }
565
+
566
+ if (specifier.type === 'ImportSpecifier') {
567
+ const localName = identifierName(specifier.local);
568
+ const importName = identifierName(specifier.imported);
569
+ if (!localName || !importName) {
570
+ continue;
571
+ }
572
+ if (!options.runCalleeNames.has(localName)) {
573
+ continue;
574
+ }
575
+ resolutions.set(localName, await enrichImportResolution({
576
+ exportName: importName,
577
+ importSource: {
578
+ ...importSourceBase,
579
+ importName,
580
+ localName,
581
+ },
582
+ resolvedPath,
583
+ }));
584
+ continue;
585
+ }
586
+
587
+ if (specifier.type === 'ImportNamespaceSpecifier') {
588
+ const localName = identifierName(specifier.local);
589
+ if (!localName) {
590
+ continue;
591
+ }
592
+ if (!options.runCalleeNames.has(localName)) {
593
+ continue;
594
+ }
595
+ resolutions.set(localName, await enrichImportResolution({
596
+ exportName: localName,
597
+ importSource: {
598
+ ...importSourceBase,
599
+ kind: 'namespace-import',
600
+ localName,
601
+ },
602
+ resolvedPath,
603
+ }));
604
+ }
605
+ }
606
+
607
+ return resolutions;
608
+ }
609
+
610
+ async function enrichImportResolution(resolution) {
611
+ const importSource = resolution.importSource;
612
+ const fallback = {
613
+ ...resolution,
614
+ callKind: inferCallKindFromPath(resolution.resolvedPath),
615
+ displayName: resolution.exportName,
616
+ };
617
+ if (!importSource?.importName || !resolution.resolvedPath) {
618
+ return fallback;
619
+ }
620
+
621
+ try {
622
+ const moduleUrl = pathToFileURL(resolution.resolvedPath).href;
623
+ const cacheKey = moduleUrl + '#' + importSource.importName;
624
+ if (staticFlowResolutionCache.has(cacheKey)) {
625
+ return staticFlowResolutionCache.get(cacheKey) ?? fallback;
626
+ }
627
+
628
+ const mod = await importCachedModule(moduleUrl);
629
+ const instance = mod[importSource.importName];
630
+ if (!instance || typeof instance.toManifest !== 'function') {
631
+ if (isLocalTypedPrimitiveImport(resolution)) {
632
+ throw new Error(
633
+ 'Could not resolve typed primitive export "' + importSource.importName + '" from ' + resolution.resolvedPath
634
+ );
635
+ }
636
+ staticFlowResolutionCache.set(cacheKey, null);
637
+ return fallback;
638
+ }
639
+
640
+ const manifest = compactManifestForFlowGraph(instance.toManifest());
641
+ const credentialRequirements =
642
+ typeof instance.getCredentialRequirements === 'function' ? instance.getCredentialRequirements() : [];
643
+ const inputSchema = getInputSchema(manifest);
644
+ const outputSchema = getOutputSchema(manifest);
645
+ const nestedRunSource =
646
+ typeof instance.getRunFunctionSource === 'function' ? instance.getRunFunctionSource() : undefined;
647
+
648
+ const enriched = {
649
+ ...resolution,
650
+ callKind: inferCallKindFromPath(resolution.resolvedPath, manifest),
651
+ displayName: manifest.name ?? instance.name ?? resolution.exportName,
652
+ description: manifest.description ?? instance.description,
653
+ manifest,
654
+ ...(inputSchema ? { inputSchema } : {}),
655
+ ...(outputSchema ? { outputSchema } : {}),
656
+ ...(Array.isArray(manifest.requiredOAuthScopes) && manifest.requiredOAuthScopes.length > 0
657
+ ? { requiredOAuthScopes: manifest.requiredOAuthScopes }
658
+ : {}),
659
+ ...(Array.isArray(credentialRequirements) && credentialRequirements.length > 0
660
+ ? { credentialSets: credentialRequirements.map(credentialRequirementToFlowGraphEntry) }
661
+ : Array.isArray(manifest.credentialSets) && manifest.credentialSets.length > 0
662
+ ? { credentialSets: manifest.credentialSets.map(manifestCredentialSetToFlowGraphEntry) }
663
+ : {}),
664
+ ...(nestedRunSource ? { nestedRunSource } : {}),
665
+ };
666
+ staticFlowResolutionCache.set(cacheKey, enriched);
667
+ return enriched;
668
+ } catch (error) {
669
+ if (isLocalTypedPrimitiveImport(resolution)) {
670
+ throw error;
671
+ }
672
+ return fallback;
673
+ }
674
+ }
675
+
676
+ function importCachedModule(moduleUrl) {
677
+ const cached = staticFlowModuleCache.get(moduleUrl);
678
+ if (cached) {
679
+ return cached;
680
+ }
681
+ const promise = import(moduleUrl);
682
+ staticFlowModuleCache.set(moduleUrl, promise);
683
+ return promise;
684
+ }
685
+
686
+ function compactManifestForFlowGraph(manifest) {
687
+ return {
688
+ ...(manifest.type ? { type: manifest.type } : {}),
689
+ ...(manifest.name ? { name: manifest.name } : {}),
690
+ ...(manifest.description ? { description: manifest.description } : {}),
691
+ ...(isJsonSchema(manifest.input) ? { input: manifest.input } : {}),
692
+ ...(isJsonSchema(manifest.output) ? { output: manifest.output } : {}),
693
+ ...(manifest.workflowSchemas
694
+ ? {
695
+ workflowSchemas: {
696
+ ...(isJsonSchema(manifest.workflowSchemas.input)
697
+ ? { input: manifest.workflowSchemas.input }
698
+ : {}),
699
+ ...(isJsonSchema(manifest.workflowSchemas.output)
700
+ ? { output: manifest.workflowSchemas.output }
701
+ : {}),
702
+ },
703
+ }
704
+ : {}),
705
+ ...(Array.isArray(manifest.requiredOAuthScopes) && manifest.requiredOAuthScopes.length > 0
706
+ ? { requiredOAuthScopes: [...manifest.requiredOAuthScopes] }
707
+ : {}),
708
+ ...(Array.isArray(manifest.credentialSets) && manifest.credentialSets.length > 0
709
+ ? { credentialSets: [...manifest.credentialSets] }
710
+ : {}),
711
+ };
712
+ }
713
+
714
+ function getInputSchema(manifest) {
715
+ const schema = manifest.workflowSchemas?.input ?? manifest.input;
716
+ return isJsonSchema(schema) ? schema : undefined;
717
+ }
718
+
719
+ function getOutputSchema(manifest) {
720
+ const schema = manifest.workflowSchemas?.output ?? manifest.output;
721
+ return isJsonSchema(schema) ? schema : undefined;
722
+ }
723
+
724
+ function isJsonSchema(value) {
725
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
726
+ }
727
+
728
+ function inferCallKindFromPath(filePath, manifest) {
729
+ if (!filePath) {
730
+ return manifest?.type === 'agent' ? 'agent' : 'workflow-step';
731
+ }
732
+ const lower = filePath.toLowerCase();
733
+ if (lower.endsWith('.agent.ts') || lower.endsWith('.agent.tsx') || lower.endsWith('.agent.mts') || manifest?.type === 'agent') {
734
+ return 'agent';
735
+ }
736
+ if (lower.endsWith('.workflow.ts') || lower.endsWith('.workflow.tsx') || lower.endsWith('.workflow.mts')) {
737
+ return 'child-workflow';
738
+ }
739
+ return 'workflow-step';
740
+ }
741
+
742
+ function collectRunCalleeNames(root) {
743
+ const names = new Set();
744
+ visitNode(root, (node) => {
745
+ if (node.type !== 'CallExpression' || !isNode(node.callee)) {
746
+ return;
747
+ }
748
+ const callee = node.callee;
749
+ if (callee.type !== 'MemberExpression') {
750
+ return;
751
+ }
752
+ if (identifierName(callee.property) !== 'run') {
753
+ return;
754
+ }
755
+ const objectName = identifierName(callee.object);
756
+ if (objectName) {
757
+ names.add(objectName);
758
+ }
759
+ });
760
+ return names;
761
+ }
762
+
763
+ function isLocalTypedPrimitiveImport(resolution) {
764
+ if (resolution.importSource?.kind !== 'local' || !resolution.resolvedPath) {
765
+ return false;
766
+ }
767
+ const lower = resolution.resolvedPath.toLowerCase();
768
+ return (
769
+ lower.endsWith('.step.ts') ||
770
+ lower.endsWith('.step.tsx') ||
771
+ lower.endsWith('.step.mts') ||
772
+ lower.endsWith('.operation.ts') ||
773
+ lower.endsWith('.operation.tsx') ||
774
+ lower.endsWith('.operation.mts') ||
775
+ lower.endsWith('.tool.ts') ||
776
+ lower.endsWith('.tool.tsx') ||
777
+ lower.endsWith('.tool.mts') ||
778
+ lower.endsWith('.agent.ts') ||
779
+ lower.endsWith('.agent.tsx') ||
780
+ lower.endsWith('.agent.mts') ||
781
+ lower.endsWith('.workflow.ts') ||
782
+ lower.endsWith('.workflow.tsx') ||
783
+ lower.endsWith('.workflow.mts')
784
+ );
785
+ }
786
+
787
+ function credentialRequirementToFlowGraphEntry(entry) {
788
+ return {
789
+ resolvedId: entry.resolvedCredentialSetId,
790
+ credentialKeys: [...(entry.credentialKeys ?? [])],
791
+ ...(entry.scope ? { scope: entry.scope } : {}),
792
+ ...(entry.alias ? { alias: entry.alias } : {}),
793
+ ...(entry.credentialRef ? { credentialRef: entry.credentialRef } : {}),
794
+ ...(Array.isArray(entry.optionalCredentialKeys) && entry.optionalCredentialKeys.length > 0
795
+ ? { optionalCredentialKeys: [...entry.optionalCredentialKeys] }
796
+ : {}),
797
+ ...(Array.isArray(entry.storedCredentialKeys) && entry.storedCredentialKeys.length > 0
798
+ ? { storedCredentialKeys: [...entry.storedCredentialKeys] }
799
+ : {}),
800
+ ...(Array.isArray(entry.optionalStoredCredentialKeys) && entry.optionalStoredCredentialKeys.length > 0
801
+ ? { optionalStoredCredentialKeys: [...entry.optionalStoredCredentialKeys] }
802
+ : {}),
803
+ ...(entry.proxy ? { proxy: entry.proxy } : {}),
804
+ ...(entry.needsRawSecret === true ? { needsRawSecret: true } : {}),
805
+ ...(entry.needsResolve === true ? { needsResolve: true } : {}),
806
+ ...(typeof entry.resolveCacheMs === 'number' && entry.resolveCacheMs > 0
807
+ ? { resolveCacheMs: entry.resolveCacheMs }
808
+ : {}),
809
+ ...(entry.onCredentialRevoked ? { onCredentialRevoked: entry.onCredentialRevoked } : {}),
810
+ ...(entry.schemaFingerprint ? { schemaFingerprint: entry.schemaFingerprint } : {}),
811
+ };
812
+ }
813
+
814
+ function manifestCredentialSetToFlowGraphEntry(entry) {
815
+ return {
816
+ resolvedId: entry.resolvedCredentialSetId,
817
+ credentialKeys: Array.isArray(entry.credentialKeys)
818
+ ? [...entry.credentialKeys]
819
+ : Object.keys(entry.auth?.properties ?? {}),
820
+ ...(entry.scope ? { scope: entry.scope } : {}),
821
+ ...(entry.alias ? { alias: entry.alias } : {}),
822
+ ...(entry.credentialRef ? { credentialRef: entry.credentialRef } : {}),
823
+ ...(Array.isArray(entry.optionalCredentialKeys) && entry.optionalCredentialKeys.length > 0
824
+ ? { optionalCredentialKeys: [...entry.optionalCredentialKeys] }
825
+ : {}),
826
+ ...(Array.isArray(entry.storedCredentialKeys) && entry.storedCredentialKeys.length > 0
827
+ ? { storedCredentialKeys: [...entry.storedCredentialKeys] }
828
+ : Object.keys(entry.stored?.properties ?? {}).length > 0
829
+ ? { storedCredentialKeys: Object.keys(entry.stored?.properties ?? {}) }
830
+ : {}),
831
+ ...(Array.isArray(entry.optionalStoredCredentialKeys) && entry.optionalStoredCredentialKeys.length > 0
832
+ ? { optionalStoredCredentialKeys: [...entry.optionalStoredCredentialKeys] }
833
+ : {}),
834
+ ...(entry.proxy ? { proxy: entry.proxy } : {}),
835
+ ...(entry.needsRawSecret === true ? { needsRawSecret: true } : {}),
836
+ ...(entry.needsResolve === true ? { needsResolve: true } : {}),
837
+ ...(typeof entry.resolveCacheMs === 'number' && entry.resolveCacheMs > 0
838
+ ? { resolveCacheMs: entry.resolveCacheMs }
839
+ : {}),
840
+ ...(entry.onCredentialRevoked ? { onCredentialRevoked: entry.onCredentialRevoked } : {}),
841
+ ...(entry.schemaFingerprint ? { schemaFingerprint: entry.schemaFingerprint } : {}),
842
+ };
843
+ }
844
+
845
+ function parseSourceFile(filePath, sourceText) {
846
+ const result = parseSync(filePath, sourceText, {
847
+ lang: filePath.endsWith('.tsx') ? 'tsx' : 'ts',
848
+ sourceType: 'module',
849
+ astType: 'ts',
850
+ preserveParens: true,
851
+ });
852
+ if (result.errors.length > 0) {
853
+ throw new Error('Failed to parse ' + filePath + ': ' + result.errors.map((error) => error.message).join('; '));
854
+ }
855
+ return {
856
+ program: result.program,
857
+ statements: result.program.body,
858
+ };
859
+ }
860
+
861
+ function visitNode(value, visitor) {
862
+ if (!isNode(value)) {
863
+ return;
864
+ }
865
+ visitor(value);
866
+ for (const child of Object.values(value)) {
867
+ if (Array.isArray(child)) {
868
+ for (const item of child) {
869
+ visitNode(item, visitor);
870
+ }
871
+ continue;
872
+ }
873
+ visitNode(child, visitor);
874
+ }
875
+ }
876
+
877
+ function isNode(value) {
878
+ return value && typeof value === 'object' && typeof value.type === 'string';
879
+ }
880
+
881
+ function identifierName(node) {
882
+ return isNode(node) && node.type === 'Identifier' && typeof node.name === 'string'
883
+ ? node.name
884
+ : undefined;
885
+ }
886
+
887
+ function literalString(node) {
888
+ return isNode(node) && node.type === 'Literal' && typeof node.value === 'string'
889
+ ? node.value
890
+ : undefined;
891
+ }
892
+
893
+ function isExportedDeclaration(statement) {
894
+ return (
895
+ statement.type === 'ExportNamedDeclaration' &&
896
+ isNode(statement.declaration) &&
897
+ ['VariableDeclaration', 'FunctionDeclaration', 'ClassDeclaration'].includes(statement.declaration.type)
898
+ );
899
+ }
900
+
901
+ function getVariableDeclarators(statement) {
902
+ const declaration =
903
+ statement.type === 'VariableDeclaration'
904
+ ? statement
905
+ : statement.type === 'ExportNamedDeclaration' && isNode(statement.declaration)
906
+ ? statement.declaration
907
+ : null;
908
+ if (!declaration || declaration.type !== 'VariableDeclaration') {
909
+ return [];
910
+ }
911
+ return Array.isArray(declaration.declarations)
912
+ ? declaration.declarations.filter((node) => isNode(node))
913
+ : [];
914
+ }
915
+
916
+ function getDeclaredBindingNames(statement) {
917
+ const declaration =
918
+ statement.type === 'ExportNamedDeclaration' && isNode(statement.declaration)
919
+ ? statement.declaration
920
+ : statement;
921
+
922
+ if (declaration.type === 'VariableDeclaration') {
923
+ return getVariableDeclarators(declaration)
924
+ .map((variable) => identifierName(variable.id))
925
+ .filter((name) => typeof name === 'string');
926
+ }
927
+
928
+ const name = identifierName(declaration.id);
929
+ return name ? [name] : [];
930
+ }
931
+
932
+ function addExportName(exportNamesByLocalName, localName, exportName) {
933
+ const exportNames = exportNamesByLocalName.get(localName) ?? new Set();
934
+ exportNames.add(exportName);
935
+ exportNamesByLocalName.set(localName, exportNames);
936
+ }
937
+
938
+ async function resolveStepCredentialsFromImports(flowGraph) {
939
+ for (const node of flowGraph.nodes) {
940
+ if (node.type !== 'step' || node.data.kind !== 'call') continue;
941
+ const importSource = node.data.importSource;
942
+ if (!importSource?.moduleSpecifier || !importSource?.importName) continue;
943
+
944
+ try {
945
+ const moduleUrl =
946
+ importSource.kind === 'local' && importSource.resolvedPath
947
+ ? pathToFileURL(path.resolve(process.cwd(), importSource.resolvedPath)).href
948
+ : importSource.moduleSpecifier;
949
+ const mod = await import(moduleUrl);
950
+ const instance = mod[importSource.importName];
951
+ if (!instance || typeof instance.getCredentialRequirements !== 'function') continue;
952
+
953
+ const reqs = instance.getCredentialRequirements();
954
+ if (Array.isArray(reqs) && reqs.length > 0) {
955
+ node.data.credentialSets = reqs.map((r) => ({
956
+ resolvedId: r.resolvedCredentialSetId,
957
+ credentialKeys: r.credentialKeys,
958
+ ...(Array.isArray(r.optionalCredentialKeys) && r.optionalCredentialKeys.length > 0
959
+ ? { optionalCredentialKeys: r.optionalCredentialKeys }
960
+ : {}),
961
+ ...(Array.isArray(r.storedCredentialKeys) && r.storedCredentialKeys.length > 0
962
+ ? { storedCredentialKeys: r.storedCredentialKeys }
963
+ : {}),
964
+ ...(Array.isArray(r.optionalStoredCredentialKeys) &&
965
+ r.optionalStoredCredentialKeys.length > 0
966
+ ? { optionalStoredCredentialKeys: r.optionalStoredCredentialKeys }
967
+ : {}),
968
+ ...(r.proxy ? { proxy: r.proxy } : {}),
969
+ ...(r.needsRawSecret === true ? { needsRawSecret: true } : {}),
970
+ ...(r.needsResolve === true ? { needsResolve: true } : {}),
971
+ ...(typeof r.resolveCacheMs === 'number' && r.resolveCacheMs > 0
972
+ ? { resolveCacheMs: r.resolveCacheMs }
973
+ : {}),
974
+ ...(r.onCredentialRevoked ? { onCredentialRevoked: r.onCredentialRevoked } : {}),
975
+ ...(r.schemaFingerprint ? { schemaFingerprint: r.schemaFingerprint } : {}),
976
+ }));
977
+ }
978
+ } catch {
979
+ // Module not resolvable — leave without credentials.
980
+ }
981
+ }
982
+ }
983
+
984
+ function discoverCredentialsByBindingName(mod) {
985
+ const result = {};
986
+
987
+ for (const [exportName, value] of Object.entries(mod)) {
988
+ if (value instanceof Step) {
989
+ const entries = getCredentialRequirements(value);
990
+ if (entries.length > 0) {
991
+ result[exportName] = entries;
992
+ if (value.name !== exportName) {
993
+ result[value.name] = entries;
994
+ }
995
+ }
996
+ continue;
997
+ }
998
+ if (value instanceof Agent) {
999
+ const entries = getCredentialRequirements(value);
1000
+ if (entries.length > 0) {
1001
+ result[exportName] = entries;
1002
+ if (value.name !== exportName) {
1003
+ result[value.name] = entries;
1004
+ }
1005
+ }
1006
+ continue;
1007
+ }
1008
+ }
1009
+ return result;
1010
+ }
1011
+
1012
+ async function loadSiblingTriggerModules(workflowFilePath) {
1013
+ const dir = dirname(workflowFilePath);
1014
+ const results = [];
1015
+ try {
1016
+ const files = readdirSync(dir);
1017
+ for (const file of files) {
1018
+ if (file.endsWith('.trigger.ts') || file.endsWith('.trigger.js')) {
1019
+ const fullPath = join(dir, file);
1020
+ if (fullPath === workflowFilePath) continue;
1021
+ try {
1022
+ const mod = await import(pathToFileURL(fullPath).href);
1023
+ results.push({ filePath: fullPath, exports: mod });
1024
+ } catch {
1025
+ // skip files that fail to import
1026
+ }
1027
+ }
1028
+ }
1029
+ } catch {
1030
+ // skip if directory read fails
1031
+ }
1032
+ return results;
1033
+ }
1034
+
1035
+ function isTriggerLike(value) {
1036
+ return value != null && (typeof value === 'object' || typeof value === 'function')
1037
+ && typeof value.toManifest === 'function' && typeof value.name === 'string';
1038
+ }
1039
+
1040
+ function isBoundTriggerLike(value) {
1041
+ return value != null && (typeof value === 'object' || typeof value === 'function')
1042
+ && value.isBoundTrigger === true && value.trigger != null
1043
+ && typeof value.trigger.toManifest === 'function' && typeof value.trigger.name === 'string';
1044
+ }
1045
+
1046
+ function getTriggerCandidate(value) {
1047
+ if (isTriggerLike(value)) {
1048
+ return value;
1049
+ }
1050
+ if (isBoundTriggerLike(value)) {
1051
+ return value.trigger;
1052
+ }
1053
+ return null;
1054
+ }
1055
+
1056
+
1057
+ function describeTriggerEntry(entry) {
1058
+ if (entry && typeof entry === 'object') {
1059
+ if ('isBoundTrigger' in entry && entry.isBoundTrigger === true && entry.trigger) {
1060
+ return entry.trigger.name ?? '<unknown trigger>';
1061
+ }
1062
+ if ('name' in entry && typeof entry.name === 'string') {
1063
+ return entry.name;
1064
+ }
1065
+ }
1066
+ return '<unknown trigger>';
1067
+ }
1068
+
1069
+ function findTriggerSource(entry, trigger, workflowMod, workflowFilePath, siblingModules) {
1070
+ const triggerName = trigger.name;
1071
+ // First, check if the trigger is re-exported from the workflow module (identity check)
1072
+ for (const [name, exp] of Object.entries(workflowMod)) {
1073
+ const candidate = getTriggerCandidate(exp);
1074
+ if (exp === entry || exp === trigger || candidate === trigger) {
1075
+ return { triggerExportName: name, sourceFilePath: workflowFilePath };
1076
+ }
1077
+ }
1078
+
1079
+ const siblingMatches = [];
1080
+ for (const { filePath, exports: triggerMod } of siblingModules) {
1081
+ for (const [name, exp] of Object.entries(triggerMod)) {
1082
+ const candidate = getTriggerCandidate(exp);
1083
+ if (exp === entry || exp === trigger || candidate === trigger || candidate?.name === triggerName) {
1084
+ siblingMatches.push({ triggerExportName: name, sourceFilePath: filePath });
1085
+ continue;
1086
+ }
1087
+
1088
+ if (isBoundTriggerLike(exp) && exp.trigger.name === triggerName) {
1089
+ siblingMatches.push({ triggerExportName: name, sourceFilePath: filePath });
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ if (siblingMatches.length > 1) {
1095
+ const matchList = siblingMatches
1096
+ .map((match) => String(match.triggerExportName) + ' (' + String(match.sourceFilePath) + ')')
1097
+ .join(', ');
1098
+ throw new Error(
1099
+ 'Ambiguous trigger source for "' + triggerName + '" in ' + workflowFilePath + '. Matching sibling exports: ' + matchList
1100
+ );
1101
+ }
1102
+
1103
+ if (siblingMatches.length === 1) {
1104
+ return siblingMatches[0];
1105
+ }
1106
+
1107
+ return { triggerExportName: undefined, sourceFilePath: workflowFilePath };
1108
+ }
1109
+
1110
+ async function discoverTriggers(workflow, mod, workflowFilePath) {
1111
+ const triggers = [];
1112
+ const triggerEntries = workflow.triggers ?? [];
1113
+ if (triggerEntries.length === 0) return triggers;
1114
+
1115
+ const siblingModules = await loadSiblingTriggerModules(workflowFilePath);
1116
+
1117
+ for (let triggerIndex = 0; triggerIndex < triggerEntries.length; triggerIndex++) {
1118
+ const entry = triggerEntries[triggerIndex];
1119
+ try {
1120
+ const isBound = 'isBoundTrigger' in entry && entry.isBoundTrigger === true;
1121
+ const trigger = isBound ? entry.trigger : entry;
1122
+ const triggerManifest = trigger.toManifest();
1123
+ const hasTransform = isBound && typeof entry.transform === 'function';
1124
+ const hasFilter = isBound ? typeof entry.filter === 'function' : typeof trigger.filter === 'function';
1125
+ const { triggerExportName, sourceFilePath } = findTriggerSource(entry, trigger, mod, workflowFilePath, siblingModules);
1126
+ // For cron triggers, capture the static payload for runtime use
1127
+ const staticPayload = triggerManifest.triggerType === 'cron' && 'payload' in trigger
1128
+ ? trigger.payload
1129
+ : undefined;
1130
+ triggers.push({ triggerManifest, triggerExportName, sourceFilePath, hasTransform, hasFilter, staticPayload, triggerIndex });
1131
+ } catch (error) {
1132
+ const triggerName = describeTriggerEntry(entry);
1133
+ const message = serializeError(error);
1134
+ throw new Error(
1135
+ 'Failed to discover trigger "' + triggerName + '" for workflow "' + workflow.name + '": ' + message
1136
+ );
1137
+ }
1138
+ }
1139
+ return triggers;
1140
+ }
1141
+
1142
+ async function loadOne(input) {
1143
+ const importStartedAt = performance.now();
1144
+ const moduleUrl = pathToFileURL(input.resolvedFilePath);
1145
+ moduleUrl.searchParams.set('keystrokeLoader', input.cacheKey);
1146
+
1147
+ let importedModule;
1148
+ try {
1149
+ importedModule = await import(moduleUrl.href);
1150
+ } catch (error) {
1151
+ await emitError(input.bindingKey, 'import', error);
1152
+ return;
1153
+ }
1154
+ const importMs = performance.now() - importStartedAt;
1155
+
1156
+
1157
+
1158
+ const resolvedBinding =
1159
+ input.localExportName === 'default'
1160
+ ? importedModule.default
1161
+ : importedModule[input.localExportName];
1162
+
1163
+ if (resolvedBinding instanceof Agent) {
1164
+ const manifestStartedAt = performance.now();
1165
+ let manifest;
1166
+ let toolEnrichments;
1167
+ try {
1168
+ manifest = resolvedBinding.toManifest();
1169
+ toolEnrichments = await computeAgentToolEnrichments(resolvedBinding);
1170
+ } catch (error) {
1171
+ await emitError(input.bindingKey, 'manifest', error);
1172
+ return;
1173
+ }
1174
+ const manifestMs = performance.now() - manifestStartedAt;
1175
+
1176
+ await writeJson({
1177
+ bindingKey: input.bindingKey,
1178
+ status: 'success',
1179
+ isAgent: true,
1180
+ manifest,
1181
+ toolEnrichments,
1182
+ timing: { importMs, manifestMs },
1183
+ });
1184
+ return;
1185
+ }
1186
+
1187
+ if (resolvedBinding instanceof Task) {
1188
+ const manifestStartedAt = performance.now();
1189
+ let manifest;
1190
+ try {
1191
+ manifest = resolvedBinding.toManifest();
1192
+ } catch (error) {
1193
+ await emitError(input.bindingKey, 'manifest', error);
1194
+ return;
1195
+ }
1196
+ const manifestMs = performance.now() - manifestStartedAt;
1197
+
1198
+ let triggers;
1199
+ try {
1200
+ triggers = await discoverTriggers(resolvedBinding, importedModule, input.resolvedFilePath);
1201
+ } catch (error) {
1202
+ await emitError(input.bindingKey, 'import', error);
1203
+ return;
1204
+ }
1205
+
1206
+ await writeJson({
1207
+ bindingKey: input.bindingKey,
1208
+ status: 'success',
1209
+ isTask: true,
1210
+ manifest,
1211
+ triggers,
1212
+ timing: { importMs, manifestMs },
1213
+ });
1214
+ return;
1215
+ }
1216
+
1217
+ if (!(resolvedBinding instanceof Workflow)) {
1218
+ await emitError(
1219
+ input.bindingKey,
1220
+ 'import',
1221
+ \`Discovered binding \${input.bindingKey} did not resolve to a Workflow, Agent, or Task instance\`
1222
+ );
1223
+ return;
1224
+ }
1225
+
1226
+ const manifestStartedAt = performance.now();
1227
+ let manifest;
1228
+ try {
1229
+ manifest = resolvedBinding.toManifest();
1230
+ } catch (error) {
1231
+ await emitError(input.bindingKey, 'manifest', error);
1232
+ return;
1233
+ }
1234
+ const manifestMs = performance.now() - manifestStartedAt;
1235
+
1236
+ const flowGraphStartedAt = performance.now();
1237
+ let flowGraph;
1238
+ try {
1239
+ const resolveCandidate = await createStaticFlowResolver({
1240
+ filePath: input.resolvedFilePath,
1241
+ projectRoot: process.cwd(),
1242
+ });
1243
+ flowGraph = await resolvedBinding.toFlowGraph({ resolveCandidate });
1244
+ } catch (error) {
1245
+ await emitError(input.bindingKey, 'flow-graph', error);
1246
+ return;
1247
+ }
1248
+ const flowGraphMs = performance.now() - flowGraphStartedAt;
1249
+
1250
+ let triggers;
1251
+ try {
1252
+ triggers = await discoverTriggers(resolvedBinding, importedModule, input.resolvedFilePath);
1253
+ } catch (error) {
1254
+ await emitError(input.bindingKey, 'import', error);
1255
+ return;
1256
+ }
1257
+ const attribution = await buildLoaderCredentialAttribution({
1258
+ flowGraph,
1259
+ filePath: input.resolvedFilePath,
1260
+ projectRoot: process.cwd(),
1261
+ credentialsByBindingName: discoverCredentialsByBindingName(importedModule),
1262
+ });
1263
+ await resolveStepCredentialsFromImports(attribution.flowGraph);
1264
+
1265
+ await writeJson({
1266
+ bindingKey: input.bindingKey,
1267
+ status: 'success',
1268
+ manifest,
1269
+ flowGraph: attribution.flowGraph,
1270
+ triggers,
1271
+ credentialsByCallsiteFingerprint: attribution.credentialsByCallsiteFingerprint,
1272
+ credentialAttributionWarnings: attribution.credentialAttributionWarnings,
1273
+ timing: {
1274
+ importMs,
1275
+ manifestMs,
1276
+ flowGraphMs,
1277
+ },
1278
+ });
1279
+ }
1280
+
1281
+ async function main() {
1282
+ if (!encodedInput) {
1283
+ await emitError('', 'import', 'Missing KEYSTROKE_WORKFLOW_LOADER_INPUT');
1284
+ return;
1285
+ }
1286
+
1287
+ try {
1288
+ const payload = JSON.parse(Buffer.from(encodedInput, 'base64').toString('utf-8'));
1289
+ const inputs = Array.isArray(payload) ? payload : [payload];
1290
+ for (const input of inputs) {
1291
+ try {
1292
+ await loadOne(input);
1293
+ } catch (error) {
1294
+ await emitError(input.bindingKey ?? '', 'import', error);
1295
+ }
1296
+ }
1297
+ } catch (error) {
1298
+ await emitError('', 'import', error);
1299
+ }
1300
+ }
1301
+
1302
+ await main();
1303
+ // Importing integration packages (discoverIntegrationCredentials) can leave HTTP keep-alive
1304
+ // handles open; without an explicit exit, the subprocess would stay alive and execFile never returns.
1305
+ process.exit(0);
1306
+ `;
1307
+ }
1308
+ const execFileAsync = promisify(execFile);
1309
+ const TSX_IMPORT_PATH = pathToFileURL(createRequire(import.meta.url).resolve("tsx")).href;
1310
+ function resolveNodeBinary() {
1311
+ const nodePath = process.env.KEYSTROKE_NODE_PATH;
1312
+ if (nodePath) return nodePath;
1313
+ if (path$1.basename(process.execPath) === "node") return process.execPath;
1314
+ return "node";
1315
+ }
1316
+ function extractFirstLine(text) {
1317
+ const line = text.split("\n")[0]?.trim() ?? text.trim();
1318
+ return line.length > 200 ? `${line.slice(0, 200)}…` : line;
1319
+ }
1320
+ const LoadedTriggerManifestSchema = z.union([
1321
+ cronTriggerManifestSchema,
1322
+ pollingTriggerManifestSchema,
1323
+ webhookTriggerManifestSchema
1324
+ ]);
1325
+ const LoadedWorkflowTriggerSchema = z.object({
1326
+ triggerManifest: LoadedTriggerManifestSchema,
1327
+ triggerExportName: z.string().optional(),
1328
+ sourceFilePath: z.string().optional(),
1329
+ hasTransform: z.boolean(),
1330
+ hasFilter: z.boolean(),
1331
+ staticPayload: z.unknown().optional(),
1332
+ triggerIndex: z.number()
1333
+ });
1334
+ const LoaderTimingSchema = z.object({
1335
+ importMs: z.number(),
1336
+ manifestMs: z.number(),
1337
+ flowGraphMs: z.number()
1338
+ });
1339
+ const DeclaredCredentialEntrySchema = DeclaredCredentialRequirementSchema;
1340
+ const CredentialsByCallsiteFingerprintSchema = z.record(z.string(), z.array(DeclaredCredentialEntrySchema));
1341
+ const CredentialAttributionWarningSchema = z.object({
1342
+ category: z.literal("unmatched-callsite"),
1343
+ bindingName: z.string(),
1344
+ credentialSetIds: z.array(z.string())
1345
+ });
1346
+ const LoaderSuccessSchema = z.object({
1347
+ status: z.literal("success"),
1348
+ manifest: z.unknown(),
1349
+ flowGraph: FlowGraphSchema,
1350
+ triggers: z.array(LoadedWorkflowTriggerSchema).optional().default([]),
1351
+ credentialsByCallsiteFingerprint: CredentialsByCallsiteFingerprintSchema.optional().default({}),
1352
+ credentialAttributionWarnings: z.array(CredentialAttributionWarningSchema).optional().default([]),
1353
+ timing: LoaderTimingSchema
1354
+ });
1355
+ const LoaderFailureSchema = z.object({
1356
+ status: z.literal("error"),
1357
+ phase: z.enum([
1358
+ "import",
1359
+ "manifest",
1360
+ "flow-graph"
1361
+ ]),
1362
+ error: z.string().min(1)
1363
+ });
1364
+ const AgentToolSuspensionReasonSchema = z.object({
1365
+ kind: z.enum([
1366
+ "wait",
1367
+ "hook",
1368
+ "child-workflow-suspending",
1369
+ "agent",
1370
+ "long-step"
1371
+ ]),
1372
+ location: z.unknown().optional(),
1373
+ detail: z.string(),
1374
+ stepId: z.string().optional(),
1375
+ hookName: z.string().optional(),
1376
+ authoredWorkflowId: z.string().optional()
1377
+ });
1378
+ const AgentToolEnrichmentSchema = z.object({
1379
+ invocation: z.enum(["sync", "yield"]).optional(),
1380
+ suspensions: z.array(AgentToolSuspensionReasonSchema).optional(),
1381
+ outputSchemaIsTabular: z.boolean().optional(),
1382
+ dispatch: z.enum(["sandbox", "host"]).optional(),
1383
+ /** Workflow tools: derived from analyzeWorkflowCredentials. Replaces operation-derived credentialSets when present. */
1384
+ credentialSets: z.array(z.object({
1385
+ id: z.string(),
1386
+ required: z.boolean()
1387
+ })).optional()
1388
+ });
1389
+ const LoaderAgentSuccessSchema = z.object({
1390
+ status: z.literal("success"),
1391
+ isAgent: z.literal(true),
1392
+ manifest: AgentManifestSchema,
1393
+ toolEnrichments: z.record(z.string(), AgentToolEnrichmentSchema).optional().default({}),
1394
+ timing: z.object({
1395
+ importMs: z.number(),
1396
+ manifestMs: z.number()
1397
+ })
1398
+ });
1399
+ const LoaderTaskSuccessSchema = z.object({
1400
+ status: z.literal("success"),
1401
+ isTask: z.literal(true),
1402
+ manifest: z.unknown(),
1403
+ triggers: z.array(LoadedWorkflowTriggerSchema).optional().default([]),
1404
+ timing: z.object({
1405
+ importMs: z.number(),
1406
+ manifestMs: z.number()
1407
+ })
1408
+ });
1409
+ const LoaderResultSchema = z.discriminatedUnion("status", [LoaderSuccessSchema, LoaderFailureSchema]);
1410
+ const WORKFLOW_LOADER_SCRIPT = createWorkflowLoaderScript();
1411
+ var WorkflowMetadataLoadError = class extends Error {
1412
+ phase;
1413
+ constructor(phase, message) {
1414
+ super(message);
1415
+ this.name = "WorkflowMetadataLoadError";
1416
+ this.phase = phase;
1417
+ }
1418
+ };
1419
+ var WorkflowLoaderProtocolError = class extends Error {
1420
+ source;
1421
+ rawOutput;
1422
+ constructor(source, message, rawOutput) {
1423
+ super(message);
1424
+ this.name = "WorkflowLoaderProtocolError";
1425
+ this.source = source;
1426
+ this.rawOutput = rawOutput;
1427
+ }
1428
+ };
1429
+ async function loadWorkflowMetadata(discoveredWorkflow) {
1430
+ const projectRoot = await resolveLoaderProjectRoot(discoveredWorkflow.resolvedFilePath);
1431
+ try {
1432
+ const { stdout } = await execFileAsync(resolveNodeBinary(), [
1433
+ "--import",
1434
+ TSX_IMPORT_PATH,
1435
+ "--input-type=module",
1436
+ "--eval",
1437
+ WORKFLOW_LOADER_SCRIPT
1438
+ ], {
1439
+ cwd: projectRoot,
1440
+ env: {
1441
+ ...process.env,
1442
+ KEYSTROKE_WORKFLOW_LOADER_INPUT: Buffer.from(JSON.stringify({
1443
+ bindingKey: discoveredWorkflow.bindingKey,
1444
+ resolvedFilePath: discoveredWorkflow.resolvedFilePath,
1445
+ localExportName: discoveredWorkflow.localExportName,
1446
+ cacheKey: randomUUID()
1447
+ })).toString("base64")
1448
+ },
1449
+ maxBuffer: 10 * 1024 * 1024
1450
+ });
1451
+ return parseLoaderOutput(stdout, discoveredWorkflow);
1452
+ } catch (error) {
1453
+ const failure = error;
1454
+ const stdout = (failure.stdout ?? "").trim();
1455
+ if (stdout.length > 0) return parseLoaderOutput(stdout, discoveredWorkflow);
1456
+ const stderr = (failure.stderr ?? "").trim();
1457
+ const exportLabel = discoveredWorkflow.localExportName;
1458
+ if (stderr.length > 0) throw new WorkflowLoaderProtocolError("stderr", `Workflow loader crashed for export "${exportLabel}".`, stderr);
1459
+ const rawMessage = (failure.message ?? "").trim();
1460
+ throw new WorkflowLoaderProtocolError("process", rawMessage.length > 0 ? `Export "${exportLabel}" failed: ${extractFirstLine(rawMessage)}` : `Export "${exportLabel}" failed with no output.`, rawMessage || void 0);
1461
+ }
1462
+ }
1463
+ function isAgentLoadResult(result) {
1464
+ return result.ok && "agentMetadata" in result;
1465
+ }
1466
+ function isWorkflowLoadResult(result) {
1467
+ return result.ok && "metadata" in result;
1468
+ }
1469
+ function isTaskLoadResult(result) {
1470
+ return result.ok && "taskMetadata" in result;
1471
+ }
1472
+ async function loadWorkflowMetadataBatch(workflows, options) {
1473
+ if (workflows.length === 0) return [];
1474
+ if (workflows.length === 1 && workflows[0]) return [await loadSingleAsBatchResult(workflows[0])];
1475
+ const byProjectRoot = /* @__PURE__ */ new Map();
1476
+ for (const workflow of workflows) {
1477
+ const projectRoot = await resolveLoaderProjectRoot(workflow.resolvedFilePath);
1478
+ const group = byProjectRoot.get(projectRoot);
1479
+ if (group) group.push(workflow);
1480
+ else byProjectRoot.set(projectRoot, [workflow]);
1481
+ }
1482
+ return loadViaSubprocessShards(byProjectRoot, options?.concurrency);
1483
+ }
1484
+ async function loadViaSubprocessShards(byProjectRoot, concurrency) {
1485
+ const shardCount = resolveWorkflowMetadataLoaderShardCount(Array.from(byProjectRoot.values()).reduce((total, group) => total + group.length, 0), concurrency);
1486
+ const batches = [];
1487
+ for (const [projectRoot, group] of byProjectRoot) {
1488
+ const shardSize = Math.max(1, Math.ceil(group.length / shardCount));
1489
+ for (let i = 0; i < group.length; i += shardSize) batches.push({
1490
+ index: batches.length,
1491
+ projectRoot,
1492
+ workflows: group.slice(i, i + shardSize)
1493
+ });
1494
+ }
1495
+ const batchResults = Array.from({ length: batches.length });
1496
+ await runWithConcurrency(batches, shardCount, async (batch) => {
1497
+ batchResults[batch.index] = await runBatchSubprocess(batch.projectRoot, batch.workflows);
1498
+ });
1499
+ const results = [];
1500
+ for (const batch of batchResults) {
1501
+ if (!batch) continue;
1502
+ results.push(...batch);
1503
+ }
1504
+ return results;
1505
+ }
1506
+ function resolveWorkflowMetadataLoaderShardCount(workflowCount, concurrency) {
1507
+ if (workflowCount <= 0) return 1;
1508
+ if (concurrency !== void 0) {
1509
+ if (!Number.isFinite(concurrency) || concurrency < 1) return 1;
1510
+ return Math.min(Math.floor(concurrency), workflowCount);
1511
+ }
1512
+ return Math.min((os.availableParallelism?.() ?? os.cpus().length) || 4, 8, workflowCount);
1513
+ }
1514
+ async function loadSingleAsBatchResult(workflow) {
1515
+ try {
1516
+ const result = await loadWorkflowMetadata(workflow);
1517
+ if ("flowGraph" in result) return {
1518
+ ok: true,
1519
+ bindingKey: workflow.bindingKey,
1520
+ metadata: result
1521
+ };
1522
+ if ("manifest" in result && result.manifest.type === "task") return {
1523
+ ok: true,
1524
+ bindingKey: workflow.bindingKey,
1525
+ taskMetadata: result
1526
+ };
1527
+ return {
1528
+ ok: true,
1529
+ bindingKey: workflow.bindingKey,
1530
+ agentMetadata: result
1531
+ };
1532
+ } catch (error) {
1533
+ return {
1534
+ ok: false,
1535
+ bindingKey: workflow.bindingKey,
1536
+ error: error instanceof Error ? error : new Error(String(error))
1537
+ };
1538
+ }
1539
+ }
1540
+ async function runBatchSubprocess(projectRoot, workflows) {
1541
+ const workflowsByKey = /* @__PURE__ */ new Map();
1542
+ const inputs = workflows.map((w) => {
1543
+ workflowsByKey.set(w.bindingKey, w);
1544
+ return {
1545
+ bindingKey: w.bindingKey,
1546
+ resolvedFilePath: w.resolvedFilePath,
1547
+ localExportName: w.localExportName,
1548
+ cacheKey: randomUUID()
1549
+ };
1550
+ });
1551
+ let stdout = "";
1552
+ let stderr = "";
1553
+ try {
1554
+ stdout = (await execFileAsync(resolveNodeBinary(), [
1555
+ "--import",
1556
+ TSX_IMPORT_PATH,
1557
+ "--input-type=module",
1558
+ "--eval",
1559
+ WORKFLOW_LOADER_SCRIPT
1560
+ ], {
1561
+ cwd: projectRoot,
1562
+ env: {
1563
+ ...process.env,
1564
+ KEYSTROKE_WORKFLOW_LOADER_INPUT: Buffer.from(JSON.stringify(inputs)).toString("base64")
1565
+ },
1566
+ maxBuffer: 50 * 1024 * 1024
1567
+ })).stdout;
1568
+ } catch (error) {
1569
+ const failure = error;
1570
+ stdout = (failure.stdout ?? "").trim();
1571
+ stderr = (failure.stderr ?? "").trim();
1572
+ if (stdout.length === 0 && stderr.length > 0) return workflows.map((w) => ({
1573
+ ok: false,
1574
+ bindingKey: w.bindingKey,
1575
+ error: new WorkflowLoaderProtocolError("stderr", `Workflow loader crashed for batch.`, stderr)
1576
+ }));
1577
+ if (stdout.length === 0) {
1578
+ const rawMessage = (failure.message ?? "").trim();
1579
+ return workflows.map((w) => ({
1580
+ ok: false,
1581
+ bindingKey: w.bindingKey,
1582
+ error: new WorkflowLoaderProtocolError("process", rawMessage.length > 0 ? `Batch loader failed: ${extractFirstLine(rawMessage)}` : "Batch loader failed with no output.", rawMessage || void 0)
1583
+ }));
1584
+ }
1585
+ }
1586
+ return parseBatchOutput(stdout, workflowsByKey);
1587
+ }
1588
+ function parseBatchOutput(output, workflowsByKey) {
1589
+ const results = [];
1590
+ const seenKeys = /* @__PURE__ */ new Set();
1591
+ let searchStart = 0;
1592
+ while (true) {
1593
+ const sentinelIndex = output.indexOf(LOADER_SENTINEL, searchStart);
1594
+ if (sentinelIndex === -1) break;
1595
+ const envelopeStart = sentinelIndex + 31;
1596
+ const newlineIndex = output.indexOf("\n", envelopeStart);
1597
+ const envelopeStr = newlineIndex === -1 ? output.slice(envelopeStart).trim() : output.slice(envelopeStart, newlineIndex).trim();
1598
+ searchStart = newlineIndex === -1 ? output.length : newlineIndex + 1;
1599
+ let bindingKey = "";
1600
+ try {
1601
+ const parsedJson = JSON.parse(envelopeStr);
1602
+ bindingKey = typeof parsedJson.bindingKey === "string" ? parsedJson.bindingKey : "";
1603
+ seenKeys.add(bindingKey);
1604
+ const workflow = workflowsByKey.get(bindingKey);
1605
+ if (!workflow) continue;
1606
+ if (parsedJson.isAgent === true) {
1607
+ const agentResult = LoaderAgentSuccessSchema.parse(parsedJson);
1608
+ results.push({
1609
+ ok: true,
1610
+ bindingKey,
1611
+ agentMetadata: {
1612
+ manifest: agentResult.manifest,
1613
+ toolEnrichments: agentResult.toolEnrichments,
1614
+ timing: agentResult.timing
1615
+ }
1616
+ });
1617
+ continue;
1618
+ }
1619
+ if (parsedJson.isTask === true) {
1620
+ const taskResult = LoaderTaskSuccessSchema.parse(parsedJson);
1621
+ results.push({
1622
+ ok: true,
1623
+ bindingKey,
1624
+ taskMetadata: {
1625
+ manifest: taskResult.manifest,
1626
+ triggers: taskResult.triggers.map((t) => ({
1627
+ ...t,
1628
+ sourceFilePath: t.sourceFilePath || workflow.resolvedFilePath
1629
+ })),
1630
+ timing: taskResult.timing
1631
+ }
1632
+ });
1633
+ continue;
1634
+ }
1635
+ const parsedResult = LoaderResultSchema.parse(parsedJson);
1636
+ if (parsedResult.status === "error") {
1637
+ results.push({
1638
+ ok: false,
1639
+ bindingKey,
1640
+ error: new WorkflowMetadataLoadError(parsedResult.phase, parsedResult.error)
1641
+ });
1642
+ continue;
1643
+ }
1644
+ const workflowResult = parsedResult;
1645
+ const manifest = normalizeWorkflowManifest(workflowResult.manifest, workflow);
1646
+ results.push({
1647
+ ok: true,
1648
+ bindingKey,
1649
+ metadata: {
1650
+ manifest,
1651
+ flowGraph: workflowResult.flowGraph,
1652
+ triggers: workflowResult.triggers.map((t) => ({
1653
+ ...t,
1654
+ sourceFilePath: t.sourceFilePath || workflow.resolvedFilePath
1655
+ })),
1656
+ credentialsByCallsiteFingerprint: workflowResult.credentialsByCallsiteFingerprint,
1657
+ credentialAttributionWarnings: workflowResult.credentialAttributionWarnings,
1658
+ timing: workflowResult.timing
1659
+ }
1660
+ });
1661
+ } catch (error) {
1662
+ const message = error instanceof Error ? error.message : String(error);
1663
+ results.push({
1664
+ ok: false,
1665
+ bindingKey,
1666
+ error: new WorkflowLoaderProtocolError("stdout", `Invalid batch loader response: ${message}`, envelopeStr)
1667
+ });
1668
+ }
1669
+ }
1670
+ for (const [bindingKey] of workflowsByKey) if (!seenKeys.has(bindingKey)) results.push({
1671
+ ok: false,
1672
+ bindingKey,
1673
+ error: new WorkflowLoaderProtocolError("process", `No response received for "${bindingKey}" from batch loader.`, void 0)
1674
+ });
1675
+ return results;
1676
+ }
1677
+ async function resolveLoaderProjectRoot(filePath) {
1678
+ try {
1679
+ return await findProjectRoot(path$1.dirname(filePath)) ?? path$1.dirname(filePath);
1680
+ } catch {
1681
+ return path$1.dirname(filePath);
1682
+ }
1683
+ }
1684
+ function parseLoaderOutput(output, discoveredWorkflow) {
1685
+ try {
1686
+ const sentinelIndex = output.lastIndexOf(LOADER_SENTINEL);
1687
+ if (sentinelIndex === -1) throw new WorkflowLoaderProtocolError("stdout", "Workflow loader did not emit the expected structured response sentinel.", output);
1688
+ const envelope = output.slice(sentinelIndex + 31).trim();
1689
+ const parsedJson = JSON.parse(envelope);
1690
+ if (parsedJson.isAgent === true) {
1691
+ const agentResult = LoaderAgentSuccessSchema.parse(parsedJson);
1692
+ return {
1693
+ manifest: agentResult.manifest,
1694
+ toolEnrichments: agentResult.toolEnrichments,
1695
+ timing: agentResult.timing
1696
+ };
1697
+ }
1698
+ if (parsedJson.isTask === true) {
1699
+ const taskResult = LoaderTaskSuccessSchema.parse(parsedJson);
1700
+ return {
1701
+ manifest: taskResult.manifest,
1702
+ triggers: taskResult.triggers.map((t) => ({
1703
+ ...t,
1704
+ sourceFilePath: t.sourceFilePath || discoveredWorkflow.resolvedFilePath
1705
+ })),
1706
+ timing: taskResult.timing
1707
+ };
1708
+ }
1709
+ const parsedResult = LoaderResultSchema.parse(parsedJson);
1710
+ if (parsedResult.status === "error") throw new WorkflowMetadataLoadError(parsedResult.phase, parsedResult.error);
1711
+ const workflowResult = parsedResult;
1712
+ return {
1713
+ manifest: normalizeWorkflowManifest(workflowResult.manifest, discoveredWorkflow),
1714
+ flowGraph: workflowResult.flowGraph,
1715
+ triggers: workflowResult.triggers.map((t) => ({
1716
+ ...t,
1717
+ sourceFilePath: t.sourceFilePath || discoveredWorkflow.resolvedFilePath
1718
+ })),
1719
+ credentialsByCallsiteFingerprint: workflowResult.credentialsByCallsiteFingerprint,
1720
+ credentialAttributionWarnings: workflowResult.credentialAttributionWarnings,
1721
+ timing: workflowResult.timing
1722
+ };
1723
+ } catch (error) {
1724
+ if (error instanceof WorkflowMetadataLoadError || error instanceof WorkflowLoaderProtocolError) throw error;
1725
+ throw new WorkflowLoaderProtocolError("stdout", `Invalid workflow loader response: ${error instanceof Error ? error.message : String(error)}`, output);
1726
+ }
1727
+ }
1728
+ function normalizeWorkflowManifest(manifest, discoveredWorkflow) {
1729
+ const parsedManifest = WorkflowCoreManifestSchema.parse(manifest);
1730
+ return WorkflowCoreManifestSchema.parse({
1731
+ ...parsedManifest,
1732
+ schemaVersion: WORKFLOW_MANIFEST_SCHEMA_VERSION,
1733
+ exportName: parsedManifest.exportName ?? discoveredWorkflow.exportName,
1734
+ buildInfo: parsedManifest.buildInfo ?? {
1735
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1736
+ builderVersion: "workflow-builder",
1737
+ generatedBy: "local-build",
1738
+ nodeVersion: process.version,
1739
+ platform: process.platform,
1740
+ sourceChecksumAlgorithm: "sha256",
1741
+ sourceChecksum: "0".repeat(64),
1742
+ generatedFiles: []
1743
+ }
1744
+ });
1745
+ }
1746
+ //#endregion
1747
+ export { createDiscoveryFailure as a, createNamespaceStarExportDiscoveryError as c, isWorkflowLoadResult as d, loadWorkflowMetadataBatch as f, createAmbiguousExportDiscoveryError as i, isAgentLoadResult as l, WorkflowLoaderProtocolError as n, createDuplicateWorkflowIdFailure as o, WorkflowMetadataLoadError as r, createLoaderProtocolFailure as s, DuplicateWorkflowIdError as t, isTaskLoadResult as u };