@agents-at-scale/ark 0.1.53 → 0.1.56

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 (141) hide show
  1. package/dist/arkServices.js +14 -0
  2. package/dist/commands/completion/index.js +9 -6
  3. package/dist/commands/export/index.js +6 -5
  4. package/dist/commands/generate/generators/agent.js +2 -0
  5. package/dist/commands/generate/generators/marketplace.js +2 -0
  6. package/dist/commands/generate/generators/mcpserver.js +2 -0
  7. package/dist/commands/generate/generators/project.js +9 -2
  8. package/dist/commands/generate/generators/query.js +2 -0
  9. package/dist/commands/generate/generators/team.js +2 -1
  10. package/dist/commands/generate/templateDiscovery.js +1 -0
  11. package/dist/commands/generate/templateEngine.js +1 -3
  12. package/dist/commands/import/index.js +1 -1
  13. package/dist/commands/install/index.js +35 -31
  14. package/dist/commands/marketplace/index.js +18 -3
  15. package/dist/commands/models/create.js +1 -0
  16. package/dist/commands/models/kubernetes/manifest-builder.js +49 -10
  17. package/dist/commands/models/providers/anthropic.d.ts +15 -0
  18. package/dist/commands/models/providers/anthropic.js +72 -0
  19. package/dist/commands/models/providers/azure.d.ts +10 -7
  20. package/dist/commands/models/providers/azure.js +83 -21
  21. package/dist/commands/models/providers/factory.js +3 -0
  22. package/dist/commands/models/providers/index.d.ts +3 -4
  23. package/dist/commands/models/providers/index.js +1 -0
  24. package/dist/commands/uninstall/index.js +9 -3
  25. package/dist/components/ChatUI.js +21 -33
  26. package/dist/components/statusChecker.js +3 -3
  27. package/dist/index.js +0 -2
  28. package/dist/lib/arkApiClient.d.ts +14 -4
  29. package/dist/lib/arkApiClient.js +61 -42
  30. package/dist/lib/arkApiProxy.js +1 -0
  31. package/dist/lib/arkServiceProxy.js +5 -1
  32. package/dist/lib/chatClient.d.ts +4 -6
  33. package/dist/lib/chatClient.js +138 -95
  34. package/dist/lib/config.js +8 -3
  35. package/dist/lib/errors.d.ts +0 -1
  36. package/dist/lib/errors.js +3 -1
  37. package/dist/lib/marketplaceFetcher.d.ts +1 -0
  38. package/dist/lib/marketplaceFetcher.js +17 -0
  39. package/dist/lib/types.d.ts +0 -38
  40. package/dist/marketplaceServices.d.ts +6 -1
  41. package/dist/marketplaceServices.js +19 -3
  42. package/dist/types/arkService.d.ts +1 -0
  43. package/dist/types/marketplace.d.ts +1 -1
  44. package/dist/ui/asyncOperations/connectingToArk.js +2 -2
  45. package/package.json +19 -13
  46. package/templates/marketplace/marketplace.json.example +2 -2
  47. package/templates/tool/uv.lock +794 -95
  48. package/dist/arkServices.spec.d.ts +0 -1
  49. package/dist/arkServices.spec.js +0 -138
  50. package/dist/commands/agents/index.spec.d.ts +0 -1
  51. package/dist/commands/agents/index.spec.js +0 -67
  52. package/dist/commands/cluster/get.spec.d.ts +0 -1
  53. package/dist/commands/cluster/get.spec.js +0 -92
  54. package/dist/commands/cluster/index.spec.d.ts +0 -1
  55. package/dist/commands/cluster/index.spec.js +0 -24
  56. package/dist/commands/completion/index.spec.d.ts +0 -1
  57. package/dist/commands/completion/index.spec.js +0 -34
  58. package/dist/commands/config/index.spec.d.ts +0 -1
  59. package/dist/commands/config/index.spec.js +0 -78
  60. package/dist/commands/evaluation/index.d.ts +0 -3
  61. package/dist/commands/evaluation/index.js +0 -60
  62. package/dist/commands/evaluation/index.spec.d.ts +0 -1
  63. package/dist/commands/evaluation/index.spec.js +0 -161
  64. package/dist/commands/export/index.spec.d.ts +0 -1
  65. package/dist/commands/export/index.spec.js +0 -145
  66. package/dist/commands/import/index.spec.d.ts +0 -1
  67. package/dist/commands/import/index.spec.js +0 -46
  68. package/dist/commands/install/index.spec.d.ts +0 -1
  69. package/dist/commands/install/index.spec.js +0 -286
  70. package/dist/commands/marketplace/index.spec.d.ts +0 -1
  71. package/dist/commands/marketplace/index.spec.js +0 -88
  72. package/dist/commands/memory/index.spec.d.ts +0 -1
  73. package/dist/commands/memory/index.spec.js +0 -124
  74. package/dist/commands/models/create.spec.d.ts +0 -1
  75. package/dist/commands/models/create.spec.js +0 -167
  76. package/dist/commands/models/index.spec.d.ts +0 -1
  77. package/dist/commands/models/index.spec.js +0 -96
  78. package/dist/commands/models/providers/azure.spec.d.ts +0 -1
  79. package/dist/commands/models/providers/azure.spec.js +0 -232
  80. package/dist/commands/models/providers/bedrock.spec.d.ts +0 -1
  81. package/dist/commands/models/providers/bedrock.spec.js +0 -241
  82. package/dist/commands/models/providers/openai.spec.d.ts +0 -1
  83. package/dist/commands/models/providers/openai.spec.js +0 -180
  84. package/dist/commands/queries/delete.spec.d.ts +0 -1
  85. package/dist/commands/queries/delete.spec.js +0 -74
  86. package/dist/commands/queries/index.spec.d.ts +0 -1
  87. package/dist/commands/queries/index.spec.js +0 -167
  88. package/dist/commands/queries/list.spec.d.ts +0 -1
  89. package/dist/commands/queries/list.spec.js +0 -170
  90. package/dist/commands/queries/validation.spec.d.ts +0 -1
  91. package/dist/commands/queries/validation.spec.js +0 -27
  92. package/dist/commands/query/index.spec.d.ts +0 -1
  93. package/dist/commands/query/index.spec.js +0 -104
  94. package/dist/commands/targets/index.spec.d.ts +0 -1
  95. package/dist/commands/targets/index.spec.js +0 -154
  96. package/dist/commands/teams/index.spec.d.ts +0 -1
  97. package/dist/commands/teams/index.spec.js +0 -70
  98. package/dist/commands/tools/index.spec.d.ts +0 -1
  99. package/dist/commands/tools/index.spec.js +0 -70
  100. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  101. package/dist/commands/uninstall/index.spec.js +0 -125
  102. package/dist/lib/arkServiceProxy.spec.d.ts +0 -1
  103. package/dist/lib/arkServiceProxy.spec.js +0 -100
  104. package/dist/lib/arkStatus.spec.d.ts +0 -1
  105. package/dist/lib/arkStatus.spec.js +0 -49
  106. package/dist/lib/chatClient.spec.d.ts +0 -1
  107. package/dist/lib/chatClient.spec.js +0 -108
  108. package/dist/lib/cluster.spec.d.ts +0 -1
  109. package/dist/lib/cluster.spec.js +0 -338
  110. package/dist/lib/commands.spec.d.ts +0 -1
  111. package/dist/lib/commands.spec.js +0 -146
  112. package/dist/lib/config.spec.d.ts +0 -1
  113. package/dist/lib/config.spec.js +0 -202
  114. package/dist/lib/duration.spec.d.ts +0 -1
  115. package/dist/lib/duration.spec.js +0 -13
  116. package/dist/lib/errors.spec.d.ts +0 -1
  117. package/dist/lib/errors.spec.js +0 -221
  118. package/dist/lib/executeEvaluation.d.ts +0 -16
  119. package/dist/lib/executeEvaluation.js +0 -155
  120. package/dist/lib/executeQuery.spec.d.ts +0 -1
  121. package/dist/lib/executeQuery.spec.js +0 -325
  122. package/dist/lib/kubectl.spec.d.ts +0 -1
  123. package/dist/lib/kubectl.spec.js +0 -192
  124. package/dist/lib/marketplaceFetcher.spec.d.ts +0 -1
  125. package/dist/lib/marketplaceFetcher.spec.js +0 -225
  126. package/dist/lib/nextSteps.spec.d.ts +0 -1
  127. package/dist/lib/nextSteps.spec.js +0 -59
  128. package/dist/lib/output.spec.d.ts +0 -1
  129. package/dist/lib/output.spec.js +0 -123
  130. package/dist/lib/startup.spec.d.ts +0 -1
  131. package/dist/lib/startup.spec.js +0 -152
  132. package/dist/lib/stdin.spec.d.ts +0 -1
  133. package/dist/lib/stdin.spec.js +0 -82
  134. package/dist/lib/timeout.spec.d.ts +0 -1
  135. package/dist/lib/timeout.spec.js +0 -14
  136. package/dist/lib/waitForReady.spec.d.ts +0 -1
  137. package/dist/lib/waitForReady.spec.js +0 -104
  138. package/dist/marketplaceServices.spec.d.ts +0 -1
  139. package/dist/marketplaceServices.spec.js +0 -74
  140. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  141. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -1,202 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import path from 'path';
3
- import os from 'os';
4
- const mockFs = {
5
- existsSync: jest.fn(),
6
- readFileSync: jest.fn(),
7
- };
8
- jest.unstable_mockModule('fs', () => ({
9
- default: mockFs,
10
- ...mockFs,
11
- }));
12
- const mockYaml = {
13
- parse: jest.fn(),
14
- stringify: jest.fn(),
15
- };
16
- jest.unstable_mockModule('yaml', () => ({
17
- default: mockYaml,
18
- ...mockYaml,
19
- }));
20
- const { loadConfig, getConfigPaths, formatConfig } = await import('./config.js');
21
- describe('config', () => {
22
- const originalEnv = process.env;
23
- beforeEach(() => {
24
- jest.clearAllMocks();
25
- process.env = { ...originalEnv };
26
- });
27
- afterEach(() => {
28
- process.env = originalEnv;
29
- });
30
- it('returns default config when no files exist', () => {
31
- mockFs.existsSync.mockReturnValue(false);
32
- const config = loadConfig();
33
- expect(config).toEqual({
34
- chat: {
35
- streaming: true,
36
- outputFormat: 'text',
37
- },
38
- marketplace: {
39
- repoUrl: 'https://github.com/mckinsey/agents-at-scale-marketplace',
40
- registry: 'oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts',
41
- },
42
- services: {
43
- reusePortForwards: false,
44
- },
45
- });
46
- });
47
- it('loads and merges configs in order: defaults, user, project', () => {
48
- mockFs.existsSync.mockReturnValue(true);
49
- mockFs.readFileSync
50
- .mockReturnValueOnce('user yaml')
51
- .mockReturnValueOnce('project yaml');
52
- mockYaml.parse
53
- .mockReturnValueOnce({
54
- chat: {
55
- streaming: false,
56
- outputFormat: 'markdown',
57
- },
58
- })
59
- .mockReturnValueOnce({
60
- chat: {
61
- streaming: true,
62
- },
63
- });
64
- const config = loadConfig();
65
- expect(config.chat?.streaming).toBe(true);
66
- expect(config.chat?.outputFormat).toBe('markdown');
67
- });
68
- it('environment variables override all configs', () => {
69
- mockFs.existsSync.mockReturnValue(false);
70
- process.env.ARK_CHAT_STREAMING = '1';
71
- process.env.ARK_CHAT_OUTPUT_FORMAT = 'MARKDOWN';
72
- const config = loadConfig();
73
- expect(config.chat?.streaming).toBe(true);
74
- expect(config.chat?.outputFormat).toBe('markdown');
75
- });
76
- it('loads queryTimeout from config file', () => {
77
- mockFs.existsSync.mockReturnValue(true);
78
- mockFs.readFileSync.mockReturnValue('yaml');
79
- mockYaml.parse.mockReturnValue({ queryTimeout: '30m' });
80
- const config = loadConfig();
81
- expect(config.queryTimeout).toBe('30m');
82
- });
83
- it('loads defaultExportTypes from config file', () => {
84
- mockFs.existsSync.mockReturnValue(true);
85
- mockFs.readFileSync.mockReturnValue('yaml');
86
- mockYaml.parse.mockReturnValue({ defaultExportTypes: ['agents', 'teams'] });
87
- const config = loadConfig();
88
- expect(config.defaultExportTypes).toEqual(['agents', 'teams']);
89
- });
90
- it('ARK_QUERY_TIMEOUT environment variable overrides config', () => {
91
- mockFs.existsSync.mockReturnValue(true);
92
- mockFs.readFileSync.mockReturnValue('yaml');
93
- mockYaml.parse.mockReturnValue({ queryTimeout: '5m' });
94
- process.env.ARK_QUERY_TIMEOUT = '1h';
95
- const config = loadConfig();
96
- expect(config.queryTimeout).toBe('1h');
97
- });
98
- it('throws error for invalid YAML', () => {
99
- const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
100
- mockFs.existsSync.mockImplementation((path) => path === userConfigPath);
101
- mockFs.readFileSync.mockReturnValue('invalid yaml');
102
- mockYaml.parse.mockImplementation(() => {
103
- throw new Error('YAML parse error');
104
- });
105
- expect(() => loadConfig()).toThrow(`Invalid YAML in ${userConfigPath}: YAML parse error`);
106
- });
107
- it('handles non-Error exceptions', () => {
108
- const userConfigPath = path.join(os.homedir(), '.arkrc.yaml');
109
- mockFs.existsSync.mockImplementation((path) => path === userConfigPath);
110
- mockFs.readFileSync.mockReturnValue('invalid yaml');
111
- mockYaml.parse.mockImplementation(() => {
112
- throw 'string error';
113
- });
114
- expect(() => loadConfig()).toThrow(`Invalid YAML in ${userConfigPath}: Unknown error`);
115
- });
116
- it('getConfigPaths returns correct paths', () => {
117
- const paths = getConfigPaths();
118
- expect(paths.user).toBe(path.join(os.homedir(), '.arkrc.yaml'));
119
- expect(paths.project).toBe(path.join(process.cwd(), '.arkrc.yaml'));
120
- });
121
- it('formatConfig uses yaml.stringify', () => {
122
- const config = { chat: { streaming: true, outputFormat: 'text' } };
123
- mockYaml.stringify.mockReturnValue('formatted');
124
- const result = formatConfig(config);
125
- expect(mockYaml.stringify).toHaveBeenCalledWith(config);
126
- expect(result).toBe('formatted');
127
- });
128
- it('loads marketplace config from config file', () => {
129
- mockFs.existsSync.mockReturnValue(true);
130
- mockFs.readFileSync.mockReturnValue('yaml');
131
- mockYaml.parse.mockReturnValue({
132
- marketplace: {
133
- repoUrl: 'https://example.com/my-marketplace',
134
- registry: 'oci://example.com/charts',
135
- },
136
- });
137
- const config = loadConfig();
138
- expect(config.marketplace?.repoUrl).toBe('https://example.com/my-marketplace');
139
- expect(config.marketplace?.registry).toBe('oci://example.com/charts');
140
- });
141
- it('marketplace environment variables override config', () => {
142
- mockFs.existsSync.mockReturnValue(true);
143
- mockFs.readFileSync.mockReturnValue('yaml');
144
- mockYaml.parse.mockReturnValue({
145
- marketplace: {
146
- repoUrl: 'https://example.com/my-marketplace',
147
- registry: 'oci://example.com/charts',
148
- },
149
- });
150
- process.env.ARK_MARKETPLACE_REPO_URL = 'https://custom.com/marketplace';
151
- process.env.ARK_MARKETPLACE_REGISTRY = 'oci://custom.com/charts';
152
- const config = loadConfig();
153
- expect(config.marketplace?.repoUrl).toBe('https://custom.com/marketplace');
154
- expect(config.marketplace?.registry).toBe('oci://custom.com/charts');
155
- });
156
- });
157
- describe('marketplace helpers', () => {
158
- const originalEnv = process.env;
159
- beforeEach(() => {
160
- jest.clearAllMocks();
161
- process.env = { ...originalEnv };
162
- });
163
- afterEach(() => {
164
- process.env = originalEnv;
165
- });
166
- it('getMarketplaceRepoUrl returns custom config value', async () => {
167
- mockFs.existsSync.mockReturnValue(true);
168
- mockFs.readFileSync.mockReturnValue('yaml');
169
- mockYaml.parse.mockReturnValue({
170
- marketplace: {
171
- repoUrl: 'https://custom-repo.com/marketplace',
172
- },
173
- });
174
- jest.resetModules();
175
- const { getMarketplaceRepoUrl } = await import('./config.js');
176
- expect(getMarketplaceRepoUrl()).toBe('https://custom-repo.com/marketplace');
177
- });
178
- it('getMarketplaceRepoUrl returns default value', async () => {
179
- mockFs.existsSync.mockReturnValue(false);
180
- jest.resetModules();
181
- const { getMarketplaceRepoUrl } = await import('./config.js');
182
- expect(getMarketplaceRepoUrl()).toBe('https://github.com/mckinsey/agents-at-scale-marketplace');
183
- });
184
- it('getMarketplaceRegistry returns custom config value', async () => {
185
- mockFs.existsSync.mockReturnValue(true);
186
- mockFs.readFileSync.mockReturnValue('yaml');
187
- mockYaml.parse.mockReturnValue({
188
- marketplace: {
189
- registry: 'oci://custom-registry.com/charts',
190
- },
191
- });
192
- jest.resetModules();
193
- const { getMarketplaceRegistry } = await import('./config.js');
194
- expect(getMarketplaceRegistry()).toBe('oci://custom-registry.com/charts');
195
- });
196
- it('getMarketplaceRegistry returns default value', async () => {
197
- mockFs.existsSync.mockReturnValue(false);
198
- jest.resetModules();
199
- const { getMarketplaceRegistry } = await import('./config.js');
200
- expect(getMarketplaceRegistry()).toBe('oci://ghcr.io/mckinsey/agents-at-scale-marketplace/charts');
201
- });
202
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { parseDuration } from './duration.js';
2
- describe('parseDuration', () => {
3
- it('should parse durations correctly', () => {
4
- expect(parseDuration('100ms')).toBe(100);
5
- expect(parseDuration('30s')).toBe(30000);
6
- expect(parseDuration('5m')).toBe(300000);
7
- expect(parseDuration('1h')).toBe(3600000);
8
- });
9
- it('should throw on invalid format', () => {
10
- expect(() => parseDuration('invalid')).toThrow('Invalid duration format');
11
- expect(() => parseDuration('10')).toThrow('Invalid duration format');
12
- });
13
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,221 +0,0 @@
1
- import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
2
- import { ErrorCode, ArkError, ValidationError, TemplateError, ProjectStructureError, ErrorHandler, InputValidator, } from './errors.js';
3
- jest.mock('fs');
4
- describe('Error Classes', () => {
5
- describe('ArkError', () => {
6
- it('creates error with default code', () => {
7
- const error = new ArkError('test message');
8
- expect(error.message).toBe('test message');
9
- expect(error.code).toBe(ErrorCode.UNKNOWN_ERROR);
10
- expect(error.name).toBe('ArkError');
11
- expect(error.details).toBeUndefined();
12
- expect(error.suggestions).toBeUndefined();
13
- });
14
- it('creates error with all properties', () => {
15
- const error = new ArkError('test error', ErrorCode.INVALID_INPUT, { field: 'name' }, ['Check the input', 'Try again']);
16
- expect(error.message).toBe('test error');
17
- expect(error.code).toBe(ErrorCode.INVALID_INPUT);
18
- expect(error.details).toEqual({ field: 'name' });
19
- expect(error.suggestions).toEqual(['Check the input', 'Try again']);
20
- });
21
- });
22
- describe('ValidationError', () => {
23
- it('creates validation error without field', () => {
24
- const error = new ValidationError('validation failed');
25
- expect(error.message).toBe('validation failed');
26
- expect(error.code).toBe(ErrorCode.VALIDATION_ERROR);
27
- expect(error.name).toBe('ValidationError');
28
- expect(error.details).toBeUndefined();
29
- });
30
- it('creates validation error with field and suggestions', () => {
31
- const error = new ValidationError('invalid email', 'email', [
32
- 'Use valid format',
33
- ]);
34
- expect(error.message).toBe('invalid email');
35
- expect(error.code).toBe(ErrorCode.VALIDATION_ERROR);
36
- expect(error.details).toEqual({ field: 'email' });
37
- expect(error.suggestions).toEqual(['Use valid format']);
38
- });
39
- });
40
- describe('TemplateError', () => {
41
- it('creates template error', () => {
42
- const error = new TemplateError('template failed', 'template.yaml');
43
- expect(error.message).toBe('template failed');
44
- expect(error.code).toBe(ErrorCode.TEMPLATE_ERROR);
45
- expect(error.name).toBe('TemplateError');
46
- expect(error.details).toEqual({ templatePath: 'template.yaml' });
47
- });
48
- });
49
- describe('ProjectStructureError', () => {
50
- it('creates project structure error with defaults', () => {
51
- const error = new ProjectStructureError('project invalid');
52
- expect(error.message).toBe('project invalid');
53
- expect(error.code).toBe(ErrorCode.PROJECT_STRUCTURE_INVALID);
54
- expect(error.name).toBe('ProjectStructureError');
55
- expect(error.suggestions).toEqual([
56
- 'Ensure you are in a valid ARK project directory',
57
- 'Run "ark generate project" to create a new project',
58
- 'Check that Chart.yaml and agents/ directory exist',
59
- ]);
60
- });
61
- it('creates project structure error with path', () => {
62
- const error = new ProjectStructureError('project invalid', '/path/to/project');
63
- expect(error.message).toBe('project invalid');
64
- expect(error.code).toBe(ErrorCode.PROJECT_STRUCTURE_INVALID);
65
- expect(error.details).toEqual({ projectPath: '/path/to/project' });
66
- });
67
- });
68
- describe('ErrorHandler', () => {
69
- it('formats basic error', () => {
70
- const error = new Error('simple error');
71
- const formatted = ErrorHandler.formatError(error);
72
- expect(formatted).toContain('❌ simple error');
73
- });
74
- it('formats ArkError with details and suggestions', () => {
75
- const error = new ArkError('test error', ErrorCode.INVALID_INPUT, { field: 'name', value: 'test' }, ['Fix the input', 'Try again']);
76
- const formatted = ErrorHandler.formatError(error);
77
- expect(formatted).toContain('❌ test error');
78
- expect(formatted).toContain('Details:');
79
- expect(formatted).toContain('field: name');
80
- expect(formatted).toContain('value: test');
81
- expect(formatted).toContain('💡 Suggestions:');
82
- expect(formatted).toContain('• Fix the input');
83
- expect(formatted).toContain('• Try again');
84
- });
85
- it('includes stack trace in debug mode', () => {
86
- process.env.DEBUG = 'true';
87
- const error = new Error('debug error');
88
- const formatted = ErrorHandler.formatError(error);
89
- expect(formatted).toContain('Stack trace:');
90
- delete process.env.DEBUG;
91
- });
92
- it('handles missing stack trace', () => {
93
- process.env.NODE_ENV = 'development';
94
- const error = new Error('no stack');
95
- error.stack = undefined;
96
- const formatted = ErrorHandler.formatError(error);
97
- expect(formatted).toContain('No stack trace available');
98
- delete process.env.NODE_ENV;
99
- });
100
- describe('handleAndExit', () => {
101
- let mockExit;
102
- let mockConsoleError;
103
- beforeEach(() => {
104
- mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
105
- throw new Error('process.exit');
106
- });
107
- mockConsoleError = jest
108
- .spyOn(console, 'error')
109
- .mockImplementation(() => { });
110
- });
111
- afterEach(() => {
112
- mockExit.mockRestore();
113
- mockConsoleError.mockRestore();
114
- });
115
- it('exits with code 22 for validation errors', () => {
116
- const error = new ValidationError('invalid');
117
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
118
- expect(mockExit).toHaveBeenCalledWith(22);
119
- });
120
- it('exits with code 2 for file not found', () => {
121
- const error = new ArkError('not found', ErrorCode.FILE_NOT_FOUND);
122
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
123
- expect(mockExit).toHaveBeenCalledWith(2);
124
- });
125
- it('exits with code 13 for permission denied', () => {
126
- const error = new ArkError('denied', ErrorCode.PERMISSION_DENIED);
127
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
128
- expect(mockExit).toHaveBeenCalledWith(13);
129
- });
130
- it('exits with code 127 for missing dependency', () => {
131
- const error = new ArkError('missing', ErrorCode.DEPENDENCY_MISSING);
132
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
133
- expect(mockExit).toHaveBeenCalledWith(127);
134
- });
135
- it('exits with code 1 for unknown errors', () => {
136
- const error = new Error('unknown');
137
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
138
- expect(mockExit).toHaveBeenCalledWith(1);
139
- });
140
- it('exits with code 1 for default ArkError', () => {
141
- const error = new ArkError('general', ErrorCode.UNKNOWN_ERROR);
142
- expect(() => ErrorHandler.handleAndExit(error)).toThrow('process.exit');
143
- expect(mockExit).toHaveBeenCalledWith(1);
144
- });
145
- });
146
- describe('catchAndHandle', () => {
147
- it('returns successful promise result', async () => {
148
- const result = await ErrorHandler.catchAndHandle(async () => 'success');
149
- expect(result).toBe('success');
150
- });
151
- it('rethrows ArkError unchanged', async () => {
152
- const arkError = new ValidationError('test');
153
- await expect(ErrorHandler.catchAndHandle(async () => {
154
- throw arkError;
155
- })).rejects.toThrow(arkError);
156
- });
157
- it('wraps generic errors with context', async () => {
158
- const error = new Error('generic');
159
- await expect(ErrorHandler.catchAndHandle(async () => {
160
- throw error;
161
- }, 'context')).rejects.toThrow('context: generic');
162
- });
163
- it('wraps non-Error objects', async () => {
164
- await expect(ErrorHandler.catchAndHandle(async () => {
165
- throw 'string error';
166
- })).rejects.toThrow('string error');
167
- });
168
- });
169
- });
170
- describe('InputValidator', () => {
171
- describe('validateName', () => {
172
- it('accepts valid names', () => {
173
- expect(() => InputValidator.validateName('valid-name')).not.toThrow();
174
- expect(() => InputValidator.validateName('test123')).not.toThrow();
175
- expect(() => InputValidator.validateName('a-b-c-123')).not.toThrow();
176
- });
177
- it('rejects empty names', () => {
178
- expect(() => InputValidator.validateName('')).toThrow('name cannot be empty');
179
- expect(() => InputValidator.validateName(' ')).toThrow('name cannot be empty');
180
- });
181
- it('rejects names over 63 characters', () => {
182
- const longName = 'a'.repeat(64);
183
- expect(() => InputValidator.validateName(longName)).toThrow('must be 63 characters or less');
184
- });
185
- it('rejects invalid characters', () => {
186
- expect(() => InputValidator.validateName('Invalid Name')).toThrow('Invalid name');
187
- expect(() => InputValidator.validateName('test_name')).toThrow('Invalid name');
188
- expect(() => InputValidator.validateName('-start')).toThrow('Invalid name');
189
- expect(() => InputValidator.validateName('end-')).toThrow('Invalid name');
190
- });
191
- it('suggests normalized names', () => {
192
- try {
193
- InputValidator.validateName('TestName');
194
- }
195
- catch (e) {
196
- expect(e.suggestions).toContain('Try: "test-name"');
197
- }
198
- });
199
- });
200
- describe('validatePath', () => {
201
- it('accepts valid paths', () => {
202
- expect(() => InputValidator.validatePath('/valid/path')).not.toThrow();
203
- expect(() => InputValidator.validatePath('./relative')).not.toThrow();
204
- expect(() => InputValidator.validatePath('simple')).not.toThrow();
205
- });
206
- it('rejects empty paths', () => {
207
- expect(() => InputValidator.validatePath('')).toThrow('path cannot be empty');
208
- });
209
- it('rejects dangerous paths', () => {
210
- expect(() => InputValidator.validatePath('../parent')).toThrow('unsafe characters');
211
- expect(() => InputValidator.validatePath('~/home')).toThrow('unsafe characters');
212
- expect(() => InputValidator.validatePath('$HOME/test')).toThrow('unsafe characters');
213
- });
214
- });
215
- describe('validateDirectory', () => {
216
- it('validates path first', () => {
217
- expect(() => InputValidator.validateDirectory('')).toThrow('directory cannot be empty');
218
- });
219
- });
220
- });
221
- });
@@ -1,16 +0,0 @@
1
- export interface DirectEvaluationOptions {
2
- evaluatorName: string;
3
- input: string;
4
- output: string;
5
- timeout?: string;
6
- watchTimeout?: string;
7
- }
8
- export interface QueryEvaluationOptions {
9
- evaluatorName: string;
10
- queryName: string;
11
- responseTarget?: string;
12
- timeout?: string;
13
- watchTimeout?: string;
14
- }
15
- export declare function executeDirectEvaluation(options: DirectEvaluationOptions): Promise<void>;
16
- export declare function executeQueryEvaluation(options: QueryEvaluationOptions): Promise<void>;
@@ -1,155 +0,0 @@
1
- import { execa } from 'execa';
2
- import ora from 'ora';
3
- import chalk from 'chalk';
4
- import output from './output.js';
5
- import { ExitCodes } from './errors.js';
6
- import { parseDuration } from './duration.js';
7
- async function waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay) {
8
- const spinner = ora('Waiting for evaluation completion...').start();
9
- try {
10
- await execa('kubectl', [
11
- 'wait',
12
- '--for=condition=Completed',
13
- `evaluation/${evaluationName}`,
14
- `--timeout=${Math.floor(watchTimeoutMs / 1000)}s`,
15
- ], { timeout: watchTimeoutMs });
16
- }
17
- catch (error) {
18
- spinner.stop();
19
- if (error instanceof Error && error.message.includes('timed out waiting')) {
20
- console.error(chalk.red(`Evaluation did not complete within ${watchTimeoutDisplay}`));
21
- process.exit(ExitCodes.Timeout);
22
- }
23
- throw error;
24
- }
25
- spinner.stop();
26
- const { stdout } = await execa('kubectl', ['get', 'evaluation', evaluationName, '-o', 'json'], { stdio: 'pipe' });
27
- const evaluation = JSON.parse(stdout);
28
- const status = evaluation.status;
29
- if (status?.phase === 'done') {
30
- console.log(chalk.green('\nEvaluation completed successfully:'));
31
- if (status.score !== undefined) {
32
- console.log(`Score: ${status.score}`);
33
- }
34
- if (status.passed !== undefined) {
35
- console.log(`Result: ${status.passed ? chalk.green('PASSED') : chalk.red('FAILED')}`);
36
- }
37
- if (status.message) {
38
- console.log(`Message: ${status.message}`);
39
- }
40
- }
41
- else if (status?.phase === 'error') {
42
- console.error(chalk.red(status.message || 'Evaluation failed with unknown error'));
43
- process.exit(ExitCodes.OperationError);
44
- }
45
- else {
46
- output.warning(`Unexpected evaluation phase: ${status?.phase}`);
47
- }
48
- }
49
- export async function executeDirectEvaluation(options) {
50
- const spinner = ora('Creating evaluation...').start();
51
- const queryTimeoutMs = options.timeout
52
- ? parseDuration(options.timeout)
53
- : parseDuration('5m');
54
- const watchTimeoutMs = options.watchTimeout
55
- ? parseDuration(options.watchTimeout)
56
- : queryTimeoutMs + 60000;
57
- const timestamp = Date.now();
58
- const evaluationName = `cli-eval-${timestamp}`;
59
- const evaluationManifest = {
60
- apiVersion: 'ark.mckinsey.com/v1alpha1',
61
- kind: 'Evaluation',
62
- metadata: {
63
- name: evaluationName,
64
- },
65
- spec: {
66
- type: 'direct',
67
- evaluator: {
68
- name: options.evaluatorName,
69
- },
70
- config: {
71
- input: options.input,
72
- output: options.output,
73
- },
74
- ...(options.timeout && { timeout: options.timeout }),
75
- ttl: '1h',
76
- },
77
- };
78
- try {
79
- spinner.text = 'Submitting evaluation...';
80
- await execa('kubectl', ['apply', '-f', '-'], {
81
- input: JSON.stringify(evaluationManifest),
82
- stdio: ['pipe', 'pipe', 'pipe'],
83
- });
84
- spinner.stop();
85
- const watchTimeoutDisplay = options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`;
86
- await waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay);
87
- }
88
- catch (error) {
89
- spinner.stop();
90
- console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
91
- process.exit(ExitCodes.CliError);
92
- }
93
- }
94
- export async function executeQueryEvaluation(options) {
95
- const spinner = ora('Creating evaluation...').start();
96
- const queryTimeoutMs = options.timeout
97
- ? parseDuration(options.timeout)
98
- : parseDuration('5m');
99
- const watchTimeoutMs = options.watchTimeout
100
- ? parseDuration(options.watchTimeout)
101
- : queryTimeoutMs + 60000;
102
- const timestamp = Date.now();
103
- const evaluationName = `cli-eval-${timestamp}`;
104
- let responseTarget;
105
- if (options.responseTarget) {
106
- const parts = options.responseTarget.split(':');
107
- if (parts.length === 2) {
108
- responseTarget = {
109
- type: parts[0],
110
- name: parts[1],
111
- };
112
- }
113
- else {
114
- spinner.stop();
115
- console.error(chalk.red('Invalid response-target format. Use: type:name (e.g., agent:my-agent)'));
116
- process.exit(ExitCodes.CliError);
117
- }
118
- }
119
- const evaluationManifest = {
120
- apiVersion: 'ark.mckinsey.com/v1alpha1',
121
- kind: 'Evaluation',
122
- metadata: {
123
- name: evaluationName,
124
- },
125
- spec: {
126
- type: 'query',
127
- evaluator: {
128
- name: options.evaluatorName,
129
- },
130
- config: {
131
- queryRef: {
132
- name: options.queryName,
133
- },
134
- ...(responseTarget && { responseTarget }),
135
- },
136
- ...(options.timeout && { timeout: options.timeout }),
137
- ttl: '1h',
138
- },
139
- };
140
- try {
141
- spinner.text = 'Submitting evaluation...';
142
- await execa('kubectl', ['apply', '-f', '-'], {
143
- input: JSON.stringify(evaluationManifest),
144
- stdio: ['pipe', 'pipe', 'pipe'],
145
- });
146
- spinner.stop();
147
- const watchTimeoutDisplay = options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`;
148
- await waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay);
149
- }
150
- catch (error) {
151
- spinner.stop();
152
- console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
153
- process.exit(ExitCodes.CliError);
154
- }
155
- }
@@ -1 +0,0 @@
1
- export {};