@workflow/cli 4.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +3 -0
  3. package/bin/run.js +5 -0
  4. package/dist/base.d.ts +8 -0
  5. package/dist/base.d.ts.map +1 -0
  6. package/dist/base.js +14 -0
  7. package/dist/base.js.map +1 -0
  8. package/dist/commands/build.d.ts +14 -0
  9. package/dist/commands/build.d.ts.map +1 -0
  10. package/dist/commands/build.js +78 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/cancel.d.ts +25 -0
  13. package/dist/commands/cancel.d.ts.map +1 -0
  14. package/dist/commands/cancel.js +30 -0
  15. package/dist/commands/cancel.js.map +1 -0
  16. package/dist/commands/dev.d.ts +11 -0
  17. package/dist/commands/dev.d.ts.map +1 -0
  18. package/dist/commands/dev.js +34 -0
  19. package/dist/commands/dev.js.map +1 -0
  20. package/dist/commands/init.d.ts +11 -0
  21. package/dist/commands/init.d.ts.map +1 -0
  22. package/dist/commands/init.js +42 -0
  23. package/dist/commands/init.js.map +1 -0
  24. package/dist/commands/inspect.d.ts +31 -0
  25. package/dist/commands/inspect.d.ts.map +1 -0
  26. package/dist/commands/inspect.js +209 -0
  27. package/dist/commands/inspect.js.map +1 -0
  28. package/dist/commands/start.d.ts +26 -0
  29. package/dist/commands/start.d.ts.map +1 -0
  30. package/dist/commands/start.js +43 -0
  31. package/dist/commands/start.js.map +1 -0
  32. package/dist/commands/validate.d.ts +12 -0
  33. package/dist/commands/validate.d.ts.map +1 -0
  34. package/dist/commands/validate.js +45 -0
  35. package/dist/commands/validate.js.map +1 -0
  36. package/dist/lib/builders/apply-swc-transform.d.ts +24 -0
  37. package/dist/lib/builders/apply-swc-transform.d.ts.map +1 -0
  38. package/dist/lib/builders/apply-swc-transform.js +38 -0
  39. package/dist/lib/builders/apply-swc-transform.js.map +1 -0
  40. package/dist/lib/builders/base-builder.d.ts +45 -0
  41. package/dist/lib/builders/base-builder.d.ts.map +1 -0
  42. package/dist/lib/builders/base-builder.js +466 -0
  43. package/dist/lib/builders/base-builder.js.map +1 -0
  44. package/dist/lib/builders/discover-entries-esbuild-plugin.d.ts +11 -0
  45. package/dist/lib/builders/discover-entries-esbuild-plugin.d.ts.map +1 -0
  46. package/dist/lib/builders/discover-entries-esbuild-plugin.js +84 -0
  47. package/dist/lib/builders/discover-entries-esbuild-plugin.js.map +1 -0
  48. package/dist/lib/builders/next-build.d.ts +11 -0
  49. package/dist/lib/builders/next-build.d.ts.map +1 -0
  50. package/dist/lib/builders/next-build.js +331 -0
  51. package/dist/lib/builders/next-build.js.map +1 -0
  52. package/dist/lib/builders/node-module-esbuild-plugin.d.ts +3 -0
  53. package/dist/lib/builders/node-module-esbuild-plugin.d.ts.map +1 -0
  54. package/dist/lib/builders/node-module-esbuild-plugin.js +24 -0
  55. package/dist/lib/builders/node-module-esbuild-plugin.js.map +1 -0
  56. package/dist/lib/builders/node-module-esbuild-plugin.test.d.ts +2 -0
  57. package/dist/lib/builders/node-module-esbuild-plugin.test.d.ts.map +1 -0
  58. package/dist/lib/builders/node-module-esbuild-plugin.test.js +128 -0
  59. package/dist/lib/builders/node-module-esbuild-plugin.test.js.map +1 -0
  60. package/dist/lib/builders/swc-esbuild-plugin.d.ts +12 -0
  61. package/dist/lib/builders/swc-esbuild-plugin.d.ts.map +1 -0
  62. package/dist/lib/builders/swc-esbuild-plugin.js +134 -0
  63. package/dist/lib/builders/swc-esbuild-plugin.js.map +1 -0
  64. package/dist/lib/builders/vercel-build-output-api.d.ts +9 -0
  65. package/dist/lib/builders/vercel-build-output-api.d.ts.map +1 -0
  66. package/dist/lib/builders/vercel-build-output-api.js +138 -0
  67. package/dist/lib/builders/vercel-build-output-api.js.map +1 -0
  68. package/dist/lib/builders/vercel-static.d.ts +7 -0
  69. package/dist/lib/builders/vercel-static.d.ts.map +1 -0
  70. package/dist/lib/builders/vercel-static.js +42 -0
  71. package/dist/lib/builders/vercel-static.js.map +1 -0
  72. package/dist/lib/builders/webhook-route.test.d.ts +2 -0
  73. package/dist/lib/builders/webhook-route.test.d.ts.map +1 -0
  74. package/dist/lib/builders/webhook-route.test.js +199 -0
  75. package/dist/lib/builders/webhook-route.test.js.map +1 -0
  76. package/dist/lib/config/log.d.ts +42 -0
  77. package/dist/lib/config/log.d.ts.map +1 -0
  78. package/dist/lib/config/log.js +95 -0
  79. package/dist/lib/config/log.js.map +1 -0
  80. package/dist/lib/config/types.d.ts +28 -0
  81. package/dist/lib/config/types.d.ts.map +1 -0
  82. package/dist/lib/config/types.js +9 -0
  83. package/dist/lib/config/types.js.map +1 -0
  84. package/dist/lib/config/workflow-config.d.ts +6 -0
  85. package/dist/lib/config/workflow-config.d.ts.map +1 -0
  86. package/dist/lib/config/workflow-config.js +16 -0
  87. package/dist/lib/config/workflow-config.js.map +1 -0
  88. package/dist/lib/inspect/auth.d.ts +20 -0
  89. package/dist/lib/inspect/auth.d.ts.map +1 -0
  90. package/dist/lib/inspect/auth.js +54 -0
  91. package/dist/lib/inspect/auth.js.map +1 -0
  92. package/dist/lib/inspect/env.d.ts +25 -0
  93. package/dist/lib/inspect/env.d.ts.map +1 -0
  94. package/dist/lib/inspect/env.js +158 -0
  95. package/dist/lib/inspect/env.js.map +1 -0
  96. package/dist/lib/inspect/flags.d.ts +14 -0
  97. package/dist/lib/inspect/flags.d.ts.map +1 -0
  98. package/dist/lib/inspect/flags.js +110 -0
  99. package/dist/lib/inspect/flags.js.map +1 -0
  100. package/dist/lib/inspect/output.d.ts +31 -0
  101. package/dist/lib/inspect/output.d.ts.map +1 -0
  102. package/dist/lib/inspect/output.js +899 -0
  103. package/dist/lib/inspect/output.js.map +1 -0
  104. package/dist/lib/inspect/pagination.d.ts +55 -0
  105. package/dist/lib/inspect/pagination.d.ts.map +1 -0
  106. package/dist/lib/inspect/pagination.js +193 -0
  107. package/dist/lib/inspect/pagination.js.map +1 -0
  108. package/dist/lib/inspect/run.d.ts +9 -0
  109. package/dist/lib/inspect/run.d.ts.map +1 -0
  110. package/dist/lib/inspect/run.js +30 -0
  111. package/dist/lib/inspect/run.js.map +1 -0
  112. package/dist/lib/inspect/setup.d.ts +10 -0
  113. package/dist/lib/inspect/setup.d.ts.map +1 -0
  114. package/dist/lib/inspect/setup.js +33 -0
  115. package/dist/lib/inspect/setup.js.map +1 -0
  116. package/dist/lib/inspect/stream.d.ts +7 -0
  117. package/dist/lib/inspect/stream.d.ts.map +1 -0
  118. package/dist/lib/inspect/stream.js +50 -0
  119. package/dist/lib/inspect/stream.js.map +1 -0
  120. package/dist/lib/inspect/terminal-utils.d.ts +36 -0
  121. package/dist/lib/inspect/terminal-utils.d.ts.map +1 -0
  122. package/dist/lib/inspect/terminal-utils.js +109 -0
  123. package/dist/lib/inspect/terminal-utils.js.map +1 -0
  124. package/dist/lib/inspect/vercel-api.d.ts +12 -0
  125. package/dist/lib/inspect/vercel-api.d.ts.map +1 -0
  126. package/dist/lib/inspect/vercel-api.js +89 -0
  127. package/dist/lib/inspect/vercel-api.js.map +1 -0
  128. package/dist/lib/inspect/vercel-link.d.ts +98 -0
  129. package/dist/lib/inspect/vercel-link.d.ts.map +1 -0
  130. package/dist/lib/inspect/vercel-link.js +268 -0
  131. package/dist/lib/inspect/vercel-link.js.map +1 -0
  132. package/dist/lib/inspect/web.d.ts +9 -0
  133. package/dist/lib/inspect/web.d.ts.map +1 -0
  134. package/dist/lib/inspect/web.js +305 -0
  135. package/dist/lib/inspect/web.js.map +1 -0
  136. package/dist/lib/runtime.d.ts +2 -0
  137. package/dist/lib/runtime.d.ts.map +1 -0
  138. package/dist/lib/runtime.js +5 -0
  139. package/dist/lib/runtime.js.map +1 -0
  140. package/package.json +67 -0
@@ -0,0 +1,899 @@
1
+ import { hydrateResourceIO } from '@workflow/core/observability';
2
+ import { parseStepName, parseWorkflowName } from '@workflow/core/parse-name';
3
+ import { getRun } from '@workflow/core/runtime';
4
+ import chalk from 'chalk';
5
+ import { formatDistance } from 'date-fns';
6
+ import Table from 'easy-table';
7
+ import { logger } from '../config/log.js';
8
+ import { setupListPagination } from './pagination.js';
9
+ import { streamToConsole } from './stream.js';
10
+ import { formatISODate, formatStatus as formatStatusAbbrev, getDisplaySettings, getTerminalWidth, isCI, } from './terminal-utils.js';
11
+ const DEFAULT_PAGE_SIZE = 20;
12
+ let TABLE_TRUNCATE_IO_LENGTH = 15; // Will be adjusted based on terminal width
13
+ const WORKFLOW_RUN_IO_PROPS = ['input', 'output'];
14
+ const STEP_IO_PROPS = ['input', 'output'];
15
+ const WORKFLOW_RUN_LISTED_PROPS = [
16
+ 'runId',
17
+ 'workflowName',
18
+ 'status',
19
+ 'startedAt',
20
+ 'completedAt',
21
+ ...WORKFLOW_RUN_IO_PROPS,
22
+ ];
23
+ const STEP_LISTED_PROPS = [
24
+ 'runId',
25
+ 'stepId',
26
+ 'stepName',
27
+ 'status',
28
+ 'startedAt',
29
+ 'completedAt',
30
+ ...STEP_IO_PROPS,
31
+ ];
32
+ const EVENT_IO_PROPS = ['eventData'];
33
+ const EVENT_LISTED_PROPS = [
34
+ 'eventId',
35
+ 'eventType',
36
+ 'correlationId',
37
+ 'createdAt',
38
+ ...EVENT_IO_PROPS,
39
+ ];
40
+ // const HOOK_DATA_PROPS: (keyof Hook | 'hasResponse')[] = ['hasResponse'];
41
+ const HOOK_LISTED_PROPS = [
42
+ 'runId',
43
+ 'hookId',
44
+ 'ownerId',
45
+ 'createdAt',
46
+ // ...HOOK_DATA_PROPS,
47
+ ];
48
+ const STATUS_COLORS = {
49
+ running: chalk.blue,
50
+ completed: chalk.green,
51
+ failed: chalk.red,
52
+ cancelled: chalk.strikethrough.yellow,
53
+ pending: chalk.blue,
54
+ paused: chalk.yellow,
55
+ };
56
+ const isStreamId = (value) => {
57
+ return typeof value === 'string' && value.startsWith('strm_');
58
+ };
59
+ const showStatusLegend = () => {
60
+ logger.log('\nStatus Legend:');
61
+ const statuses = [
62
+ 'running',
63
+ 'completed',
64
+ 'failed',
65
+ 'cancelled',
66
+ 'pending',
67
+ 'paused',
68
+ ];
69
+ const legendItems = statuses.map((status) => {
70
+ const colorFunc = STATUS_COLORS[status];
71
+ const abbrev = formatStatusAbbrev(status, true);
72
+ return `${colorFunc(abbrev)} = ${status}`;
73
+ });
74
+ logger.log(` ${legendItems.join(' ')}`);
75
+ logger.log('');
76
+ };
77
+ const isSleepStep = (stepName) => {
78
+ return stepName.includes('-sleep');
79
+ };
80
+ const checkAndHandleVercelAccessError = (error, backend) => {
81
+ if (backend === 'vercel' && error && typeof error === 'object') {
82
+ const err = error;
83
+ if (err.status === 403) {
84
+ logger.error('Your current vercel account does not have access to this workflow run. Please use `vercel login` to login, or use `vercel switch` to ensure you can access the correct team.');
85
+ return true;
86
+ }
87
+ }
88
+ return false;
89
+ };
90
+ const extractErrorMessage = (err) => {
91
+ if (err.message && typeof err.message === 'string') {
92
+ return err.message;
93
+ }
94
+ if (err.body && typeof err.body === 'object') {
95
+ const body = err.body;
96
+ if (body.message && typeof body.message === 'string') {
97
+ return body.message;
98
+ }
99
+ if (body.error && typeof body.error === 'string') {
100
+ return body.error;
101
+ }
102
+ }
103
+ return undefined;
104
+ };
105
+ const handleApiError = (error, backend) => {
106
+ // First check for Vercel access errors
107
+ if (checkAndHandleVercelAccessError(error, backend)) {
108
+ return true;
109
+ }
110
+ // Handle other HTTP errors
111
+ if (error && typeof error === 'object') {
112
+ const err = error;
113
+ // Handle 400 Bad Request
114
+ if (err.status === 400) {
115
+ logger.error('Bad Request: The request was invalid.');
116
+ const message = extractErrorMessage(err);
117
+ if (message) {
118
+ logger.error(`Details: ${message}`);
119
+ }
120
+ return true;
121
+ }
122
+ // Handle other HTTP errors (404, 500, etc.)
123
+ if (typeof err.status === 'number' && err.status >= 400) {
124
+ logger.error(`HTTP Error ${err.status}: ${getStatusText(err.status)}`);
125
+ const message = extractErrorMessage(err);
126
+ if (message) {
127
+ logger.error(`Details: ${message}`);
128
+ }
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ };
134
+ const getStatusText = (status) => {
135
+ const statusTexts = {
136
+ 400: 'Bad Request',
137
+ 401: 'Unauthorized',
138
+ 403: 'Forbidden',
139
+ 404: 'Not Found',
140
+ 500: 'Internal Server Error',
141
+ 502: 'Bad Gateway',
142
+ 503: 'Service Unavailable',
143
+ };
144
+ return statusTexts[status] || 'Unknown Error';
145
+ };
146
+ const truncateNameIfNeeded = (name, maxLength) => {
147
+ return maxLength < name.length ? `${name.substring(0, maxLength)}...` : name;
148
+ };
149
+ const formatNameField = (nameNonUnique, truncateLength, isSleep) => {
150
+ const truncatedName = truncateNameIfNeeded(nameNonUnique, truncateLength);
151
+ if (isSleep) {
152
+ return chalk.yellowBright(truncatedName);
153
+ }
154
+ return chalk.blue.blueBright(truncatedName);
155
+ };
156
+ const formatIdField = (prop, value, shouldTruncateIds) => {
157
+ const valueStr = String(value);
158
+ const idStr = shouldTruncateIds ? truncateIdToLastChars(valueStr) : valueStr;
159
+ if (prop === 'streamId') {
160
+ return chalk.green(idStr);
161
+ }
162
+ return idStr;
163
+ };
164
+ const formatTableValue = (prop, value, opts = {}, displaySettings) => {
165
+ const namesTruncate = displaySettings?.namesTruncateLength ?? 40;
166
+ const shouldTruncateIds = displaySettings?.truncateIdsToLastChars ?? false;
167
+ // Handle IDs with potential truncation
168
+ if ([
169
+ 'streamId',
170
+ 'runId',
171
+ 'stepId',
172
+ 'hookId',
173
+ 'eventId',
174
+ 'correlationId',
175
+ 'ownerId',
176
+ ].includes(prop)) {
177
+ return formatIdField(prop, value, shouldTruncateIds);
178
+ }
179
+ // Handle names with truncation
180
+ if (prop === 'stepName') {
181
+ const nameNonUnique = parseStepName(String(value))?.shortName || '?';
182
+ return formatNameField(nameNonUnique, namesTruncate, isSleepStep(String(value)));
183
+ }
184
+ if (prop === 'workflowName') {
185
+ const nameNonUnique = parseWorkflowName(String(value))?.shortName || '?';
186
+ const truncatedName = truncateNameIfNeeded(nameNonUnique, namesTruncate);
187
+ return chalk.blue.blueBright(truncatedName);
188
+ }
189
+ if (prop === 'output' || prop === 'input') {
190
+ return inlineFormatIO(value);
191
+ }
192
+ if (prop === 'status') {
193
+ const status = value;
194
+ const colorFunc = STATUS_COLORS[status];
195
+ const formattedStatus = displaySettings?.abbreviateStatus
196
+ ? formatStatusAbbrev(status, true)
197
+ : status;
198
+ return colorFunc(formattedStatus);
199
+ }
200
+ if (prop === 'eventData') {
201
+ return truncateString(JSON.stringify(value));
202
+ }
203
+ if (prop === 'hasResponse') {
204
+ return value ? chalk.green('true') : chalk.gray('false');
205
+ }
206
+ if (value instanceof Date) {
207
+ return formatTableTimestamp(value, opts, displaySettings);
208
+ }
209
+ return value;
210
+ };
211
+ const getVisibleProps = (props, displaySettings) => {
212
+ if (displaySettings.hideCompletedAt) {
213
+ return props.filter((prop) => prop !== 'completedAt');
214
+ }
215
+ return props;
216
+ };
217
+ const showTable = (data, props, opts = {}) => {
218
+ // Get display settings based on terminal width
219
+ const terminalWidth = getTerminalWidth();
220
+ const displaySettings = getDisplaySettings(terminalWidth, opts.withData || false, props.includes('runName'));
221
+ // Filter out completedAt column if needed
222
+ const visibleProps = getVisibleProps(props, displaySettings);
223
+ // Update truncate length for IO fields
224
+ const originalTruncateLength = TABLE_TRUNCATE_IO_LENGTH;
225
+ TABLE_TRUNCATE_IO_LENGTH = displaySettings.dataFieldWidth;
226
+ // Show status legend if using abbreviated status
227
+ if (displaySettings.abbreviateStatus && visibleProps.includes('status')) {
228
+ showStatusLegend();
229
+ }
230
+ // Create header mapping for abbreviated status
231
+ const headerMap = {};
232
+ if (displaySettings.abbreviateStatus && visibleProps.includes('status')) {
233
+ headerMap['status'] = 'S';
234
+ }
235
+ // Add a blank line before any table
236
+ const table = new Table();
237
+ if (data && data.length === 0) {
238
+ logger.warn('No data found for this query and resource.\n');
239
+ for (const prop of visibleProps) {
240
+ const header = headerMap[prop] || prop;
241
+ table.cell(header, 'N/A');
242
+ }
243
+ table.newRow();
244
+ // Restore original truncate length
245
+ TABLE_TRUNCATE_IO_LENGTH = originalTruncateLength;
246
+ return table.toString();
247
+ }
248
+ else if (!data) {
249
+ logger.warn('Expecting an array of data, but got null.\n');
250
+ }
251
+ logger.log('');
252
+ for (const item of data) {
253
+ for (const prop of visibleProps) {
254
+ const header = headerMap[prop] || prop;
255
+ const value = item[prop];
256
+ table.cell(header, formatTableValue(prop, value, opts, displaySettings));
257
+ }
258
+ table.newRow();
259
+ }
260
+ // Restore original truncate length
261
+ TABLE_TRUNCATE_IO_LENGTH = originalTruncateLength;
262
+ return table.toString();
263
+ };
264
+ const showJson = (data) => {
265
+ const json = JSON.stringify(data, null, 2);
266
+ process.stdout.write(`${json}\n`);
267
+ };
268
+ const getCursorHint = ({ hasMore, cursor, }) => {
269
+ // Only show cursor hint in non-interactive mode (e.g., CI or when piped)
270
+ if (!isCI() && process.stdout.isTTY) {
271
+ return undefined;
272
+ }
273
+ if (hasMore && cursor) {
274
+ return `More results available. Append\n--cursor "${cursor}"\nto this command to fetch the next page.`;
275
+ }
276
+ };
277
+ /**
278
+ * In tables, we want to show a shorter timestamp, YYYY-MM-DD HH:MM:SS
279
+ */
280
+ const formatTableTimestamp = (value, opts = {}, displaySettings) => {
281
+ // Format ISO time without T and Z
282
+ const isoTime = formatISODate(value);
283
+ // Show relative time only if:
284
+ // - Not in CI mode
285
+ // - Display settings allow it (based on terminal width)
286
+ // - withData is disabled (more space available)
287
+ // - Not in JSON mode
288
+ const shouldShowRelative = !isCI() &&
289
+ displaySettings?.showRelativeDates !== false &&
290
+ !opts.withData &&
291
+ !opts.json;
292
+ if (shouldShowRelative) {
293
+ const relative = formatDistance(value, new Date(), { addSuffix: true });
294
+ return `${isoTime} (${relative})`;
295
+ }
296
+ return isoTime;
297
+ };
298
+ const truncateString = (str, maxLength = TABLE_TRUNCATE_IO_LENGTH) => {
299
+ return str && str.length > maxLength
300
+ ? `${str.substring(0, maxLength)}...`
301
+ : str;
302
+ };
303
+ const truncateIdToLastChars = (id, chars = 4) => {
304
+ if (!id || id.length <= chars)
305
+ return id;
306
+ return `...${id.substring(id.length - chars)}`;
307
+ };
308
+ const showInspectInfoBox = (resource) => {
309
+ logger.info(`To view details for a ${resource}, use \`wf i ${resource}\` <id>`);
310
+ logger.info(`To view the content of any stream, use \`wf i stream <stream-id>\``);
311
+ };
312
+ /**
313
+ * Takes hydrated step/workflow input/output and serializes it for inline display.
314
+ */
315
+ const inlineFormatIO = (io, topLevel = true) => {
316
+ const type = typeof io;
317
+ let value = '';
318
+ if (io === undefined) {
319
+ value = '<empty>';
320
+ }
321
+ else if (io === null) {
322
+ value = '<null>';
323
+ }
324
+ else if (io && Array.isArray(io)) {
325
+ if (io.length === 0) {
326
+ value = '<empty>';
327
+ }
328
+ else {
329
+ const stringified = io
330
+ .map((item) => inlineFormatIO(item, false))
331
+ .join(',');
332
+ if (stringified.length > TABLE_TRUNCATE_IO_LENGTH && topLevel) {
333
+ value = chalk.yellow(`${io.length} args`);
334
+ }
335
+ else {
336
+ value = stringified;
337
+ }
338
+ }
339
+ }
340
+ else if (type === 'object') {
341
+ if (isStreamId(io)) {
342
+ value = io.toString();
343
+ }
344
+ else if (io instanceof Date) {
345
+ value = io.toISOString();
346
+ }
347
+ else {
348
+ value = truncateString(JSON.stringify(io));
349
+ }
350
+ }
351
+ else if (['string', 'number', 'boolean'].includes(type)) {
352
+ if (type === 'string' && io.includes('strm_')) {
353
+ value = io;
354
+ }
355
+ value = truncateString(String(io));
356
+ }
357
+ else {
358
+ value = `<${type}>`;
359
+ }
360
+ return value;
361
+ };
362
+ export const listRuns = async (world, opts = {}) => {
363
+ if (opts.stepId || opts.runId) {
364
+ logger.warn('Filtering by step-id or run-id is not supported in list calls, ignoring filter.');
365
+ }
366
+ const resolveData = opts.withData ? 'all' : 'none';
367
+ // Determine which props to show based on withData flag
368
+ const props = opts.withData
369
+ ? WORKFLOW_RUN_LISTED_PROPS
370
+ : WORKFLOW_RUN_LISTED_PROPS.filter((prop) => !WORKFLOW_RUN_IO_PROPS.includes(prop));
371
+ // For JSON output, just fetch once and return
372
+ if (opts.json) {
373
+ try {
374
+ const runs = await world.runs.list({
375
+ workflowName: opts.workflowName,
376
+ pagination: {
377
+ sortOrder: opts.sort || 'desc',
378
+ cursor: opts.cursor,
379
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
380
+ },
381
+ resolveData,
382
+ });
383
+ const runsWithHydratedIO = runs.data.map(hydrateResourceIO);
384
+ showJson({ ...runs, data: runsWithHydratedIO });
385
+ return;
386
+ }
387
+ catch (error) {
388
+ if (handleApiError(error, opts.backend)) {
389
+ process.exit(1);
390
+ }
391
+ throw error;
392
+ }
393
+ }
394
+ await setupListPagination({
395
+ initialCursor: opts.cursor,
396
+ fetchPage: async (cursor) => {
397
+ try {
398
+ const runs = await world.runs.list({
399
+ workflowName: opts.workflowName,
400
+ pagination: {
401
+ sortOrder: opts.sort || 'desc',
402
+ cursor,
403
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
404
+ },
405
+ resolveData,
406
+ });
407
+ return {
408
+ data: runs.data,
409
+ cursor: runs.cursor,
410
+ hasMore: runs.hasMore,
411
+ };
412
+ }
413
+ catch (error) {
414
+ if (handleApiError(error, opts.backend)) {
415
+ process.exit(1);
416
+ }
417
+ throw error;
418
+ }
419
+ },
420
+ displayPage: async (runs) => {
421
+ const runsWithHydratedIO = runs.map(hydrateResourceIO);
422
+ logger.log(showTable(runsWithHydratedIO, props, opts));
423
+ },
424
+ });
425
+ };
426
+ export const getRecentRun = async (world, opts = {}) => {
427
+ logger.warn(`No runId provided, fetching data for latest run instead.`);
428
+ try {
429
+ const runs = await world.runs.list({
430
+ pagination: { limit: 1, sortOrder: opts.sort || 'desc' },
431
+ resolveData: 'none', // Don't need data for just getting the ID
432
+ });
433
+ runs.data = runs.data.map(hydrateResourceIO);
434
+ return runs.data[0];
435
+ }
436
+ catch (error) {
437
+ if (handleApiError(error, opts.backend)) {
438
+ process.exit(1);
439
+ }
440
+ throw error;
441
+ }
442
+ };
443
+ export const showRun = async (world, runId, opts = {}) => {
444
+ if (opts.withData) {
445
+ logger.warn('`withData` flag is ignored when showing individual resources');
446
+ }
447
+ try {
448
+ const run = await world.runs.get(runId, { resolveData: 'all' });
449
+ const runWithHydratedIO = hydrateResourceIO(run);
450
+ if (opts.json) {
451
+ showJson(runWithHydratedIO);
452
+ return;
453
+ }
454
+ else {
455
+ logger.log(runWithHydratedIO);
456
+ }
457
+ }
458
+ catch (error) {
459
+ if (handleApiError(error, opts.backend)) {
460
+ process.exit(1);
461
+ }
462
+ throw error;
463
+ }
464
+ };
465
+ export const listSteps = async (world, opts = {
466
+ runId: undefined,
467
+ }) => {
468
+ if (opts.stepId) {
469
+ logger.warn('Filtering by step-id is not supported in list calls, ignoring filter.');
470
+ }
471
+ if (opts.workflowName) {
472
+ logger.warn('Filtering by workflow-name is not supported for steps, ignoring filter.');
473
+ }
474
+ const runId = opts.runId
475
+ ? opts.runId
476
+ : (await getRecentRun(world, opts))?.runId;
477
+ if (!runId) {
478
+ logger.error('No run found.');
479
+ return;
480
+ }
481
+ const resolveData = opts.withData ? 'all' : 'none';
482
+ // Determine which props to show based on withData flag
483
+ const props = opts.withData
484
+ ? STEP_LISTED_PROPS
485
+ : STEP_LISTED_PROPS.filter((prop) => !STEP_IO_PROPS.includes(prop));
486
+ // For JSON output, just fetch once and return
487
+ if (opts.json) {
488
+ logger.debug(`Fetching steps for run ${runId}`);
489
+ try {
490
+ const stepChunks = await world.steps.list({
491
+ runId,
492
+ pagination: {
493
+ sortOrder: opts.sort || 'desc',
494
+ cursor: opts.cursor,
495
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
496
+ },
497
+ resolveData,
498
+ });
499
+ showJson(stepChunks.data);
500
+ return;
501
+ }
502
+ catch (error) {
503
+ if (handleApiError(error, opts.backend)) {
504
+ process.exit(1);
505
+ }
506
+ throw error;
507
+ }
508
+ }
509
+ await setupListPagination({
510
+ initialCursor: opts.cursor,
511
+ fetchPage: async (cursor) => {
512
+ logger.debug(`Fetching steps for run ${runId}`);
513
+ try {
514
+ const stepChunks = await world.steps.list({
515
+ runId,
516
+ pagination: {
517
+ sortOrder: opts.sort || 'desc',
518
+ cursor,
519
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
520
+ },
521
+ resolveData,
522
+ });
523
+ return {
524
+ data: stepChunks.data,
525
+ cursor: stepChunks.cursor,
526
+ hasMore: stepChunks.hasMore,
527
+ };
528
+ }
529
+ catch (error) {
530
+ if (handleApiError(error, opts.backend)) {
531
+ process.exit(1);
532
+ }
533
+ throw error;
534
+ }
535
+ },
536
+ displayPage: async (steps) => {
537
+ const stepsWithHydratedIO = steps.map(hydrateResourceIO);
538
+ logger.log(showTable(stepsWithHydratedIO, props, opts));
539
+ showInspectInfoBox('step');
540
+ },
541
+ });
542
+ };
543
+ export const showStep = async (world, stepId, opts = {}) => {
544
+ if (opts.withData) {
545
+ logger.warn('`withData` flag is ignored when showing individual resources');
546
+ }
547
+ if (opts.stepId) {
548
+ logger.warn('Filtering by step-id is not supported in get calls, ignoring filter.');
549
+ }
550
+ try {
551
+ const step = await world.steps.get(opts.runId, stepId, {
552
+ resolveData: 'all',
553
+ });
554
+ const stepWithHydratedIO = hydrateResourceIO(step);
555
+ if (opts.json) {
556
+ showJson(stepWithHydratedIO);
557
+ return;
558
+ }
559
+ else {
560
+ logger.log(stepWithHydratedIO);
561
+ }
562
+ }
563
+ catch (error) {
564
+ if (handleApiError(error, opts.backend)) {
565
+ process.exit(1);
566
+ }
567
+ throw error;
568
+ }
569
+ };
570
+ export const showStream = async (_, streamId, opts = {}) => {
571
+ if (opts.runId || opts.stepId) {
572
+ logger.warn('Filtering by run-id or step-id is not supported in get calls, ignoring filter.');
573
+ }
574
+ const run = getRun(streamId);
575
+ const stream = run.readable;
576
+ logger.info('Streaming to stdout, press CTRL+C to abort.');
577
+ logger.info('Use --json to output the stream as newline-delimited JSON without info logs.\n');
578
+ await streamToConsole(stream, streamId, opts);
579
+ };
580
+ /**
581
+ * Listing streams only lists available stream IDs based on run/step passed,
582
+ * and doesn't read any data from the streams.
583
+ */
584
+ export const listStreams = async (world, opts = {}) => {
585
+ if (opts.withData) {
586
+ logger.warn('`withData` flag is ignored when listing streams');
587
+ }
588
+ if (opts.workflowName) {
589
+ logger.warn('Filtering by workflow-name is not supported for streams, ignoring filter.');
590
+ }
591
+ const steps = [];
592
+ const runs = [];
593
+ if (opts.stepId) {
594
+ try {
595
+ const step = await world.steps.get(undefined, opts.stepId, {
596
+ resolveData: 'all',
597
+ });
598
+ steps.push(step);
599
+ }
600
+ catch (error) {
601
+ if (handleApiError(error, opts.backend)) {
602
+ process.exit(1);
603
+ }
604
+ throw error;
605
+ }
606
+ }
607
+ else if (opts.runId) {
608
+ try {
609
+ const run = await world.runs.get(opts.runId, { resolveData: 'all' });
610
+ runs.push(run);
611
+ const runsSteps = await world.steps.list({
612
+ runId: opts.runId,
613
+ pagination: {
614
+ sortOrder: opts.sort || 'desc',
615
+ cursor: opts.cursor,
616
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
617
+ },
618
+ resolveData: 'all', // Need data to find stream IDs
619
+ });
620
+ runsSteps.data.forEach((step) => {
621
+ steps.push(step);
622
+ });
623
+ logger.info(getCursorHint(runsSteps));
624
+ }
625
+ catch (error) {
626
+ if (handleApiError(error, opts.backend)) {
627
+ process.exit(1);
628
+ }
629
+ throw error;
630
+ }
631
+ }
632
+ else {
633
+ logger.warn('No run-id or step-id provided. Listing streams for latest run instead.', 'Use --run=<run-id> or --step=<step-id> to filter streams by run or step.');
634
+ const run = await getRecentRun(world, opts);
635
+ if (!run) {
636
+ logger.warn('No runs found.');
637
+ return;
638
+ }
639
+ try {
640
+ const fullRun = await world.runs.get(run.runId, { resolveData: 'all' });
641
+ runs.push(fullRun);
642
+ const runsSteps = await world.steps.list({
643
+ runId: runs[0].runId,
644
+ pagination: {
645
+ sortOrder: opts.sort || 'desc',
646
+ cursor: opts.cursor,
647
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
648
+ },
649
+ resolveData: 'all', // Need data to find stream IDs
650
+ });
651
+ runsSteps.data.forEach((step) => {
652
+ steps.push(step);
653
+ });
654
+ logger.info(getCursorHint(runsSteps));
655
+ }
656
+ catch (error) {
657
+ if (handleApiError(error, opts.backend)) {
658
+ process.exit(1);
659
+ }
660
+ throw error;
661
+ }
662
+ }
663
+ const runIds = runs.map((item) => item.runId);
664
+ const stepIds = steps.map((item) => item.stepId);
665
+ logger.debug(`Found IO for runs/steps: ${runIds.concat(stepIds).join(', ')}`);
666
+ const runsWithHydratedIO = runs.map(hydrateResourceIO);
667
+ const stepsWithHydratedIO = steps.map(hydrateResourceIO);
668
+ const matchingStreams = [
669
+ ...runsWithHydratedIO,
670
+ ...stepsWithHydratedIO,
671
+ ].flatMap((item) => findAllStreamIdsForObjectWithIO({
672
+ input: item.input,
673
+ output: item.output,
674
+ runId: item.runId,
675
+ stepId: 'stepId' in item ? item.stepId : undefined,
676
+ }));
677
+ if (opts.json) {
678
+ showJson(matchingStreams);
679
+ return;
680
+ }
681
+ logger.log(showTable(matchingStreams, ['runId', 'stepId', 'streamId']));
682
+ };
683
+ const findAllStreamIdsForObjectWithIO = (obj) => {
684
+ const matchingStreams = [];
685
+ const inputStreams = getStreamIdsFromHydratedObject(obj.input);
686
+ for (const streamId of inputStreams) {
687
+ matchingStreams.push({
688
+ runId: obj.runId,
689
+ stepId: obj.stepId || '/',
690
+ streamId,
691
+ });
692
+ }
693
+ const outputStreams = getStreamIdsFromHydratedObject(obj.output);
694
+ for (const streamId of outputStreams) {
695
+ matchingStreams.push({
696
+ runId: obj.runId,
697
+ stepId: obj.stepId || '/',
698
+ streamId,
699
+ });
700
+ }
701
+ return matchingStreams;
702
+ };
703
+ const getStreamIdsFromHydratedObject = (io) => {
704
+ const streamIds = [];
705
+ const traverse = (obj) => {
706
+ if (!obj || typeof obj !== 'object')
707
+ return;
708
+ if (isStreamId(obj)) {
709
+ streamIds.push(obj);
710
+ }
711
+ else if (Array.isArray(obj)) {
712
+ obj.forEach(traverse);
713
+ }
714
+ else {
715
+ Object.values(obj).forEach(traverse);
716
+ }
717
+ };
718
+ traverse(io);
719
+ return streamIds;
720
+ };
721
+ export const listEvents = async (world, opts = {}) => {
722
+ if (opts.workflowName) {
723
+ logger.warn('Filtering by workflow-name is not supported for events, ignoring filter.');
724
+ }
725
+ let filterId = opts.hookId || opts.stepId || opts.runId;
726
+ if (!filterId) {
727
+ filterId = (await getRecentRun(world, opts))?.runId;
728
+ if (!filterId) {
729
+ logger.error('No run found.');
730
+ return;
731
+ }
732
+ }
733
+ const isCorrelationId = Boolean(opts.hookId || opts.stepId);
734
+ const params = {
735
+ pagination: {
736
+ sortOrder: opts.sort || 'desc',
737
+ cursor: opts.cursor,
738
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
739
+ },
740
+ resolveData: opts.withData ? 'all' : 'none',
741
+ };
742
+ const listCall = isCorrelationId
743
+ ? (correlationId, pagination) => world.events.listByCorrelationId({
744
+ ...params,
745
+ correlationId,
746
+ pagination: { ...params.pagination, ...pagination },
747
+ })
748
+ : (runId, pagination) => world.events.list({
749
+ ...params,
750
+ runId,
751
+ pagination: { ...params.pagination, ...pagination },
752
+ });
753
+ // Determine which props to show based on withData flag
754
+ const props = opts.withData
755
+ ? EVENT_LISTED_PROPS
756
+ : EVENT_LISTED_PROPS.filter((prop) => !EVENT_IO_PROPS.includes(prop));
757
+ // For JSON output, just fetch once and return
758
+ if (opts.json) {
759
+ logger.debug(`Fetching events for run ${filterId}`);
760
+ try {
761
+ const events = await listCall(filterId, {});
762
+ showJson(events.data);
763
+ return;
764
+ }
765
+ catch (error) {
766
+ if (handleApiError(error, opts.backend)) {
767
+ process.exit(1);
768
+ }
769
+ throw error;
770
+ }
771
+ }
772
+ await setupListPagination({
773
+ initialCursor: opts.cursor,
774
+ fetchPage: async (cursor) => {
775
+ logger.debug(`Fetching events for run ${filterId}`);
776
+ try {
777
+ const events = await listCall(filterId, { cursor });
778
+ return {
779
+ data: events.data,
780
+ cursor: events.cursor,
781
+ hasMore: events.hasMore,
782
+ };
783
+ }
784
+ catch (error) {
785
+ if (handleApiError(error, opts.backend)) {
786
+ process.exit(1);
787
+ }
788
+ throw error;
789
+ }
790
+ },
791
+ displayPage: async (events) => {
792
+ logger.log(showTable(events, props, opts));
793
+ showInspectInfoBox('event');
794
+ },
795
+ });
796
+ };
797
+ export const listHooks = async (world, opts = {}) => {
798
+ if (opts.workflowName) {
799
+ logger.warn('Filtering by workflow-name is not supported for hooks, ignoring filter.');
800
+ }
801
+ if (opts.stepId) {
802
+ logger.warn('Filtering by step-id is not supported for hooks, ignoring filter.');
803
+ }
804
+ const runId = opts.runId;
805
+ const resolveData = opts.withData ? 'all' : 'none';
806
+ // For JSON output, just fetch once and return
807
+ if (opts.json) {
808
+ if (!runId) {
809
+ logger.debug('Fetching all hooks');
810
+ }
811
+ else {
812
+ logger.debug(`Fetching hooks for run ${runId}`);
813
+ }
814
+ try {
815
+ const hooks = await world.hooks.list({
816
+ runId,
817
+ pagination: {
818
+ sortOrder: opts.sort || 'desc',
819
+ cursor: opts.cursor,
820
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
821
+ },
822
+ resolveData,
823
+ });
824
+ const hydratedHooks = hooks.data.map(hydrateResourceIO);
825
+ showJson({ ...hooks, data: hydratedHooks });
826
+ return;
827
+ }
828
+ catch (error) {
829
+ if (handleApiError(error, opts.backend)) {
830
+ process.exit(1);
831
+ }
832
+ throw error;
833
+ }
834
+ }
835
+ // Setup pagination with new mechanism
836
+ await setupListPagination({
837
+ initialCursor: opts.cursor,
838
+ fetchPage: async (cursor) => {
839
+ if (!runId) {
840
+ logger.debug('Fetching all hooks');
841
+ }
842
+ else {
843
+ logger.debug(`Fetching hooks for run ${runId}`);
844
+ }
845
+ try {
846
+ const hooks = await world.hooks.list({
847
+ runId,
848
+ pagination: {
849
+ sortOrder: opts.sort || 'desc',
850
+ cursor,
851
+ limit: opts.limit || DEFAULT_PAGE_SIZE,
852
+ },
853
+ resolveData,
854
+ });
855
+ return {
856
+ data: hooks.data,
857
+ cursor: hooks.cursor,
858
+ hasMore: hooks.hasMore,
859
+ };
860
+ }
861
+ catch (error) {
862
+ if (handleApiError(error, opts.backend)) {
863
+ process.exit(1);
864
+ }
865
+ throw error;
866
+ }
867
+ },
868
+ displayPage: async (hooks) => {
869
+ const hydratedHooks = hooks.map(hydrateResourceIO);
870
+ logger.log(showTable(hydratedHooks, HOOK_LISTED_PROPS, opts));
871
+ showInspectInfoBox('hook');
872
+ },
873
+ });
874
+ };
875
+ export const showHook = async (world, hookId, opts = {}) => {
876
+ if (opts.withData) {
877
+ logger.warn('`withData` flag is ignored when showing individual resources');
878
+ }
879
+ try {
880
+ const hook = await world.hooks.get(hookId, {
881
+ resolveData: 'all',
882
+ });
883
+ const hydratedHook = hydrateResourceIO(hook);
884
+ if (opts.json) {
885
+ showJson(hydratedHook);
886
+ return;
887
+ }
888
+ else {
889
+ logger.log(hydratedHook);
890
+ }
891
+ }
892
+ catch (error) {
893
+ if (handleApiError(error, opts.backend)) {
894
+ process.exit(1);
895
+ }
896
+ throw error;
897
+ }
898
+ };
899
+ //# sourceMappingURL=output.js.map