@outputai/cli 0.1.3-dev.0 → 0.1.3

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/package.json +4 -4
  2. package/dist/api/generated/api.d.ts +0 -820
  3. package/dist/api/generated/api.js +0 -226
  4. package/dist/api/http_client.d.ts +0 -27
  5. package/dist/api/http_client.js +0 -71
  6. package/dist/api/orval_post_process.d.ts +0 -11
  7. package/dist/api/orval_post_process.js +0 -46
  8. package/dist/api/parser.d.ts +0 -17
  9. package/dist/api/parser.js +0 -68
  10. package/dist/assets/config/costs.yml +0 -309
  11. package/dist/assets/docker/docker-compose-dev.yml +0 -146
  12. package/dist/commands/credentials/edit.d.ts +0 -10
  13. package/dist/commands/credentials/edit.js +0 -67
  14. package/dist/commands/credentials/edit.spec.d.ts +0 -1
  15. package/dist/commands/credentials/edit.spec.js +0 -73
  16. package/dist/commands/credentials/get.d.ts +0 -13
  17. package/dist/commands/credentials/get.js +0 -46
  18. package/dist/commands/credentials/get.spec.d.ts +0 -1
  19. package/dist/commands/credentials/get.spec.js +0 -74
  20. package/dist/commands/credentials/init.d.ts +0 -11
  21. package/dist/commands/credentials/init.js +0 -45
  22. package/dist/commands/credentials/init.spec.d.ts +0 -1
  23. package/dist/commands/credentials/init.spec.js +0 -68
  24. package/dist/commands/credentials/show.d.ts +0 -10
  25. package/dist/commands/credentials/show.js +0 -33
  26. package/dist/commands/credentials/show.spec.d.ts +0 -1
  27. package/dist/commands/credentials/show.spec.js +0 -57
  28. package/dist/commands/dev/eject.d.ts +0 -11
  29. package/dist/commands/dev/eject.js +0 -58
  30. package/dist/commands/dev/eject.spec.d.ts +0 -1
  31. package/dist/commands/dev/eject.spec.js +0 -109
  32. package/dist/commands/dev/index.d.ts +0 -14
  33. package/dist/commands/dev/index.js +0 -173
  34. package/dist/commands/dev/index.spec.d.ts +0 -1
  35. package/dist/commands/dev/index.spec.js +0 -239
  36. package/dist/commands/init.d.ts +0 -12
  37. package/dist/commands/init.js +0 -37
  38. package/dist/commands/init.spec.d.ts +0 -1
  39. package/dist/commands/init.spec.js +0 -100
  40. package/dist/commands/update.d.ts +0 -14
  41. package/dist/commands/update.js +0 -120
  42. package/dist/commands/update.spec.d.ts +0 -1
  43. package/dist/commands/update.spec.js +0 -178
  44. package/dist/commands/workflow/cost.d.ts +0 -16
  45. package/dist/commands/workflow/cost.js +0 -71
  46. package/dist/commands/workflow/cost.spec.d.ts +0 -1
  47. package/dist/commands/workflow/cost.spec.js +0 -47
  48. package/dist/commands/workflow/dataset/generate.d.ts +0 -22
  49. package/dist/commands/workflow/dataset/generate.js +0 -143
  50. package/dist/commands/workflow/dataset/list.d.ts +0 -12
  51. package/dist/commands/workflow/dataset/list.js +0 -87
  52. package/dist/commands/workflow/debug.d.ts +0 -16
  53. package/dist/commands/workflow/debug.js +0 -60
  54. package/dist/commands/workflow/debug.spec.d.ts +0 -1
  55. package/dist/commands/workflow/debug.spec.js +0 -34
  56. package/dist/commands/workflow/generate.d.ts +0 -17
  57. package/dist/commands/workflow/generate.js +0 -85
  58. package/dist/commands/workflow/generate.spec.d.ts +0 -1
  59. package/dist/commands/workflow/generate.spec.js +0 -115
  60. package/dist/commands/workflow/list.d.ts +0 -22
  61. package/dist/commands/workflow/list.js +0 -152
  62. package/dist/commands/workflow/list.spec.d.ts +0 -1
  63. package/dist/commands/workflow/list.spec.js +0 -99
  64. package/dist/commands/workflow/plan.d.ts +0 -12
  65. package/dist/commands/workflow/plan.js +0 -66
  66. package/dist/commands/workflow/plan.spec.d.ts +0 -1
  67. package/dist/commands/workflow/plan.spec.js +0 -341
  68. package/dist/commands/workflow/reset.d.ts +0 -14
  69. package/dist/commands/workflow/reset.js +0 -51
  70. package/dist/commands/workflow/result.d.ts +0 -13
  71. package/dist/commands/workflow/result.js +0 -46
  72. package/dist/commands/workflow/result.spec.d.ts +0 -1
  73. package/dist/commands/workflow/result.spec.js +0 -23
  74. package/dist/commands/workflow/run.d.ts +0 -16
  75. package/dist/commands/workflow/run.js +0 -97
  76. package/dist/commands/workflow/run.spec.d.ts +0 -1
  77. package/dist/commands/workflow/run.spec.js +0 -110
  78. package/dist/commands/workflow/runs/list.d.ts +0 -14
  79. package/dist/commands/workflow/runs/list.js +0 -104
  80. package/dist/commands/workflow/start.d.ts +0 -15
  81. package/dist/commands/workflow/start.js +0 -62
  82. package/dist/commands/workflow/start.spec.d.ts +0 -1
  83. package/dist/commands/workflow/start.spec.js +0 -28
  84. package/dist/commands/workflow/status.d.ts +0 -13
  85. package/dist/commands/workflow/status.js +0 -57
  86. package/dist/commands/workflow/status.spec.d.ts +0 -1
  87. package/dist/commands/workflow/status.spec.js +0 -33
  88. package/dist/commands/workflow/stop.d.ts +0 -10
  89. package/dist/commands/workflow/stop.js +0 -31
  90. package/dist/commands/workflow/stop.spec.d.ts +0 -1
  91. package/dist/commands/workflow/stop.spec.js +0 -17
  92. package/dist/commands/workflow/terminate.d.ts +0 -13
  93. package/dist/commands/workflow/terminate.js +0 -39
  94. package/dist/commands/workflow/test_eval.d.ts +0 -20
  95. package/dist/commands/workflow/test_eval.js +0 -151
  96. package/dist/config.d.ts +0 -47
  97. package/dist/config.js +0 -47
  98. package/dist/generated/framework_version.json +0 -3
  99. package/dist/hooks/init.d.ts +0 -3
  100. package/dist/hooks/init.js +0 -30
  101. package/dist/hooks/init.spec.d.ts +0 -1
  102. package/dist/hooks/init.spec.js +0 -54
  103. package/dist/index.d.ts +0 -1
  104. package/dist/index.js +0 -1
  105. package/dist/index.spec.d.ts +0 -1
  106. package/dist/index.spec.js +0 -6
  107. package/dist/services/claude_client.d.ts +0 -30
  108. package/dist/services/claude_client.integration.test.d.ts +0 -1
  109. package/dist/services/claude_client.integration.test.js +0 -43
  110. package/dist/services/claude_client.js +0 -215
  111. package/dist/services/claude_client.spec.d.ts +0 -1
  112. package/dist/services/claude_client.spec.js +0 -145
  113. package/dist/services/coding_agents.d.ts +0 -36
  114. package/dist/services/coding_agents.js +0 -236
  115. package/dist/services/coding_agents.spec.d.ts +0 -1
  116. package/dist/services/coding_agents.spec.js +0 -256
  117. package/dist/services/copy_assets.spec.d.ts +0 -1
  118. package/dist/services/copy_assets.spec.js +0 -22
  119. package/dist/services/cost_calculator.d.ts +0 -18
  120. package/dist/services/cost_calculator.js +0 -359
  121. package/dist/services/cost_calculator.spec.d.ts +0 -1
  122. package/dist/services/cost_calculator.spec.js +0 -540
  123. package/dist/services/credentials_service.d.ts +0 -12
  124. package/dist/services/credentials_service.integration.test.d.ts +0 -1
  125. package/dist/services/credentials_service.integration.test.js +0 -66
  126. package/dist/services/credentials_service.js +0 -64
  127. package/dist/services/credentials_service.spec.d.ts +0 -1
  128. package/dist/services/credentials_service.spec.js +0 -106
  129. package/dist/services/datasets.d.ts +0 -20
  130. package/dist/services/datasets.js +0 -132
  131. package/dist/services/docker.d.ts +0 -39
  132. package/dist/services/docker.js +0 -160
  133. package/dist/services/docker.spec.d.ts +0 -1
  134. package/dist/services/docker.spec.js +0 -124
  135. package/dist/services/env_configurator.d.ts +0 -15
  136. package/dist/services/env_configurator.js +0 -163
  137. package/dist/services/env_configurator.spec.d.ts +0 -1
  138. package/dist/services/env_configurator.spec.js +0 -192
  139. package/dist/services/generate_plan_name@v1.prompt +0 -24
  140. package/dist/services/messages.d.ts +0 -9
  141. package/dist/services/messages.js +0 -338
  142. package/dist/services/messages.spec.d.ts +0 -1
  143. package/dist/services/messages.spec.js +0 -55
  144. package/dist/services/npm_update_service.d.ts +0 -6
  145. package/dist/services/npm_update_service.js +0 -87
  146. package/dist/services/npm_update_service.spec.d.ts +0 -1
  147. package/dist/services/npm_update_service.spec.js +0 -104
  148. package/dist/services/project_scaffold.d.ts +0 -31
  149. package/dist/services/project_scaffold.js +0 -212
  150. package/dist/services/project_scaffold.spec.d.ts +0 -1
  151. package/dist/services/project_scaffold.spec.js +0 -122
  152. package/dist/services/s3_trace_downloader.d.ts +0 -12
  153. package/dist/services/s3_trace_downloader.js +0 -57
  154. package/dist/services/template_processor.d.ts +0 -14
  155. package/dist/services/template_processor.js +0 -57
  156. package/dist/services/trace_reader.d.ts +0 -16
  157. package/dist/services/trace_reader.js +0 -57
  158. package/dist/services/trace_reader.spec.d.ts +0 -1
  159. package/dist/services/trace_reader.spec.js +0 -78
  160. package/dist/services/version_check.d.ts +0 -6
  161. package/dist/services/version_check.js +0 -52
  162. package/dist/services/version_check.spec.d.ts +0 -1
  163. package/dist/services/version_check.spec.js +0 -106
  164. package/dist/services/workflow_builder.d.ts +0 -16
  165. package/dist/services/workflow_builder.js +0 -86
  166. package/dist/services/workflow_builder.spec.d.ts +0 -1
  167. package/dist/services/workflow_builder.spec.js +0 -165
  168. package/dist/services/workflow_generator.d.ts +0 -5
  169. package/dist/services/workflow_generator.js +0 -40
  170. package/dist/services/workflow_generator.spec.d.ts +0 -1
  171. package/dist/services/workflow_generator.spec.js +0 -77
  172. package/dist/services/workflow_planner.d.ts +0 -15
  173. package/dist/services/workflow_planner.js +0 -48
  174. package/dist/services/workflow_planner.spec.d.ts +0 -1
  175. package/dist/services/workflow_planner.spec.js +0 -122
  176. package/dist/services/workflow_runs.d.ts +0 -14
  177. package/dist/services/workflow_runs.js +0 -25
  178. package/dist/templates/agent_instructions/CLAUDE.md.template +0 -19
  179. package/dist/templates/agent_instructions/dotclaude/settings.json.template +0 -29
  180. package/dist/templates/project/.env.example.template +0 -9
  181. package/dist/templates/project/.gitignore.template +0 -35
  182. package/dist/templates/project/README.md.template +0 -100
  183. package/dist/templates/project/config/costs.yml.template +0 -29
  184. package/dist/templates/project/package.json.template +0 -25
  185. package/dist/templates/project/src/clients/jina.ts.template +0 -30
  186. package/dist/templates/project/src/shared/utils/string.ts.template +0 -3
  187. package/dist/templates/project/src/shared/utils/url.ts.template +0 -15
  188. package/dist/templates/project/src/workflows/blog_evaluator/evaluators.ts.template +0 -23
  189. package/dist/templates/project/src/workflows/blog_evaluator/prompts/signal_noise@v1.prompt.template +0 -26
  190. package/dist/templates/project/src/workflows/blog_evaluator/scenarios/paulgraham_hwh.json.template +0 -3
  191. package/dist/templates/project/src/workflows/blog_evaluator/steps.ts.template +0 -27
  192. package/dist/templates/project/src/workflows/blog_evaluator/types.ts.template +0 -30
  193. package/dist/templates/project/src/workflows/blog_evaluator/utils.ts.template +0 -15
  194. package/dist/templates/project/src/workflows/blog_evaluator/workflow.ts.template +0 -27
  195. package/dist/templates/project/tsconfig.json.template +0 -20
  196. package/dist/templates/workflow/README.md.template +0 -216
  197. package/dist/templates/workflow/evaluators.ts.template +0 -21
  198. package/dist/templates/workflow/prompts/example@v1.prompt.template +0 -15
  199. package/dist/templates/workflow/scenarios/test_input.json.template +0 -3
  200. package/dist/templates/workflow/steps.ts.template +0 -20
  201. package/dist/templates/workflow/types.ts.template +0 -13
  202. package/dist/templates/workflow/workflow.ts.template +0 -23
  203. package/dist/test_helpers/mocks.d.ts +0 -38
  204. package/dist/test_helpers/mocks.js +0 -77
  205. package/dist/types/cost.d.ts +0 -149
  206. package/dist/types/cost.js +0 -6
  207. package/dist/types/domain.d.ts +0 -20
  208. package/dist/types/domain.js +0 -4
  209. package/dist/types/errors.d.ts +0 -68
  210. package/dist/types/errors.js +0 -100
  211. package/dist/types/errors.spec.d.ts +0 -1
  212. package/dist/types/errors.spec.js +0 -18
  213. package/dist/types/generator.d.ts +0 -26
  214. package/dist/types/generator.js +0 -1
  215. package/dist/types/trace.d.ts +0 -161
  216. package/dist/types/trace.js +0 -18
  217. package/dist/utils/claude.d.ts +0 -5
  218. package/dist/utils/claude.js +0 -19
  219. package/dist/utils/claude.spec.d.ts +0 -1
  220. package/dist/utils/claude.spec.js +0 -119
  221. package/dist/utils/constants.d.ts +0 -5
  222. package/dist/utils/constants.js +0 -4
  223. package/dist/utils/cost_formatter.d.ts +0 -5
  224. package/dist/utils/cost_formatter.js +0 -218
  225. package/dist/utils/date_formatter.d.ts +0 -23
  226. package/dist/utils/date_formatter.js +0 -49
  227. package/dist/utils/env_loader.d.ts +0 -1
  228. package/dist/utils/env_loader.js +0 -22
  229. package/dist/utils/env_loader.spec.d.ts +0 -1
  230. package/dist/utils/env_loader.spec.js +0 -43
  231. package/dist/utils/error_handler.d.ts +0 -8
  232. package/dist/utils/error_handler.js +0 -71
  233. package/dist/utils/error_utils.d.ts +0 -24
  234. package/dist/utils/error_utils.js +0 -87
  235. package/dist/utils/file_system.d.ts +0 -3
  236. package/dist/utils/file_system.js +0 -33
  237. package/dist/utils/format_workflow_result.d.ts +0 -5
  238. package/dist/utils/format_workflow_result.js +0 -18
  239. package/dist/utils/format_workflow_result.spec.d.ts +0 -1
  240. package/dist/utils/format_workflow_result.spec.js +0 -81
  241. package/dist/utils/framework_version.d.ts +0 -4
  242. package/dist/utils/framework_version.js +0 -4
  243. package/dist/utils/framework_version.spec.d.ts +0 -1
  244. package/dist/utils/framework_version.spec.js +0 -13
  245. package/dist/utils/header_utils.d.ts +0 -12
  246. package/dist/utils/header_utils.js +0 -29
  247. package/dist/utils/header_utils.spec.d.ts +0 -1
  248. package/dist/utils/header_utils.spec.js +0 -52
  249. package/dist/utils/input_parser.d.ts +0 -1
  250. package/dist/utils/input_parser.js +0 -19
  251. package/dist/utils/output_formatter.d.ts +0 -2
  252. package/dist/utils/output_formatter.js +0 -11
  253. package/dist/utils/paths.d.ts +0 -25
  254. package/dist/utils/paths.js +0 -36
  255. package/dist/utils/process.d.ts +0 -4
  256. package/dist/utils/process.js +0 -50
  257. package/dist/utils/resolve_input.d.ts +0 -1
  258. package/dist/utils/resolve_input.js +0 -22
  259. package/dist/utils/scenario_resolver.d.ts +0 -9
  260. package/dist/utils/scenario_resolver.js +0 -93
  261. package/dist/utils/scenario_resolver.spec.d.ts +0 -1
  262. package/dist/utils/scenario_resolver.spec.js +0 -214
  263. package/dist/utils/secret_sanitizer.d.ts +0 -1
  264. package/dist/utils/secret_sanitizer.js +0 -29
  265. package/dist/utils/sleep.d.ts +0 -5
  266. package/dist/utils/sleep.js +0 -5
  267. package/dist/utils/template.d.ts +0 -9
  268. package/dist/utils/template.js +0 -30
  269. package/dist/utils/template.spec.d.ts +0 -1
  270. package/dist/utils/template.spec.js +0 -77
  271. package/dist/utils/trace_extractor.d.ts +0 -27
  272. package/dist/utils/trace_extractor.js +0 -53
  273. package/dist/utils/trace_formatter.d.ts +0 -11
  274. package/dist/utils/trace_formatter.js +0 -402
  275. package/dist/utils/validation.d.ts +0 -13
  276. package/dist/utils/validation.js +0 -25
  277. package/dist/utils/validation.spec.d.ts +0 -1
  278. package/dist/utils/validation.spec.js +0 -140
@@ -1,106 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import path from 'node:path';
3
- vi.mock('node:fs', () => ({
4
- default: {
5
- existsSync: vi.fn(),
6
- readFileSync: vi.fn(),
7
- writeFileSync: vi.fn(),
8
- mkdirSync: vi.fn()
9
- }
10
- }));
11
- vi.mock('@outputai/credentials', async (importOriginal) => ({
12
- ...(await importOriginal()),
13
- encrypt: vi.fn((plaintext) => `encrypted:${plaintext}`),
14
- decrypt: vi.fn((ciphertext) => ciphertext.replace('encrypted:', '')),
15
- generateKey: vi.fn(() => 'a'.repeat(64))
16
- }));
17
- import { resolveCredentialsPath, resolveKeyPath, credentialsExist, resolveKey, decryptCredentials, initCredentials } from './credentials_service.js';
18
- import fs from 'node:fs';
19
- describe('credentials service', () => {
20
- const cwd = process.cwd();
21
- beforeEach(() => {
22
- vi.clearAllMocks();
23
- delete process.env.OUTPUT_CREDENTIALS_KEY;
24
- delete process.env.OUTPUT_CREDENTIALS_KEY_PRODUCTION;
25
- });
26
- afterEach(() => {
27
- delete process.env.OUTPUT_CREDENTIALS_KEY;
28
- delete process.env.OUTPUT_CREDENTIALS_KEY_PRODUCTION;
29
- });
30
- describe('resolveCredentialsPath', () => {
31
- it('should resolve default path when no environment', () => {
32
- const result = resolveCredentialsPath(undefined);
33
- expect(result).toBe(path.resolve(cwd, 'config', 'credentials.yml.enc'));
34
- });
35
- it('should resolve environment-specific path', () => {
36
- const result = resolveCredentialsPath('production');
37
- expect(result).toBe(path.resolve(cwd, 'config', 'credentials', 'production.yml.enc'));
38
- });
39
- });
40
- describe('resolveKeyPath', () => {
41
- it('should resolve default key path', () => {
42
- const result = resolveKeyPath(undefined);
43
- expect(result).toBe(path.resolve(cwd, 'config', 'credentials.key'));
44
- });
45
- it('should resolve environment-specific key path', () => {
46
- const result = resolveKeyPath('staging');
47
- expect(result).toBe(path.resolve(cwd, 'config', 'credentials', 'staging.key'));
48
- });
49
- });
50
- describe('credentialsExist', () => {
51
- it('should return true when credentials file exists', () => {
52
- vi.mocked(fs.existsSync).mockReturnValue(true);
53
- expect(credentialsExist(undefined)).toBe(true);
54
- });
55
- it('should return false when credentials file does not exist', () => {
56
- vi.mocked(fs.existsSync).mockReturnValue(false);
57
- expect(credentialsExist(undefined)).toBe(false);
58
- });
59
- });
60
- describe('resolveKey', () => {
61
- it('should return key from env var', () => {
62
- process.env.OUTPUT_CREDENTIALS_KEY = 'env-key';
63
- expect(resolveKey(undefined)).toBe('env-key');
64
- });
65
- it('should return environment-specific key from env var', () => {
66
- process.env.OUTPUT_CREDENTIALS_KEY_PRODUCTION = 'prod-key';
67
- expect(resolveKey('production')).toBe('prod-key');
68
- });
69
- it('should fall back to key file', () => {
70
- vi.mocked(fs.existsSync).mockReturnValue(true);
71
- vi.mocked(fs.readFileSync).mockReturnValue('file-key\n');
72
- expect(resolveKey(undefined)).toBe('file-key');
73
- });
74
- it('should throw when no key source is available', () => {
75
- vi.mocked(fs.existsSync).mockReturnValue(false);
76
- expect(() => resolveKey(undefined)).toThrow('No key found');
77
- });
78
- });
79
- describe('decryptCredentials', () => {
80
- it('should decrypt credentials file', () => {
81
- process.env.OUTPUT_CREDENTIALS_KEY = 'test-key';
82
- vi.mocked(fs.existsSync).mockReturnValue(true);
83
- vi.mocked(fs.readFileSync).mockReturnValue('encrypted:hello');
84
- expect(decryptCredentials(undefined)).toBe('hello');
85
- });
86
- it('should throw when credentials file does not exist', () => {
87
- process.env.OUTPUT_CREDENTIALS_KEY = 'test-key';
88
- vi.mocked(fs.existsSync).mockReturnValue(false);
89
- expect(() => decryptCredentials(undefined)).toThrow('Credentials file not found');
90
- });
91
- });
92
- describe('initCredentials', () => {
93
- it('should create key file and encrypted credentials file', () => {
94
- const result = initCredentials(undefined);
95
- expect(fs.mkdirSync).toHaveBeenCalled();
96
- expect(fs.writeFileSync).toHaveBeenCalledTimes(2);
97
- expect(result.keyPath).toContain('credentials.key');
98
- expect(result.credPath).toContain('credentials.yml.enc');
99
- });
100
- it('should create environment-specific files', () => {
101
- const result = initCredentials('production');
102
- expect(result.keyPath).toContain(path.join('credentials', 'production.key'));
103
- expect(result.credPath).toContain(path.join('credentials', 'production.yml.enc'));
104
- });
105
- });
106
- });
@@ -1,20 +0,0 @@
1
- import type { Dataset } from '@outputai/evals';
2
- export interface DatasetInfo {
3
- name: string;
4
- path: string;
5
- hasLastOutput: boolean;
6
- lastOutputDate?: string;
7
- lastEvalDate?: string;
8
- }
9
- export declare function resolveDatasetsDir(workflowName: string, basePath?: string): string | null;
10
- export declare function resolveDefaultDatasetsDir(workflowName: string, basePath?: string): string;
11
- export declare function readDataset(filePath: string): Promise<Dataset>;
12
- export declare function readAllDatasets(workflowName: string, filterNames?: string[], basePath?: string): Promise<{
13
- datasets: Dataset[];
14
- dir: string;
15
- }>;
16
- export declare function writeDataset(dataset: Dataset, filePath: string): Promise<void>;
17
- export declare function listDatasets(workflowName: string, basePath?: string): Promise<DatasetInfo[]>;
18
- export declare function buildDataset(name: string, input: Record<string, unknown>, output: unknown, executionTimeMs?: number): Dataset;
19
- export declare function extractDatasetName(tracePathOrKey: string): string;
20
- export declare function getExecutionTime(workflowId: string | undefined): Promise<number | undefined>;
@@ -1,132 +0,0 @@
1
- import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
2
- import { existsSync } from 'node:fs';
3
- import { resolve, join } from 'node:path';
4
- import yaml from 'js-yaml';
5
- import { DatasetSchema } from '@outputai/evals';
6
- import { getTrace } from '#services/trace_reader.js';
7
- import { sanitizeSecrets } from '#utils/secret_sanitizer.js';
8
- const DATASETS_DIR = 'tests/datasets';
9
- const WORKFLOWS_PATHS = ['src/workflows', 'workflows'];
10
- export function resolveDatasetsDir(workflowName, basePath = process.cwd()) {
11
- for (const workflowsDir of WORKFLOWS_PATHS) {
12
- const candidate = resolve(basePath, workflowsDir, workflowName, DATASETS_DIR);
13
- if (existsSync(candidate)) {
14
- return candidate;
15
- }
16
- }
17
- return null;
18
- }
19
- export function resolveDefaultDatasetsDir(workflowName, basePath = process.cwd()) {
20
- const existing = resolveDatasetsDir(workflowName, basePath);
21
- if (existing) {
22
- return existing;
23
- }
24
- // Default to first workflows path
25
- return resolve(basePath, WORKFLOWS_PATHS[0], workflowName, DATASETS_DIR);
26
- }
27
- export async function readDataset(filePath) {
28
- const content = await readFile(filePath, 'utf-8');
29
- const raw = yaml.load(content);
30
- return DatasetSchema.parse(raw);
31
- }
32
- export async function readAllDatasets(workflowName, filterNames, basePath) {
33
- const dir = resolveDatasetsDir(workflowName, basePath);
34
- if (!dir) {
35
- return { datasets: [], dir: resolveDefaultDatasetsDir(workflowName, basePath) };
36
- }
37
- const files = await readdir(dir);
38
- const ymlFiles = files.filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
39
- const datasets = [];
40
- for (const file of ymlFiles) {
41
- const dataset = await readDataset(join(dir, file));
42
- if (filterNames && !filterNames.includes(dataset.name)) {
43
- continue;
44
- }
45
- datasets.push(dataset);
46
- }
47
- return { datasets, dir };
48
- }
49
- async function mergeWithExisting(dataset, filePath) {
50
- if (!existsSync(filePath)) {
51
- return dataset;
52
- }
53
- try {
54
- const existing = await readDataset(filePath);
55
- return {
56
- ...existing,
57
- ...dataset,
58
- ground_truth: dataset.ground_truth ?? existing.ground_truth,
59
- last_output: dataset.last_output ?? existing.last_output,
60
- last_eval: dataset.last_eval ?? existing.last_eval
61
- };
62
- }
63
- catch {
64
- return dataset;
65
- }
66
- }
67
- export async function writeDataset(dataset, filePath) {
68
- const merged = await mergeWithExisting(dataset, filePath);
69
- const dir = resolve(filePath, '..');
70
- if (!existsSync(dir)) {
71
- await mkdir(dir, { recursive: true });
72
- }
73
- const content = yaml.dump(merged, { lineWidth: 120, noRefs: true, sortKeys: false });
74
- await writeFile(filePath, content, 'utf-8');
75
- }
76
- export async function listDatasets(workflowName, basePath) {
77
- const dir = resolveDatasetsDir(workflowName, basePath);
78
- if (!dir) {
79
- return [];
80
- }
81
- const files = await readdir(dir);
82
- const ymlFiles = files.filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
83
- const results = [];
84
- for (const file of ymlFiles) {
85
- const filePath = join(dir, file);
86
- try {
87
- const dataset = await readDataset(filePath);
88
- results.push({
89
- name: dataset.name,
90
- path: filePath,
91
- hasLastOutput: dataset.last_output?.output !== undefined,
92
- lastOutputDate: dataset.last_output?.date,
93
- lastEvalDate: dataset.last_eval?.date
94
- });
95
- }
96
- catch {
97
- results.push({
98
- name: file.replace(/\.(yml|yaml)$/, ''),
99
- path: filePath,
100
- hasLastOutput: false
101
- });
102
- }
103
- }
104
- return results;
105
- }
106
- export function buildDataset(name, input, output, executionTimeMs) {
107
- return {
108
- name,
109
- input: sanitizeSecrets(input),
110
- last_output: {
111
- output: sanitizeSecrets(output),
112
- executionTimeMs,
113
- date: new Date().toISOString()
114
- }
115
- };
116
- }
117
- export function extractDatasetName(tracePathOrKey) {
118
- return tracePathOrKey.replace(/^.*\//, '').replace(/\.json$/, '');
119
- }
120
- export function getExecutionTime(workflowId) {
121
- if (!workflowId) {
122
- return Promise.resolve(undefined);
123
- }
124
- return getTrace(workflowId)
125
- .then(traceResult => {
126
- const root = traceResult.data.root;
127
- return (root.startTime && root.endTime) ?
128
- root.endTime - root.startTime :
129
- root.duration;
130
- })
131
- .catch(() => undefined);
132
- }
@@ -1,39 +0,0 @@
1
- import { type ChildProcess } from 'node:child_process';
2
- export declare const SERVICE_HEALTH: {
3
- readonly HEALTHY: "healthy";
4
- readonly UNHEALTHY: "unhealthy";
5
- readonly STARTING: "starting";
6
- readonly NONE: "none";
7
- };
8
- export declare const SERVICE_STATE: {
9
- readonly RUNNING: "running";
10
- readonly EXITED: "exited";
11
- };
12
- declare class DockerValidationError extends Error {
13
- }
14
- export interface ServiceStatus {
15
- name: string;
16
- state: string;
17
- health: string;
18
- ports: string[];
19
- }
20
- export declare class DockerComposeConfigNotFoundError extends Error {
21
- constructor(dockerComposePath: string);
22
- }
23
- declare const isDockerInstalled: () => boolean;
24
- declare const isDockerComposeAvailable: () => boolean;
25
- declare const isDockerDaemonRunning: () => boolean;
26
- export declare function validateDockerEnvironment(): void;
27
- export declare function getDefaultDockerComposePath(): string;
28
- export declare function parseServiceStatus(jsonOutput: string): ServiceStatus[];
29
- export declare function getServiceStatus(dockerComposePath: string): Promise<ServiceStatus[]>;
30
- export declare function waitForServicesHealthy(dockerComposePath: string, timeoutMs?: number, pollIntervalMs?: number): Promise<void>;
31
- export interface DockerComposeProcess {
32
- process: ChildProcess;
33
- waitForHealthy: () => Promise<void>;
34
- }
35
- export type PullPolicy = 'always' | 'missing' | 'never';
36
- export declare function startDockerCompose(dockerComposePath: string, pullPolicy?: PullPolicy): Promise<DockerComposeProcess>;
37
- export declare function startDockerComposeDetached(dockerComposePath: string, pullPolicy?: PullPolicy): void;
38
- export declare function stopDockerCompose(dockerComposePath: string): Promise<void>;
39
- export { isDockerInstalled, isDockerComposeAvailable, isDockerDaemonRunning, DockerValidationError };
@@ -1,160 +0,0 @@
1
- import { execFileSync, execSync, spawn } from 'node:child_process';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { ux } from '@oclif/core';
5
- import logUpdate from 'log-update';
6
- const DEFAULT_COMPOSE_PATH = '../assets/docker/docker-compose-dev.yml';
7
- export const SERVICE_HEALTH = {
8
- HEALTHY: 'healthy',
9
- UNHEALTHY: 'unhealthy',
10
- STARTING: 'starting',
11
- NONE: 'none'
12
- };
13
- export const SERVICE_STATE = {
14
- RUNNING: 'running',
15
- EXITED: 'exited'
16
- };
17
- class DockerValidationError extends Error {
18
- }
19
- export class DockerComposeConfigNotFoundError extends Error {
20
- constructor(dockerComposePath) {
21
- super(`Docker Compose configuration not found at: ${dockerComposePath}\n\
22
- This may indicate a problem with the CLI installation.`);
23
- }
24
- }
25
- const checkDockerCommand = (command) => {
26
- try {
27
- execSync(command, { stdio: 'pipe' });
28
- return true;
29
- }
30
- catch {
31
- return false;
32
- }
33
- };
34
- const isDockerInstalled = () => checkDockerCommand('docker --version');
35
- const isDockerComposeAvailable = () => checkDockerCommand('docker compose version');
36
- const isDockerDaemonRunning = () => checkDockerCommand('docker ps');
37
- const DOCKER_VALIDATIONS = [
38
- {
39
- check: isDockerInstalled,
40
- error: 'Docker is not installed. Please install Docker to use the dev command.\nVisit: https://docs.docker.com/get-docker/'
41
- },
42
- {
43
- check: isDockerComposeAvailable,
44
- error: 'Docker Compose is not installed. Please install Docker Compose to use the dev command.\nVisit: https://docs.docker.com/compose/install/'
45
- },
46
- {
47
- check: isDockerDaemonRunning,
48
- error: 'Docker daemon is not running. Please start Docker and try again.'
49
- }
50
- ];
51
- export function validateDockerEnvironment() {
52
- const failedValidation = DOCKER_VALIDATIONS.find(v => !v.check());
53
- if (failedValidation) {
54
- throw new DockerValidationError(failedValidation.error);
55
- }
56
- }
57
- export function getDefaultDockerComposePath() {
58
- return path.resolve(path.dirname(fileURLToPath(import.meta.url)), DEFAULT_COMPOSE_PATH);
59
- }
60
- export function parseServiceStatus(jsonOutput) {
61
- if (!jsonOutput.trim()) {
62
- return [];
63
- }
64
- return jsonOutput
65
- .trim()
66
- .split('\n')
67
- .filter(Boolean)
68
- .map(line => {
69
- const data = JSON.parse(line);
70
- return {
71
- name: data.Service || data.Name || 'unknown',
72
- state: data.State,
73
- health: data.Health || SERVICE_HEALTH.NONE,
74
- ports: data.Publishers?.map(p => `${p.PublishedPort}:${p.TargetPort}`) || []
75
- };
76
- });
77
- }
78
- export async function getServiceStatus(dockerComposePath) {
79
- const result = execFileSync('docker', ['compose', '-f', dockerComposePath, 'ps', '--all', '--format', 'json'], { encoding: 'utf-8', cwd: process.cwd() });
80
- return parseServiceStatus(result);
81
- }
82
- const STATUS_ICONS = {
83
- [SERVICE_HEALTH.HEALTHY]: '✓',
84
- [SERVICE_HEALTH.UNHEALTHY]: '✗',
85
- [SERVICE_HEALTH.STARTING]: '◐',
86
- [SERVICE_HEALTH.NONE]: '✓',
87
- [SERVICE_STATE.RUNNING]: '●',
88
- [SERVICE_STATE.EXITED]: '✗'
89
- };
90
- const STATUS_COLORS = {
91
- [SERVICE_HEALTH.HEALTHY]: '\x1b[32m',
92
- [SERVICE_HEALTH.UNHEALTHY]: '\x1b[31m',
93
- [SERVICE_HEALTH.STARTING]: '\x1b[33m',
94
- [SERVICE_HEALTH.NONE]: '\x1b[32m',
95
- [SERVICE_STATE.RUNNING]: '\x1b[34m',
96
- [SERVICE_STATE.EXITED]: '\x1b[31m'
97
- };
98
- const ANSI_RESET = '\x1b[0m';
99
- const formatServiceStatus = (services) => services.map(s => {
100
- const healthKey = s.health === SERVICE_HEALTH.NONE ? s.state : s.health;
101
- const icon = STATUS_ICONS[healthKey] || '?';
102
- const color = STATUS_COLORS[healthKey] || '';
103
- const status = s.health === SERVICE_HEALTH.NONE ? s.state : s.health;
104
- return ` ${color}${icon}${ANSI_RESET} ${s.name}: ${status}`;
105
- }).join('\n');
106
- export async function waitForServicesHealthy(dockerComposePath, timeoutMs = 120000, pollIntervalMs = 2000) {
107
- const startTime = Date.now();
108
- while (Date.now() - startTime < timeoutMs) {
109
- const services = await getServiceStatus(dockerComposePath);
110
- const allHealthy = services.every(s => s.health === SERVICE_HEALTH.HEALTHY || s.health === SERVICE_HEALTH.NONE);
111
- if (services.length > 0) {
112
- const statusLines = formatServiceStatus(services);
113
- logUpdate(`⏳ Waiting for services to become healthy...\n${statusLines}`);
114
- }
115
- if (allHealthy && services.length > 0) {
116
- logUpdate.done();
117
- return;
118
- }
119
- await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
120
- }
121
- logUpdate.done();
122
- throw new Error('Timeout waiting for services to become healthy');
123
- }
124
- export async function startDockerCompose(dockerComposePath, pullPolicy) {
125
- const args = [
126
- 'compose',
127
- '-f', dockerComposePath,
128
- '--project-directory', process.cwd(),
129
- 'up'
130
- ];
131
- if (pullPolicy) {
132
- args.push('--pull', pullPolicy);
133
- }
134
- ux.stdout('🐳 Starting Docker services...\n');
135
- const dockerProcess = spawn('docker', args, {
136
- cwd: process.cwd(),
137
- stdio: ['ignore', 'pipe', 'pipe']
138
- });
139
- return {
140
- process: dockerProcess,
141
- waitForHealthy: () => waitForServicesHealthy(dockerComposePath)
142
- };
143
- }
144
- export function startDockerComposeDetached(dockerComposePath, pullPolicy) {
145
- const args = [
146
- 'compose',
147
- '-f', dockerComposePath,
148
- '--project-directory', process.cwd(),
149
- 'up', '-d'
150
- ];
151
- if (pullPolicy) {
152
- args.push('--pull', pullPolicy);
153
- }
154
- execFileSync('docker', args, { stdio: 'inherit', cwd: process.cwd() });
155
- }
156
- export async function stopDockerCompose(dockerComposePath) {
157
- ux.stdout('⏹️ Stopping services...\n');
158
- execFileSync('docker', ['compose', '-f', dockerComposePath, 'down'], { stdio: 'inherit', cwd: process.cwd() });
159
- }
160
- export { isDockerInstalled, isDockerComposeAvailable, isDockerDaemonRunning, DockerValidationError };
@@ -1 +0,0 @@
1
- export {};
@@ -1,124 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { execFileSync } from 'node:child_process';
3
- import { parseServiceStatus, getServiceStatus, waitForServicesHealthy } from './docker.js';
4
- vi.mock('node:child_process', () => ({
5
- execSync: vi.fn(),
6
- execFileSync: vi.fn(),
7
- spawn: vi.fn()
8
- }));
9
- vi.mock('log-update', () => {
10
- const fn = vi.fn();
11
- fn.done = vi.fn();
12
- return { default: fn };
13
- });
14
- describe('docker service', () => {
15
- beforeEach(() => {
16
- vi.clearAllMocks();
17
- });
18
- afterEach(() => {
19
- vi.restoreAllMocks();
20
- });
21
- describe('parseServiceStatus', () => {
22
- it('should parse single service JSON output', () => {
23
- const jsonOutput = '{"Service":"redis","State":"running","Health":"healthy","Publishers":[{"PublishedPort":6379,"TargetPort":6379}]}';
24
- const result = parseServiceStatus(jsonOutput);
25
- expect(result).toHaveLength(1);
26
- expect(result[0]).toEqual({
27
- name: 'redis',
28
- state: 'running',
29
- health: 'healthy',
30
- ports: ['6379:6379']
31
- });
32
- });
33
- it('should parse multiple services from JSON lines output', () => {
34
- const jsonOutput = `{"Service":"redis","State":"running","Health":"healthy","Publishers":[{"PublishedPort":6379,"TargetPort":6379}]}
35
- {"Service":"temporal","State":"running","Health":"healthy","Publishers":[{"PublishedPort":7233,"TargetPort":7233}]}
36
- {"Service":"temporal-ui","State":"running","Health":"","Publishers":[{"PublishedPort":8080,"TargetPort":8080}]}`;
37
- const result = parseServiceStatus(jsonOutput);
38
- expect(result).toHaveLength(3);
39
- expect(result[0].name).toBe('redis');
40
- expect(result[1].name).toBe('temporal');
41
- expect(result[2].name).toBe('temporal-ui');
42
- });
43
- it('should handle empty health status', () => {
44
- const jsonOutput = '{"Service":"api","State":"running","Health":"","Publishers":[]}';
45
- const result = parseServiceStatus(jsonOutput);
46
- expect(result[0].health).toBe('none');
47
- });
48
- it('should handle missing Publishers array', () => {
49
- const jsonOutput = '{"Service":"worker","State":"running","Health":"healthy"}';
50
- const result = parseServiceStatus(jsonOutput);
51
- expect(result[0].ports).toEqual([]);
52
- });
53
- it('should handle empty output', () => {
54
- const result = parseServiceStatus('');
55
- expect(result).toEqual([]);
56
- });
57
- it('should filter out empty lines', () => {
58
- const jsonOutput = `{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}
59
-
60
- {"Service":"api","State":"running","Health":"","Publishers":[]}
61
- `;
62
- const result = parseServiceStatus(jsonOutput);
63
- expect(result).toHaveLength(2);
64
- });
65
- it('should use Name field as fallback when Service is missing', () => {
66
- const jsonOutput = '{"Name":"output-sdk-redis-1","State":"running","Health":"healthy","Publishers":[]}';
67
- const result = parseServiceStatus(jsonOutput);
68
- expect(result[0].name).toBe('output-sdk-redis-1');
69
- });
70
- });
71
- describe('getServiceStatus', () => {
72
- it('should call docker compose ps with correct arguments', async () => {
73
- const mockOutput = '{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}';
74
- vi.mocked(execFileSync).mockReturnValue(mockOutput);
75
- await getServiceStatus('/path/to/docker-compose.yml');
76
- expect(execFileSync).toHaveBeenCalledWith('docker', ['compose', '-f', '/path/to/docker-compose.yml', 'ps', '--all', '--format', 'json'], expect.objectContaining({ encoding: 'utf-8' }));
77
- });
78
- it('should return parsed service status', async () => {
79
- const mockOutput = '{"Service":"redis","State":"running","Health":"healthy","Publishers":[{"PublishedPort":6379,"TargetPort":6379}]}';
80
- vi.mocked(execFileSync).mockReturnValue(mockOutput);
81
- const result = await getServiceStatus('/path/to/docker-compose.yml');
82
- expect(result).toHaveLength(1);
83
- expect(result[0].name).toBe('redis');
84
- });
85
- it('should throw error when docker compose command fails', async () => {
86
- vi.mocked(execFileSync).mockImplementation(() => {
87
- throw new Error('Docker command failed');
88
- });
89
- await expect(getServiceStatus('/path/to/docker-compose.yml')).rejects.toThrow();
90
- });
91
- });
92
- describe('waitForServicesHealthy', () => {
93
- it('should resolve when all services are healthy', async () => {
94
- const mockOutput = `{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}
95
- {"Service":"temporal","State":"running","Health":"healthy","Publishers":[]}`;
96
- vi.mocked(execFileSync).mockReturnValue(mockOutput);
97
- await expect(waitForServicesHealthy('/path/to/docker-compose.yml', 5000)).resolves.toBeUndefined();
98
- });
99
- it('should resolve when services have no health check (health: none)', async () => {
100
- const mockOutput = `{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}
101
- {"Service":"api","State":"running","Health":"","Publishers":[]}`;
102
- vi.mocked(execFileSync).mockReturnValue(mockOutput);
103
- await expect(waitForServicesHealthy('/path/to/docker-compose.yml', 5000)).resolves.toBeUndefined();
104
- });
105
- it('should timeout when services remain unhealthy', async () => {
106
- const mockOutput = '{"Service":"redis","State":"running","Health":"starting","Publishers":[]}';
107
- vi.mocked(execFileSync).mockReturnValue(mockOutput);
108
- const promise = waitForServicesHealthy('/path/to/docker-compose.yml', 100);
109
- await expect(promise).rejects.toThrow('Timeout waiting for services to become healthy');
110
- }, 10000);
111
- it('should poll multiple times until healthy', async () => {
112
- const callTracker = { count: 0 };
113
- vi.mocked(execFileSync).mockImplementation(() => {
114
- callTracker.count++;
115
- if (callTracker.count < 3) {
116
- return '{"Service":"redis","State":"running","Health":"starting","Publishers":[]}';
117
- }
118
- return '{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}';
119
- });
120
- await waitForServicesHealthy('/path/to/docker-compose.yml', 10000, 50);
121
- expect(callTracker.count).toBeGreaterThanOrEqual(3);
122
- });
123
- });
124
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Interactively configures environment variables for a project by prompting the user
3
- * to provide values for empty variables or variables marked as secrets.
4
- *
5
- * This function reads from .env.example and, when the user confirms configuration,
6
- * copies it to .env before prompting for values. The .env.example file remains
7
- * unchanged as a template for other developers.
8
- *
9
- * @param projectPath - The absolute path to the project directory containing the .env.example file
10
- * @param skipPrompt - If true, copies .env.example to .env without interactive prompts and returns false
11
- * @returns A promise that resolves to true if environment variables were successfully configured,
12
- * false if configuration was skipped (no .env.example file, user declined, no variables to configure,
13
- * or an error occurred)
14
- */
15
- export declare function configureEnvironmentVariables(projectPath: string, skipPrompt?: boolean): Promise<boolean>;