@outputai/cli 0.1.3 → 0.1.4-dev.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 (278) hide show
  1. package/dist/api/generated/api.d.ts +820 -0
  2. package/dist/api/generated/api.js +226 -0
  3. package/dist/api/http_client.d.ts +27 -0
  4. package/dist/api/http_client.js +71 -0
  5. package/dist/api/orval_post_process.d.ts +11 -0
  6. package/dist/api/orval_post_process.js +46 -0
  7. package/dist/api/parser.d.ts +17 -0
  8. package/dist/api/parser.js +68 -0
  9. package/dist/assets/config/costs.yml +309 -0
  10. package/dist/assets/docker/docker-compose-dev.yml +146 -0
  11. package/dist/commands/credentials/edit.d.ts +10 -0
  12. package/dist/commands/credentials/edit.js +67 -0
  13. package/dist/commands/credentials/edit.spec.d.ts +1 -0
  14. package/dist/commands/credentials/edit.spec.js +73 -0
  15. package/dist/commands/credentials/get.d.ts +13 -0
  16. package/dist/commands/credentials/get.js +46 -0
  17. package/dist/commands/credentials/get.spec.d.ts +1 -0
  18. package/dist/commands/credentials/get.spec.js +74 -0
  19. package/dist/commands/credentials/init.d.ts +11 -0
  20. package/dist/commands/credentials/init.js +45 -0
  21. package/dist/commands/credentials/init.spec.d.ts +1 -0
  22. package/dist/commands/credentials/init.spec.js +68 -0
  23. package/dist/commands/credentials/show.d.ts +10 -0
  24. package/dist/commands/credentials/show.js +33 -0
  25. package/dist/commands/credentials/show.spec.d.ts +1 -0
  26. package/dist/commands/credentials/show.spec.js +57 -0
  27. package/dist/commands/dev/eject.d.ts +11 -0
  28. package/dist/commands/dev/eject.js +58 -0
  29. package/dist/commands/dev/eject.spec.d.ts +1 -0
  30. package/dist/commands/dev/eject.spec.js +109 -0
  31. package/dist/commands/dev/index.d.ts +14 -0
  32. package/dist/commands/dev/index.js +173 -0
  33. package/dist/commands/dev/index.spec.d.ts +1 -0
  34. package/dist/commands/dev/index.spec.js +239 -0
  35. package/dist/commands/init.d.ts +12 -0
  36. package/dist/commands/init.js +37 -0
  37. package/dist/commands/init.spec.d.ts +1 -0
  38. package/dist/commands/init.spec.js +100 -0
  39. package/dist/commands/update.d.ts +14 -0
  40. package/dist/commands/update.js +120 -0
  41. package/dist/commands/update.spec.d.ts +1 -0
  42. package/dist/commands/update.spec.js +178 -0
  43. package/dist/commands/workflow/cost.d.ts +16 -0
  44. package/dist/commands/workflow/cost.js +71 -0
  45. package/dist/commands/workflow/cost.spec.d.ts +1 -0
  46. package/dist/commands/workflow/cost.spec.js +47 -0
  47. package/dist/commands/workflow/dataset/generate.d.ts +22 -0
  48. package/dist/commands/workflow/dataset/generate.js +143 -0
  49. package/dist/commands/workflow/dataset/list.d.ts +12 -0
  50. package/dist/commands/workflow/dataset/list.js +87 -0
  51. package/dist/commands/workflow/debug.d.ts +16 -0
  52. package/dist/commands/workflow/debug.js +60 -0
  53. package/dist/commands/workflow/debug.spec.d.ts +1 -0
  54. package/dist/commands/workflow/debug.spec.js +34 -0
  55. package/dist/commands/workflow/generate.d.ts +17 -0
  56. package/dist/commands/workflow/generate.js +85 -0
  57. package/dist/commands/workflow/generate.spec.d.ts +1 -0
  58. package/dist/commands/workflow/generate.spec.js +115 -0
  59. package/dist/commands/workflow/list.d.ts +22 -0
  60. package/dist/commands/workflow/list.js +152 -0
  61. package/dist/commands/workflow/list.spec.d.ts +1 -0
  62. package/dist/commands/workflow/list.spec.js +99 -0
  63. package/dist/commands/workflow/plan.d.ts +12 -0
  64. package/dist/commands/workflow/plan.js +66 -0
  65. package/dist/commands/workflow/plan.spec.d.ts +1 -0
  66. package/dist/commands/workflow/plan.spec.js +341 -0
  67. package/dist/commands/workflow/reset.d.ts +14 -0
  68. package/dist/commands/workflow/reset.js +51 -0
  69. package/dist/commands/workflow/result.d.ts +13 -0
  70. package/dist/commands/workflow/result.js +46 -0
  71. package/dist/commands/workflow/result.spec.d.ts +1 -0
  72. package/dist/commands/workflow/result.spec.js +23 -0
  73. package/dist/commands/workflow/run.d.ts +16 -0
  74. package/dist/commands/workflow/run.js +97 -0
  75. package/dist/commands/workflow/run.spec.d.ts +1 -0
  76. package/dist/commands/workflow/run.spec.js +110 -0
  77. package/dist/commands/workflow/runs/list.d.ts +14 -0
  78. package/dist/commands/workflow/runs/list.js +104 -0
  79. package/dist/commands/workflow/start.d.ts +15 -0
  80. package/dist/commands/workflow/start.js +62 -0
  81. package/dist/commands/workflow/start.spec.d.ts +1 -0
  82. package/dist/commands/workflow/start.spec.js +28 -0
  83. package/dist/commands/workflow/status.d.ts +13 -0
  84. package/dist/commands/workflow/status.js +57 -0
  85. package/dist/commands/workflow/status.spec.d.ts +1 -0
  86. package/dist/commands/workflow/status.spec.js +33 -0
  87. package/dist/commands/workflow/stop.d.ts +10 -0
  88. package/dist/commands/workflow/stop.js +31 -0
  89. package/dist/commands/workflow/stop.spec.d.ts +1 -0
  90. package/dist/commands/workflow/stop.spec.js +17 -0
  91. package/dist/commands/workflow/terminate.d.ts +13 -0
  92. package/dist/commands/workflow/terminate.js +39 -0
  93. package/dist/commands/workflow/test_eval.d.ts +20 -0
  94. package/dist/commands/workflow/test_eval.js +151 -0
  95. package/dist/config.d.ts +47 -0
  96. package/dist/config.js +47 -0
  97. package/dist/generated/framework_version.json +3 -0
  98. package/dist/hooks/init.d.ts +3 -0
  99. package/dist/hooks/init.js +30 -0
  100. package/dist/hooks/init.spec.d.ts +1 -0
  101. package/dist/hooks/init.spec.js +54 -0
  102. package/dist/index.d.ts +1 -0
  103. package/dist/index.js +1 -0
  104. package/dist/index.spec.d.ts +1 -0
  105. package/dist/index.spec.js +6 -0
  106. package/dist/services/claude_client.d.ts +30 -0
  107. package/dist/services/claude_client.integration.test.d.ts +1 -0
  108. package/dist/services/claude_client.integration.test.js +43 -0
  109. package/dist/services/claude_client.js +215 -0
  110. package/dist/services/claude_client.spec.d.ts +1 -0
  111. package/dist/services/claude_client.spec.js +145 -0
  112. package/dist/services/coding_agents.d.ts +36 -0
  113. package/dist/services/coding_agents.js +236 -0
  114. package/dist/services/coding_agents.spec.d.ts +1 -0
  115. package/dist/services/coding_agents.spec.js +256 -0
  116. package/dist/services/copy_assets.spec.d.ts +1 -0
  117. package/dist/services/copy_assets.spec.js +22 -0
  118. package/dist/services/cost_calculator.d.ts +18 -0
  119. package/dist/services/cost_calculator.js +359 -0
  120. package/dist/services/cost_calculator.spec.d.ts +1 -0
  121. package/dist/services/cost_calculator.spec.js +540 -0
  122. package/dist/services/credentials_service.d.ts +12 -0
  123. package/dist/services/credentials_service.integration.test.d.ts +1 -0
  124. package/dist/services/credentials_service.integration.test.js +66 -0
  125. package/dist/services/credentials_service.js +64 -0
  126. package/dist/services/credentials_service.spec.d.ts +1 -0
  127. package/dist/services/credentials_service.spec.js +106 -0
  128. package/dist/services/datasets.d.ts +20 -0
  129. package/dist/services/datasets.js +132 -0
  130. package/dist/services/docker.d.ts +39 -0
  131. package/dist/services/docker.js +160 -0
  132. package/dist/services/docker.spec.d.ts +1 -0
  133. package/dist/services/docker.spec.js +124 -0
  134. package/dist/services/env_configurator.d.ts +15 -0
  135. package/dist/services/env_configurator.js +163 -0
  136. package/dist/services/env_configurator.spec.d.ts +1 -0
  137. package/dist/services/env_configurator.spec.js +192 -0
  138. package/dist/services/generate_plan_name@v1.prompt +24 -0
  139. package/dist/services/messages.d.ts +9 -0
  140. package/dist/services/messages.js +338 -0
  141. package/dist/services/messages.spec.d.ts +1 -0
  142. package/dist/services/messages.spec.js +55 -0
  143. package/dist/services/npm_update_service.d.ts +6 -0
  144. package/dist/services/npm_update_service.js +87 -0
  145. package/dist/services/npm_update_service.spec.d.ts +1 -0
  146. package/dist/services/npm_update_service.spec.js +104 -0
  147. package/dist/services/project_scaffold.d.ts +31 -0
  148. package/dist/services/project_scaffold.js +212 -0
  149. package/dist/services/project_scaffold.spec.d.ts +1 -0
  150. package/dist/services/project_scaffold.spec.js +122 -0
  151. package/dist/services/s3_trace_downloader.d.ts +12 -0
  152. package/dist/services/s3_trace_downloader.js +57 -0
  153. package/dist/services/template_processor.d.ts +14 -0
  154. package/dist/services/template_processor.js +57 -0
  155. package/dist/services/trace_reader.d.ts +16 -0
  156. package/dist/services/trace_reader.js +57 -0
  157. package/dist/services/trace_reader.spec.d.ts +1 -0
  158. package/dist/services/trace_reader.spec.js +78 -0
  159. package/dist/services/version_check.d.ts +6 -0
  160. package/dist/services/version_check.js +52 -0
  161. package/dist/services/version_check.spec.d.ts +1 -0
  162. package/dist/services/version_check.spec.js +106 -0
  163. package/dist/services/workflow_builder.d.ts +16 -0
  164. package/dist/services/workflow_builder.js +86 -0
  165. package/dist/services/workflow_builder.spec.d.ts +1 -0
  166. package/dist/services/workflow_builder.spec.js +165 -0
  167. package/dist/services/workflow_generator.d.ts +5 -0
  168. package/dist/services/workflow_generator.js +40 -0
  169. package/dist/services/workflow_generator.spec.d.ts +1 -0
  170. package/dist/services/workflow_generator.spec.js +77 -0
  171. package/dist/services/workflow_planner.d.ts +15 -0
  172. package/dist/services/workflow_planner.js +48 -0
  173. package/dist/services/workflow_planner.spec.d.ts +1 -0
  174. package/dist/services/workflow_planner.spec.js +122 -0
  175. package/dist/services/workflow_runs.d.ts +14 -0
  176. package/dist/services/workflow_runs.js +25 -0
  177. package/dist/templates/agent_instructions/CLAUDE.md.template +19 -0
  178. package/dist/templates/agent_instructions/dotclaude/settings.json.template +29 -0
  179. package/dist/templates/project/.env.example.template +9 -0
  180. package/dist/templates/project/.gitignore.template +35 -0
  181. package/dist/templates/project/README.md.template +100 -0
  182. package/dist/templates/project/config/costs.yml.template +29 -0
  183. package/dist/templates/project/package.json.template +25 -0
  184. package/dist/templates/project/src/clients/jina.ts.template +30 -0
  185. package/dist/templates/project/src/shared/utils/string.ts.template +3 -0
  186. package/dist/templates/project/src/shared/utils/url.ts.template +15 -0
  187. package/dist/templates/project/src/workflows/blog_evaluator/evaluators.ts.template +23 -0
  188. package/dist/templates/project/src/workflows/blog_evaluator/prompts/signal_noise@v1.prompt.template +26 -0
  189. package/dist/templates/project/src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json.template +3 -0
  190. package/dist/templates/project/src/workflows/blog_evaluator/steps.ts.template +27 -0
  191. package/dist/templates/project/src/workflows/blog_evaluator/types.ts.template +30 -0
  192. package/dist/templates/project/src/workflows/blog_evaluator/utils.ts.template +15 -0
  193. package/dist/templates/project/src/workflows/blog_evaluator/workflow.ts.template +27 -0
  194. package/dist/templates/project/tsconfig.json.template +20 -0
  195. package/dist/templates/workflow/README.md.template +216 -0
  196. package/dist/templates/workflow/evaluators.ts.template +21 -0
  197. package/dist/templates/workflow/prompts/example@v1.prompt.template +15 -0
  198. package/dist/templates/workflow/scenarios/test_input.json.template +3 -0
  199. package/dist/templates/workflow/steps.ts.template +20 -0
  200. package/dist/templates/workflow/types.ts.template +13 -0
  201. package/dist/templates/workflow/workflow.ts.template +23 -0
  202. package/dist/test_helpers/mocks.d.ts +38 -0
  203. package/dist/test_helpers/mocks.js +77 -0
  204. package/dist/types/cost.d.ts +149 -0
  205. package/dist/types/cost.js +6 -0
  206. package/dist/types/domain.d.ts +20 -0
  207. package/dist/types/domain.js +4 -0
  208. package/dist/types/errors.d.ts +68 -0
  209. package/dist/types/errors.js +100 -0
  210. package/dist/types/errors.spec.d.ts +1 -0
  211. package/dist/types/errors.spec.js +18 -0
  212. package/dist/types/generator.d.ts +26 -0
  213. package/dist/types/generator.js +1 -0
  214. package/dist/types/trace.d.ts +161 -0
  215. package/dist/types/trace.js +18 -0
  216. package/dist/utils/claude.d.ts +5 -0
  217. package/dist/utils/claude.js +19 -0
  218. package/dist/utils/claude.spec.d.ts +1 -0
  219. package/dist/utils/claude.spec.js +119 -0
  220. package/dist/utils/constants.d.ts +5 -0
  221. package/dist/utils/constants.js +4 -0
  222. package/dist/utils/cost_formatter.d.ts +5 -0
  223. package/dist/utils/cost_formatter.js +218 -0
  224. package/dist/utils/date_formatter.d.ts +23 -0
  225. package/dist/utils/date_formatter.js +49 -0
  226. package/dist/utils/env_loader.d.ts +1 -0
  227. package/dist/utils/env_loader.js +22 -0
  228. package/dist/utils/env_loader.spec.d.ts +1 -0
  229. package/dist/utils/env_loader.spec.js +43 -0
  230. package/dist/utils/error_handler.d.ts +8 -0
  231. package/dist/utils/error_handler.js +71 -0
  232. package/dist/utils/error_utils.d.ts +24 -0
  233. package/dist/utils/error_utils.js +87 -0
  234. package/dist/utils/file_system.d.ts +3 -0
  235. package/dist/utils/file_system.js +33 -0
  236. package/dist/utils/format_workflow_result.d.ts +5 -0
  237. package/dist/utils/format_workflow_result.js +18 -0
  238. package/dist/utils/format_workflow_result.spec.d.ts +1 -0
  239. package/dist/utils/format_workflow_result.spec.js +81 -0
  240. package/dist/utils/framework_version.d.ts +4 -0
  241. package/dist/utils/framework_version.js +4 -0
  242. package/dist/utils/framework_version.spec.d.ts +1 -0
  243. package/dist/utils/framework_version.spec.js +13 -0
  244. package/dist/utils/header_utils.d.ts +12 -0
  245. package/dist/utils/header_utils.js +29 -0
  246. package/dist/utils/header_utils.spec.d.ts +1 -0
  247. package/dist/utils/header_utils.spec.js +52 -0
  248. package/dist/utils/input_parser.d.ts +1 -0
  249. package/dist/utils/input_parser.js +19 -0
  250. package/dist/utils/output_formatter.d.ts +2 -0
  251. package/dist/utils/output_formatter.js +11 -0
  252. package/dist/utils/paths.d.ts +25 -0
  253. package/dist/utils/paths.js +36 -0
  254. package/dist/utils/process.d.ts +4 -0
  255. package/dist/utils/process.js +50 -0
  256. package/dist/utils/resolve_input.d.ts +1 -0
  257. package/dist/utils/resolve_input.js +22 -0
  258. package/dist/utils/scenario_resolver.d.ts +9 -0
  259. package/dist/utils/scenario_resolver.js +93 -0
  260. package/dist/utils/scenario_resolver.spec.d.ts +1 -0
  261. package/dist/utils/scenario_resolver.spec.js +214 -0
  262. package/dist/utils/secret_sanitizer.d.ts +1 -0
  263. package/dist/utils/secret_sanitizer.js +29 -0
  264. package/dist/utils/sleep.d.ts +5 -0
  265. package/dist/utils/sleep.js +5 -0
  266. package/dist/utils/template.d.ts +9 -0
  267. package/dist/utils/template.js +30 -0
  268. package/dist/utils/template.spec.d.ts +1 -0
  269. package/dist/utils/template.spec.js +77 -0
  270. package/dist/utils/trace_extractor.d.ts +27 -0
  271. package/dist/utils/trace_extractor.js +53 -0
  272. package/dist/utils/trace_formatter.d.ts +11 -0
  273. package/dist/utils/trace_formatter.js +402 -0
  274. package/dist/utils/validation.d.ts +13 -0
  275. package/dist/utils/validation.js +25 -0
  276. package/dist/utils/validation.spec.d.ts +1 -0
  277. package/dist/utils/validation.spec.js +140 -0
  278. package/package.json +4 -4
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { formatWorkflowResult } from './format_workflow_result.js';
3
+ import { formatOutput } from './output_formatter.js';
4
+ describe('formatWorkflowResult', () => {
5
+ it('should display output for completed workflows', () => {
6
+ const result = formatWorkflowResult({
7
+ workflowId: 'wf-123',
8
+ status: 'completed',
9
+ output: { values: [1, 2, 3] },
10
+ error: null
11
+ });
12
+ expect(result).toContain('Workflow ID: wf-123');
13
+ expect(result).toContain('Output:');
14
+ expect(result).toContain('"values"');
15
+ expect(result).not.toContain('Status:');
16
+ });
17
+ it('should display error details for failed workflows', () => {
18
+ const result = formatWorkflowResult({
19
+ workflowId: 'wf-456',
20
+ status: 'failed',
21
+ output: null,
22
+ error: 'Activity task failed'
23
+ });
24
+ expect(result).toContain('Workflow ID: wf-456');
25
+ expect(result).toContain('Status: failed');
26
+ expect(result).toContain('Error: Activity task failed');
27
+ expect(result).not.toContain('Output:');
28
+ });
29
+ it('should display status for terminated workflows', () => {
30
+ const result = formatWorkflowResult({
31
+ workflowId: 'wf-term',
32
+ status: 'terminated',
33
+ output: null,
34
+ error: 'Workflow terminated by user'
35
+ });
36
+ expect(result).toContain('Status: terminated');
37
+ expect(result).toContain('Error: Workflow terminated by user');
38
+ });
39
+ it('should display status for canceled workflows', () => {
40
+ const result = formatWorkflowResult({
41
+ workflowId: 'wf-cancel',
42
+ status: 'canceled',
43
+ output: null,
44
+ error: 'Workflow was canceled'
45
+ });
46
+ expect(result).toContain('Status: canceled');
47
+ expect(result).toContain('Error: Workflow was canceled');
48
+ });
49
+ it('should display status without error line for continued workflows', () => {
50
+ const result = formatWorkflowResult({
51
+ workflowId: 'wf-cont',
52
+ status: 'continued',
53
+ output: null,
54
+ error: null
55
+ });
56
+ expect(result).toContain('Status: continued');
57
+ expect(result).not.toContain('Error:');
58
+ });
59
+ it('should omit error line when error is null on failed workflow', () => {
60
+ const result = formatWorkflowResult({
61
+ workflowId: 'wf-789',
62
+ status: 'failed',
63
+ output: null,
64
+ error: null
65
+ });
66
+ expect(result).toContain('Status: failed');
67
+ expect(result).not.toContain('Error:');
68
+ });
69
+ it('should work with formatOutput for json format', () => {
70
+ const data = {
71
+ workflowId: 'wf-456',
72
+ status: 'failed',
73
+ output: null,
74
+ error: 'Activity task failed'
75
+ };
76
+ const output = formatOutput(data, 'json', formatWorkflowResult);
77
+ const parsed = JSON.parse(output);
78
+ expect(parsed.status).toBe('failed');
79
+ expect(parsed.error).toBe('Activity task failed');
80
+ });
81
+ });
@@ -0,0 +1,4 @@
1
+ export interface FrameworkVersion {
2
+ framework: string;
3
+ }
4
+ export declare function getFrameworkVersion(): Promise<FrameworkVersion>;
@@ -0,0 +1,4 @@
1
+ import frameworkVersion from '../generated/framework_version.json' with { type: 'json' };
2
+ export async function getFrameworkVersion() {
3
+ return frameworkVersion;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getFrameworkVersion } from './framework_version.js';
3
+ describe('getFrameworkVersion', () => {
4
+ it('should return the framework version', async () => {
5
+ const version = await getFrameworkVersion();
6
+ expect(version).toHaveProperty('framework');
7
+ });
8
+ it('should return version in semver format', async () => {
9
+ const version = await getFrameworkVersion();
10
+ const semverPattern = /^\d+\.\d+\.\d+$/;
11
+ expect(version.framework).toMatch(semverPattern);
12
+ });
13
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Read, extract and parse the value from "Retry-After" header from Fetch's Response Headers object.
3
+ * - If provided as string number, convert it to milliseconds.
4
+ * - If provided as date, subtracts the current date and return the difference in milliseconds.
5
+ *
6
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After}
7
+ * @param value - The HTTP Response Headers object
8
+ * @returns Delay in ms or or null if not parseable
9
+ */
10
+ export declare function getRetryDelayFromResponse(response: {
11
+ headers?: Headers;
12
+ }): number | null;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Read, extract and parse the value from "Retry-After" header from Fetch's Response Headers object.
3
+ * - If provided as string number, convert it to milliseconds.
4
+ * - If provided as date, subtracts the current date and return the difference in milliseconds.
5
+ *
6
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After}
7
+ * @param value - The HTTP Response Headers object
8
+ * @returns Delay in ms or or null if not parseable
9
+ */
10
+ export function getRetryDelayFromResponse(response) {
11
+ if (!response.headers || !response.headers.has('retry-after')) {
12
+ return null;
13
+ }
14
+ const value = response.headers.get('retry-after');
15
+ // test if it is number
16
+ if (/^\d+$/.test(value)) {
17
+ return Number(value) * 1000;
18
+ }
19
+ // test if has letters: RFC 1123 (IMF-fixdate), RFC 850 or ANSI C asctime()
20
+ if (/[a-z]/i.test(value)) {
21
+ const date = new Date(value);
22
+ if (Number.isNaN(date.valueOf())) {
23
+ return null;
24
+ }
25
+ const delta = date.getTime() - Date.now();
26
+ return Math.max(delta, 0);
27
+ }
28
+ return null;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
+ import { getRetryDelayFromResponse } from './header_utils.js';
3
+ /** Builds a response-like object with optional Retry-After header. */
4
+ const buildResponse = (retryAfter) => retryAfter !== null ?
5
+ { headers: new Headers({ 'Retry-After': retryAfter }) } :
6
+ { headers: new Headers() };
7
+ describe('getRetryDelayFromResponse', () => {
8
+ beforeEach(() => {
9
+ vi.useFakeTimers();
10
+ vi.setSystemTime(new Date('2025-02-25T12:00:00.000Z'));
11
+ });
12
+ afterEach(() => {
13
+ vi.useRealTimers();
14
+ });
15
+ describe('delay-seconds (integer)', () => {
16
+ it('returns ms for positive integer seconds', () => {
17
+ expect(getRetryDelayFromResponse(buildResponse('0'))).toBe(0);
18
+ expect(getRetryDelayFromResponse(buildResponse('1'))).toBe(1000);
19
+ expect(getRetryDelayFromResponse(buildResponse('120'))).toBe(120_000);
20
+ });
21
+ it('returns null for negative integer', () => {
22
+ expect(getRetryDelayFromResponse(buildResponse('-1'))).toBeNull();
23
+ });
24
+ it('returns null for non-integer number', () => {
25
+ expect(getRetryDelayFromResponse(buildResponse('12.34'))).toBeNull();
26
+ });
27
+ });
28
+ describe('HTTP-date', () => {
29
+ it('returns 0 when date is in the past', () => {
30
+ expect(getRetryDelayFromResponse(buildResponse('Wed, 21 Oct 2015 07:28:00 GMT'))).toBe(0);
31
+ });
32
+ it('returns ms until future date', () => {
33
+ const future = 'Tue, 25 Feb 2025 12:00:02 GMT';
34
+ expect(getRetryDelayFromResponse(buildResponse(future))).toBe(2000);
35
+ });
36
+ });
37
+ describe('invalid or missing header', () => {
38
+ it('returns null for empty string', () => {
39
+ expect(getRetryDelayFromResponse(buildResponse(''))).toBeNull();
40
+ });
41
+ it('returns null for non-numeric non-date string', () => {
42
+ expect(getRetryDelayFromResponse(buildResponse('invalid'))).toBeNull();
43
+ expect(getRetryDelayFromResponse(buildResponse('abc123'))).toBeNull();
44
+ });
45
+ it('returns null when Retry-After header is absent', () => {
46
+ expect(getRetryDelayFromResponse(buildResponse(null))).toBeNull();
47
+ });
48
+ it('returns null when response has no headers property', () => {
49
+ expect(getRetryDelayFromResponse({})).toBeNull();
50
+ });
51
+ });
52
+ });
@@ -0,0 +1 @@
1
+ export declare function parseInputFlag(input: string): unknown;
@@ -0,0 +1,19 @@
1
+ import { readFileSync } from 'node:fs';
2
+ export function parseInputFlag(input) {
3
+ try {
4
+ return JSON.parse(input);
5
+ }
6
+ catch {
7
+ try {
8
+ const fileContent = readFileSync(input, 'utf-8');
9
+ return JSON.parse(fileContent);
10
+ }
11
+ catch (error) {
12
+ const err = error;
13
+ if (err.code === 'ENOENT') {
14
+ throw new Error(`Input file not found: ${input}`);
15
+ }
16
+ throw new Error(`Invalid JSON input: ${err.message}`);
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,2 @@
1
+ import { OutputFormat } from './constants.js';
2
+ export declare function formatOutput<T>(result: T, format: OutputFormat, textFormatter?: (result: T) => string): string;
@@ -0,0 +1,11 @@
1
+ import { OUTPUT_FORMAT } from './constants.js';
2
+ export function formatOutput(result, format, textFormatter = result => JSON.stringify(result, null, 2)) {
3
+ switch (format) {
4
+ case OUTPUT_FORMAT.JSON:
5
+ return JSON.stringify(result, null, 2);
6
+ case OUTPUT_FORMAT.TEXT:
7
+ return textFormatter(result);
8
+ default:
9
+ return textFormatter(result);
10
+ }
11
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Template directory paths
3
+ */
4
+ export declare const TEMPLATE_DIRS: {
5
+ readonly workflow: string;
6
+ readonly agent_instructions: string;
7
+ };
8
+ /**
9
+ * Default output directories
10
+ */
11
+ export declare const DEFAULT_OUTPUT_DIRS: {
12
+ readonly workflows: "src/workflows";
13
+ };
14
+ /**
15
+ * Resolve the output directory path
16
+ */
17
+ export declare function resolveOutputDir(outputDir: string): string;
18
+ /**
19
+ * Create target directory path for a workflow
20
+ */
21
+ export declare function createTargetDir(outputDir: string, workflowName: string): string;
22
+ /**
23
+ * Get the template directory for a specific template type
24
+ */
25
+ export declare function getTemplateDir(templateType: keyof typeof TEMPLATE_DIRS): string;
@@ -0,0 +1,36 @@
1
+ import * as path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ const __filename = fileURLToPath(import.meta.url);
4
+ const __dirname = path.dirname(__filename);
5
+ /**
6
+ * Template directory paths
7
+ */
8
+ export const TEMPLATE_DIRS = {
9
+ workflow: path.join(__dirname, '..', 'templates', 'workflow'),
10
+ agent_instructions: path.join(__dirname, '..', 'templates', 'agent_instructions')
11
+ };
12
+ /**
13
+ * Default output directories
14
+ */
15
+ export const DEFAULT_OUTPUT_DIRS = {
16
+ workflows: 'src/workflows'
17
+ };
18
+ /**
19
+ * Resolve the output directory path
20
+ */
21
+ export function resolveOutputDir(outputDir) {
22
+ return path.resolve(process.cwd(), outputDir);
23
+ }
24
+ /**
25
+ * Create target directory path for a workflow
26
+ */
27
+ export function createTargetDir(outputDir, workflowName) {
28
+ const resolvedOutputDir = resolveOutputDir(outputDir);
29
+ return path.join(resolvedOutputDir, workflowName);
30
+ }
31
+ /**
32
+ * Get the template directory for a specific template type
33
+ */
34
+ export function getTemplateDir(templateType) {
35
+ return TEMPLATE_DIRS[templateType];
36
+ }
@@ -0,0 +1,4 @@
1
+ export declare function executeCommand(command: string, args: string[], cwd: string): Promise<{
2
+ stderr: string[];
3
+ }>;
4
+ export declare function executeCommandWithMessages(command: () => Promise<void>, startMessage: string, successMessage: string): Promise<boolean>;
@@ -0,0 +1,50 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { ux } from '@oclif/core';
3
+ import debugFactory from 'debug';
4
+ import { getErrorMessage } from './error_utils.js';
5
+ const debug = debugFactory('output-cli:process');
6
+ export async function executeCommand(command, args, cwd) {
7
+ const stderrLines = [];
8
+ const proc = spawn(command, args, { cwd });
9
+ const handleStdout = (data) => {
10
+ const line = data.toString().trim();
11
+ if (line) {
12
+ debug(line);
13
+ }
14
+ };
15
+ const handleStderr = (data) => {
16
+ const line = data.toString().trim();
17
+ if (line) {
18
+ stderrLines.push(line);
19
+ debug(line);
20
+ }
21
+ };
22
+ proc.stdout.on('data', handleStdout);
23
+ proc.stderr.on('data', handleStderr);
24
+ return new Promise((resolve, reject) => {
25
+ proc.on('error', (error) => {
26
+ reject(new Error(`Failed to run ${command}: ${error.message}`));
27
+ });
28
+ proc.on('exit', (code) => {
29
+ if (code !== 0) {
30
+ reject(new Error(`${command} exited with code ${code || 'unknown'}`));
31
+ }
32
+ else {
33
+ resolve({ stderr: stderrLines });
34
+ }
35
+ });
36
+ });
37
+ }
38
+ export async function executeCommandWithMessages(command, startMessage, successMessage) {
39
+ try {
40
+ ux.stdout(startMessage);
41
+ await command();
42
+ ux.stdout(successMessage);
43
+ return true;
44
+ }
45
+ catch (error) {
46
+ const message = getErrorMessage(error);
47
+ ux.warn(`⚠️ ${message}`);
48
+ return false;
49
+ }
50
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveInput(workflowName: string, scenario: string | undefined, inputFlag: string | undefined, commandName: string): Promise<unknown>;
@@ -0,0 +1,22 @@
1
+ import { ux } from '@oclif/core';
2
+ import { parseInputFlag } from '#utils/input_parser.js';
3
+ import { resolveScenarioPath, getScenarioNotFoundMessage } from '#utils/scenario_resolver.js';
4
+ export async function resolveInput(workflowName, scenario, inputFlag, commandName) {
5
+ if (inputFlag && scenario) {
6
+ return ux.error('Cannot use both scenario argument and --input flag. Choose one.', { exit: 1 });
7
+ }
8
+ if (inputFlag) {
9
+ return parseInputFlag(inputFlag);
10
+ }
11
+ if (scenario) {
12
+ const resolution = await resolveScenarioPath(workflowName, scenario);
13
+ if (!resolution.found) {
14
+ return ux.error(getScenarioNotFoundMessage(workflowName, scenario, resolution.searchedPaths), { exit: 1 });
15
+ }
16
+ ux.stdout(`Using scenario: ${resolution.path}\n`);
17
+ return parseInputFlag(resolution.path);
18
+ }
19
+ return ux.error('Input required. Provide either:\n' +
20
+ ` - A scenario name: output workflow ${commandName} <workflow> <scenario>\n` +
21
+ ` - An input flag: output workflow ${commandName} <workflow> --input <json-or-file>`, { exit: 1 });
22
+ }
@@ -0,0 +1,9 @@
1
+ export interface ScenarioResolutionResult {
2
+ found: boolean;
3
+ path?: string;
4
+ searchedPaths: string[];
5
+ }
6
+ export declare function extractWorkflowRelativePath(path: string): string | null;
7
+ export declare function resolveScenarioPath(workflowName: string, scenarioName: string, basePath?: string): Promise<ScenarioResolutionResult>;
8
+ export declare function listScenariosForWorkflow(workflowName: string, workflowPath?: string, basePath?: string): string[];
9
+ export declare function getScenarioNotFoundMessage(workflowName: string, scenarioName: string, searchedPaths: string[]): string;
@@ -0,0 +1,93 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { getWorkflowCatalog } from '#api/generated/api.js';
4
+ const SCENARIOS_DIR = 'scenarios';
5
+ const WORKFLOWS_PATHS = ['src/workflows', 'workflows'];
6
+ export function extractWorkflowRelativePath(path) {
7
+ const match = path.match(/workflows\/(.+)\/workflow\.[jt]s$/);
8
+ return match ? match[1] : null;
9
+ }
10
+ async function fetchWorkflowDirectory(workflowName) {
11
+ try {
12
+ const response = await getWorkflowCatalog();
13
+ const data = response?.data;
14
+ const workflows = data?.workflows;
15
+ if (!workflows) {
16
+ return null;
17
+ }
18
+ const workflow = workflows.find(w => w.name === workflowName);
19
+ if (!workflow) {
20
+ return null;
21
+ }
22
+ const workflowPath = workflow.path;
23
+ if (!workflowPath) {
24
+ return null;
25
+ }
26
+ return extractWorkflowRelativePath(workflowPath);
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ function resolveScenarioFromDirectory(relativeDir, scenarioFileName, basePath) {
33
+ const searchedPaths = [];
34
+ for (const workflowsDir of WORKFLOWS_PATHS) {
35
+ const candidatePath = resolve(basePath, workflowsDir, relativeDir, SCENARIOS_DIR, scenarioFileName);
36
+ searchedPaths.push(candidatePath);
37
+ if (existsSync(candidatePath)) {
38
+ return { found: true, path: candidatePath, searchedPaths };
39
+ }
40
+ }
41
+ return { found: false, searchedPaths };
42
+ }
43
+ export async function resolveScenarioPath(workflowName, scenarioName, basePath = process.cwd()) {
44
+ const scenarioFileName = scenarioName.endsWith('.json') ?
45
+ scenarioName :
46
+ `${scenarioName}.json`;
47
+ const catalogDir = await fetchWorkflowDirectory(workflowName);
48
+ if (catalogDir) {
49
+ const result = resolveScenarioFromDirectory(catalogDir, scenarioFileName, basePath);
50
+ if (result.found) {
51
+ return result;
52
+ }
53
+ // Catalog resolved but scenario not found at that path — still try convention fallback
54
+ // in case the catalog path differs from local source layout
55
+ if (catalogDir !== workflowName) {
56
+ const fallback = resolveScenarioFromDirectory(workflowName, scenarioFileName, basePath);
57
+ return {
58
+ found: fallback.found,
59
+ path: fallback.path,
60
+ searchedPaths: [...result.searchedPaths, ...fallback.searchedPaths]
61
+ };
62
+ }
63
+ return result;
64
+ }
65
+ // API unavailable or workflow not in catalog — fall back to convention
66
+ return resolveScenarioFromDirectory(workflowName, scenarioFileName, basePath);
67
+ }
68
+ export function listScenariosForWorkflow(workflowName, workflowPath, basePath = process.cwd()) {
69
+ const relativeDir = (workflowPath && extractWorkflowRelativePath(workflowPath)) || workflowName;
70
+ for (const workflowsDir of WORKFLOWS_PATHS) {
71
+ const scenariosDir = resolve(basePath, workflowsDir, relativeDir, SCENARIOS_DIR);
72
+ if (existsSync(scenariosDir)) {
73
+ return readdirSync(scenariosDir)
74
+ .filter(f => f.endsWith('.json'))
75
+ .map(f => f.replace(/\.json$/, ''));
76
+ }
77
+ }
78
+ return [];
79
+ }
80
+ export function getScenarioNotFoundMessage(workflowName, scenarioName, searchedPaths) {
81
+ const pathsList = searchedPaths.map(p => ` - ${p}`).join('\n');
82
+ return [
83
+ `Scenario '${scenarioName}' not found for workflow '${workflowName}'.`,
84
+ '',
85
+ 'Searched in:',
86
+ pathsList,
87
+ '',
88
+ 'Tip: Create a scenario file in your workflow\'s scenarios/ directory.',
89
+ '',
90
+ 'Or use --input to specify a custom path:',
91
+ ` output workflow run ${workflowName} --input /path/to/input.json`
92
+ ].join('\n');
93
+ }
@@ -0,0 +1 @@
1
+ export {};