@agents-at-scale/ark 0.1.35-rc1 → 0.1.35

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 (207) hide show
  1. package/dist/commands/cluster/index.d.ts +1 -2
  2. package/dist/commands/cluster/index.js +5 -3
  3. package/dist/commands/completion.js +2 -159
  4. package/dist/commands/config.d.ts +3 -0
  5. package/dist/commands/config.js +321 -38
  6. package/dist/commands/generate/config.js +24 -5
  7. package/dist/commands/generate/generators/agent.js +2 -2
  8. package/dist/commands/generate/generators/mcpserver.d.ts +1 -2
  9. package/dist/commands/generate/generators/mcpserver.js +5 -26
  10. package/dist/commands/generate/generators/project.js +41 -22
  11. package/dist/commands/generate/generators/team.js +2 -2
  12. package/dist/commands/generate/index.d.ts +1 -2
  13. package/dist/commands/generate/index.js +1 -1
  14. package/dist/components/statusChecker.d.ts +23 -13
  15. package/dist/components/statusChecker.js +129 -275
  16. package/dist/config.d.ts +22 -3
  17. package/dist/config.js +161 -10
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +42 -40
  20. package/dist/lib/cluster.d.ts +1 -2
  21. package/dist/lib/cluster.js +16 -37
  22. package/dist/lib/config.d.ts +80 -26
  23. package/dist/lib/config.js +205 -70
  24. package/dist/lib/consts.d.ts +1 -0
  25. package/dist/lib/consts.js +2 -0
  26. package/dist/lib/errors.js +1 -1
  27. package/dist/lib/exec.d.ts +4 -0
  28. package/dist/lib/exec.js +11 -0
  29. package/dist/lib/types.d.ts +3 -10
  30. package/dist/ui/MainMenu.d.ts +1 -5
  31. package/dist/ui/MainMenu.js +91 -222
  32. package/dist/ui/statusFormatter.d.ts +7 -22
  33. package/dist/ui/statusFormatter.js +39 -39
  34. package/package.json +5 -17
  35. package/dist/arkServices.d.ts +0 -42
  36. package/dist/arkServices.js +0 -138
  37. package/dist/arkServices.spec.d.ts +0 -1
  38. package/dist/arkServices.spec.js +0 -24
  39. package/dist/charts/charts.d.ts +0 -5
  40. package/dist/charts/charts.js +0 -6
  41. package/dist/charts/dependencies.d.ts +0 -6
  42. package/dist/charts/dependencies.js +0 -50
  43. package/dist/charts/types.d.ts +0 -40
  44. package/dist/charts/types.js +0 -1
  45. package/dist/commands/agents/index.d.ts +0 -3
  46. package/dist/commands/agents/index.js +0 -51
  47. package/dist/commands/agents/index.spec.d.ts +0 -1
  48. package/dist/commands/agents/index.spec.js +0 -67
  49. package/dist/commands/agents/selector.d.ts +0 -8
  50. package/dist/commands/agents/selector.js +0 -53
  51. package/dist/commands/agents.d.ts +0 -2
  52. package/dist/commands/agents.js +0 -53
  53. package/dist/commands/chat/index.d.ts +0 -3
  54. package/dist/commands/chat/index.js +0 -29
  55. package/dist/commands/chat.d.ts +0 -2
  56. package/dist/commands/chat.js +0 -45
  57. package/dist/commands/cluster/get.d.ts +0 -2
  58. package/dist/commands/cluster/get.js +0 -39
  59. package/dist/commands/cluster/get.spec.d.ts +0 -1
  60. package/dist/commands/cluster/get.spec.js +0 -92
  61. package/dist/commands/cluster/index.spec.d.ts +0 -1
  62. package/dist/commands/cluster/index.spec.js +0 -24
  63. package/dist/commands/completion/index.d.ts +0 -3
  64. package/dist/commands/completion/index.js +0 -268
  65. package/dist/commands/completion/index.spec.d.ts +0 -1
  66. package/dist/commands/completion/index.spec.js +0 -34
  67. package/dist/commands/config/index.d.ts +0 -3
  68. package/dist/commands/config/index.js +0 -42
  69. package/dist/commands/config/index.spec.d.ts +0 -1
  70. package/dist/commands/config/index.spec.js +0 -78
  71. package/dist/commands/dashboard/index.d.ts +0 -4
  72. package/dist/commands/dashboard/index.js +0 -39
  73. package/dist/commands/dashboard.d.ts +0 -3
  74. package/dist/commands/dashboard.js +0 -39
  75. package/dist/commands/dev/index.d.ts +0 -3
  76. package/dist/commands/dev/index.js +0 -9
  77. package/dist/commands/dev/tool/check.d.ts +0 -2
  78. package/dist/commands/dev/tool/check.js +0 -142
  79. package/dist/commands/dev/tool/clean.d.ts +0 -2
  80. package/dist/commands/dev/tool/clean.js +0 -153
  81. package/dist/commands/dev/tool/generate.d.ts +0 -2
  82. package/dist/commands/dev/tool/generate.js +0 -28
  83. package/dist/commands/dev/tool/index.d.ts +0 -2
  84. package/dist/commands/dev/tool/index.js +0 -14
  85. package/dist/commands/dev/tool/init.d.ts +0 -2
  86. package/dist/commands/dev/tool/init.js +0 -320
  87. package/dist/commands/dev/tool/shared.d.ts +0 -5
  88. package/dist/commands/dev/tool/shared.js +0 -256
  89. package/dist/commands/dev/tool/status.d.ts +0 -2
  90. package/dist/commands/dev/tool/status.js +0 -136
  91. package/dist/commands/dev/tool-generate.spec.d.ts +0 -1
  92. package/dist/commands/dev/tool-generate.spec.js +0 -163
  93. package/dist/commands/dev/tool.d.ts +0 -2
  94. package/dist/commands/dev/tool.js +0 -559
  95. package/dist/commands/dev/tool.spec.d.ts +0 -1
  96. package/dist/commands/dev/tool.spec.js +0 -48
  97. package/dist/commands/install/index.d.ts +0 -8
  98. package/dist/commands/install/index.js +0 -302
  99. package/dist/commands/install/index.spec.d.ts +0 -1
  100. package/dist/commands/install/index.spec.js +0 -135
  101. package/dist/commands/install.d.ts +0 -3
  102. package/dist/commands/install.js +0 -147
  103. package/dist/commands/models/create.d.ts +0 -1
  104. package/dist/commands/models/create.js +0 -213
  105. package/dist/commands/models/create.spec.d.ts +0 -1
  106. package/dist/commands/models/create.spec.js +0 -125
  107. package/dist/commands/models/index.d.ts +0 -3
  108. package/dist/commands/models/index.js +0 -60
  109. package/dist/commands/models/index.spec.d.ts +0 -1
  110. package/dist/commands/models/index.spec.js +0 -76
  111. package/dist/commands/models/selector.d.ts +0 -8
  112. package/dist/commands/models/selector.js +0 -53
  113. package/dist/commands/routes/index.d.ts +0 -3
  114. package/dist/commands/routes/index.js +0 -93
  115. package/dist/commands/routes.d.ts +0 -2
  116. package/dist/commands/routes.js +0 -101
  117. package/dist/commands/status/index.d.ts +0 -4
  118. package/dist/commands/status/index.js +0 -232
  119. package/dist/commands/status.d.ts +0 -3
  120. package/dist/commands/status.js +0 -33
  121. package/dist/commands/targets/index.d.ts +0 -3
  122. package/dist/commands/targets/index.js +0 -65
  123. package/dist/commands/targets/index.spec.d.ts +0 -1
  124. package/dist/commands/targets/index.spec.js +0 -105
  125. package/dist/commands/targets.d.ts +0 -2
  126. package/dist/commands/targets.js +0 -65
  127. package/dist/commands/teams/index.d.ts +0 -3
  128. package/dist/commands/teams/index.js +0 -49
  129. package/dist/commands/teams/index.spec.d.ts +0 -1
  130. package/dist/commands/teams/index.spec.js +0 -70
  131. package/dist/commands/teams/selector.d.ts +0 -8
  132. package/dist/commands/teams/selector.js +0 -55
  133. package/dist/commands/tools/index.d.ts +0 -3
  134. package/dist/commands/tools/index.js +0 -49
  135. package/dist/commands/tools/index.spec.d.ts +0 -1
  136. package/dist/commands/tools/index.spec.js +0 -70
  137. package/dist/commands/tools/selector.d.ts +0 -8
  138. package/dist/commands/tools/selector.js +0 -53
  139. package/dist/commands/uninstall/index.d.ts +0 -3
  140. package/dist/commands/uninstall/index.js +0 -107
  141. package/dist/commands/uninstall/index.spec.d.ts +0 -1
  142. package/dist/commands/uninstall/index.spec.js +0 -117
  143. package/dist/commands/uninstall.d.ts +0 -2
  144. package/dist/commands/uninstall.js +0 -83
  145. package/dist/components/ChatUI.d.ts +0 -16
  146. package/dist/components/ChatUI.js +0 -801
  147. package/dist/components/StatusView.d.ts +0 -10
  148. package/dist/components/StatusView.js +0 -39
  149. package/dist/lib/arkApiClient.d.ts +0 -53
  150. package/dist/lib/arkApiClient.js +0 -102
  151. package/dist/lib/arkApiProxy.d.ts +0 -9
  152. package/dist/lib/arkApiProxy.js +0 -22
  153. package/dist/lib/arkServiceProxy.d.ts +0 -14
  154. package/dist/lib/arkServiceProxy.js +0 -95
  155. package/dist/lib/arkStatus.d.ts +0 -10
  156. package/dist/lib/arkStatus.js +0 -79
  157. package/dist/lib/arkStatus.spec.d.ts +0 -1
  158. package/dist/lib/arkStatus.spec.js +0 -49
  159. package/dist/lib/chatClient.d.ts +0 -33
  160. package/dist/lib/chatClient.js +0 -99
  161. package/dist/lib/cluster.spec.d.ts +0 -1
  162. package/dist/lib/cluster.spec.js +0 -338
  163. package/dist/lib/commandUtils.d.ts +0 -4
  164. package/dist/lib/commandUtils.js +0 -18
  165. package/dist/lib/commandUtils.test.d.ts +0 -1
  166. package/dist/lib/commandUtils.test.js +0 -44
  167. package/dist/lib/commands.d.ts +0 -16
  168. package/dist/lib/commands.js +0 -29
  169. package/dist/lib/commands.spec.d.ts +0 -1
  170. package/dist/lib/commands.spec.js +0 -146
  171. package/dist/lib/config.spec.d.ts +0 -1
  172. package/dist/lib/config.spec.js +0 -99
  173. package/dist/lib/config.test.d.ts +0 -1
  174. package/dist/lib/config.test.js +0 -93
  175. package/dist/lib/consts.spec.d.ts +0 -1
  176. package/dist/lib/consts.spec.js +0 -15
  177. package/dist/lib/dev/tools/analyzer.d.ts +0 -30
  178. package/dist/lib/dev/tools/analyzer.js +0 -190
  179. package/dist/lib/dev/tools/discover_tools.py +0 -392
  180. package/dist/lib/dev/tools/mcp-types.d.ts +0 -28
  181. package/dist/lib/dev/tools/mcp-types.js +0 -86
  182. package/dist/lib/dev/tools/types.d.ts +0 -50
  183. package/dist/lib/dev/tools/types.js +0 -1
  184. package/dist/lib/errors.spec.d.ts +0 -1
  185. package/dist/lib/errors.spec.js +0 -221
  186. package/dist/lib/output.d.ts +0 -36
  187. package/dist/lib/output.js +0 -89
  188. package/dist/lib/output.spec.d.ts +0 -1
  189. package/dist/lib/output.spec.js +0 -123
  190. package/dist/lib/portUtils.d.ts +0 -8
  191. package/dist/lib/portUtils.js +0 -39
  192. package/dist/lib/startup.d.ts +0 -5
  193. package/dist/lib/startup.js +0 -73
  194. package/dist/lib/startup.spec.d.ts +0 -1
  195. package/dist/lib/startup.spec.js +0 -168
  196. package/dist/types/types.d.ts +0 -40
  197. package/dist/types/types.js +0 -1
  198. package/dist/ui/AgentSelector.d.ts +0 -8
  199. package/dist/ui/AgentSelector.js +0 -53
  200. package/dist/ui/ModelSelector.d.ts +0 -8
  201. package/dist/ui/ModelSelector.js +0 -53
  202. package/dist/ui/TeamSelector.d.ts +0 -8
  203. package/dist/ui/TeamSelector.js +0 -55
  204. package/dist/ui/ToolSelector.d.ts +0 -8
  205. package/dist/ui/ToolSelector.js +0 -53
  206. package/dist/ui/statusFormatter.spec.d.ts +0 -1
  207. package/dist/ui/statusFormatter.spec.js +0 -58
@@ -1,213 +0,0 @@
1
- import { execa } from 'execa';
2
- import inquirer from 'inquirer';
3
- import output from '../../lib/output.js';
4
- export async function createModel(modelName) {
5
- // Step 1: Get model name if not provided
6
- if (!modelName) {
7
- const nameAnswer = await inquirer.prompt([
8
- {
9
- type: 'input',
10
- name: 'modelName',
11
- message: 'model name:',
12
- default: 'default',
13
- validate: (input) => {
14
- if (!input)
15
- return 'model name is required';
16
- // Kubernetes name validation
17
- if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(input)) {
18
- return 'model name must be a valid Kubernetes resource name';
19
- }
20
- return true;
21
- },
22
- },
23
- ]);
24
- modelName = nameAnswer.modelName;
25
- }
26
- // Check if model already exists
27
- try {
28
- await execa('kubectl', ['get', 'model', modelName], { stdio: 'pipe' });
29
- output.warning(`model ${modelName} already exists`);
30
- const { overwrite } = await inquirer.prompt([
31
- {
32
- type: 'confirm',
33
- name: 'overwrite',
34
- message: `overwrite existing model ${modelName}?`,
35
- default: false,
36
- },
37
- ]);
38
- if (!overwrite) {
39
- output.info('model creation cancelled');
40
- return false;
41
- }
42
- }
43
- catch {
44
- // Model doesn't exist, continue
45
- }
46
- // Step 2: Choose model type
47
- const { modelType } = await inquirer.prompt([
48
- {
49
- type: 'list',
50
- name: 'modelType',
51
- message: 'select model provider:',
52
- choices: [
53
- { name: 'Azure OpenAI', value: 'azure' },
54
- { name: 'OpenAI', value: 'openai' },
55
- ],
56
- default: 'azure',
57
- },
58
- ]);
59
- // Step 3: Get common parameters
60
- const commonAnswers = await inquirer.prompt([
61
- {
62
- type: 'input',
63
- name: 'modelVersion',
64
- message: 'model version:',
65
- default: 'gpt-4o-mini',
66
- },
67
- {
68
- type: 'input',
69
- name: 'baseUrl',
70
- message: 'base URL:',
71
- validate: (input) => {
72
- if (!input)
73
- return 'base URL is required';
74
- try {
75
- new URL(input);
76
- return true;
77
- }
78
- catch {
79
- return 'please enter a valid URL';
80
- }
81
- },
82
- },
83
- ]);
84
- // Remove trailing slash from base URL
85
- const baseUrl = commonAnswers.baseUrl.replace(/\/$/, '');
86
- // Step 4: Get provider-specific parameters
87
- let apiVersion = '';
88
- if (modelType === 'azure') {
89
- const azureAnswers = await inquirer.prompt([
90
- {
91
- type: 'input',
92
- name: 'apiVersion',
93
- message: 'Azure API version:',
94
- default: '2024-12-01-preview',
95
- },
96
- ]);
97
- apiVersion = azureAnswers.apiVersion;
98
- }
99
- // Step 5: Get API key (password input)
100
- const { apiKey } = await inquirer.prompt([
101
- {
102
- type: 'password',
103
- name: 'apiKey',
104
- message: 'API key:',
105
- mask: '*',
106
- validate: (input) => {
107
- if (!input)
108
- return 'API key is required';
109
- return true;
110
- },
111
- },
112
- ]);
113
- // Step 6: Create the Kubernetes secret
114
- const secretName = `${modelName}-model-api-key`;
115
- output.info(`creating secret ${secretName}...`);
116
- try {
117
- // Delete existing secret if it exists (update scenario)
118
- await execa('kubectl', ['delete', 'secret', secretName], {
119
- stdio: 'pipe',
120
- }).catch(() => {
121
- // Ignore error if secret doesn't exist
122
- });
123
- // Create the secret
124
- await execa('kubectl', [
125
- 'create',
126
- 'secret',
127
- 'generic',
128
- secretName,
129
- `--from-literal=api-key=${apiKey}`,
130
- ], { stdio: 'pipe' });
131
- output.success(`secret ${secretName} created`);
132
- }
133
- catch (error) {
134
- output.error('failed to create secret');
135
- console.error(error);
136
- return false;
137
- }
138
- // Step 7: Create the Model resource
139
- output.info(`creating model ${modelName}...`);
140
- const modelManifest = {
141
- apiVersion: 'ark.mckinsey.com/v1alpha1',
142
- kind: 'Model',
143
- metadata: {
144
- name: modelName,
145
- },
146
- spec: {
147
- type: modelType,
148
- model: {
149
- value: commonAnswers.modelVersion,
150
- },
151
- config: {},
152
- },
153
- };
154
- // Add provider-specific config
155
- if (modelType === 'azure') {
156
- modelManifest.spec.config.azure = {
157
- apiKey: {
158
- valueFrom: {
159
- secretKeyRef: {
160
- name: secretName,
161
- key: 'api-key',
162
- },
163
- },
164
- },
165
- baseUrl: {
166
- value: baseUrl,
167
- },
168
- apiVersion: {
169
- value: apiVersion,
170
- },
171
- };
172
- }
173
- else {
174
- modelManifest.spec.config.openai = {
175
- apiKey: {
176
- valueFrom: {
177
- secretKeyRef: {
178
- name: secretName,
179
- key: 'api-key',
180
- },
181
- },
182
- },
183
- baseUrl: {
184
- value: baseUrl,
185
- },
186
- };
187
- }
188
- try {
189
- // Apply the model manifest using kubectl
190
- const manifestJson = JSON.stringify(modelManifest);
191
- await execa('kubectl', ['apply', '-f', '-'], {
192
- input: manifestJson,
193
- stdio: ['pipe', 'pipe', 'pipe'],
194
- });
195
- output.success(`model ${modelName} created successfully`);
196
- console.log();
197
- output.info('you can now use this model with ARK agents and queries');
198
- return true;
199
- }
200
- catch (error) {
201
- output.error('failed to create model');
202
- console.error(error);
203
- // Try to clean up the secret if model creation failed
204
- try {
205
- await execa('kubectl', ['delete', 'secret', secretName], { stdio: 'pipe' });
206
- output.info(`cleaned up secret ${secretName}`);
207
- }
208
- catch {
209
- // Ignore cleanup errors
210
- }
211
- return false;
212
- }
213
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,125 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- const mockExeca = jest.fn();
3
- jest.unstable_mockModule('execa', () => ({
4
- execa: mockExeca,
5
- }));
6
- const mockInquirer = {
7
- prompt: jest.fn(),
8
- };
9
- jest.unstable_mockModule('inquirer', () => ({
10
- default: mockInquirer,
11
- }));
12
- const mockOutput = {
13
- info: jest.fn(),
14
- warning: jest.fn(),
15
- error: jest.fn(),
16
- success: jest.fn(),
17
- };
18
- jest.unstable_mockModule('../../lib/output.js', () => ({
19
- default: mockOutput,
20
- }));
21
- jest.spyOn(console, 'log').mockImplementation(() => { });
22
- jest.spyOn(console, 'error').mockImplementation(() => { });
23
- const { createModel } = await import('./create.js');
24
- describe('createModel', () => {
25
- beforeEach(() => {
26
- jest.clearAllMocks();
27
- });
28
- it('creates new model with provided name', async () => {
29
- // Model doesn't exist
30
- mockExeca.mockRejectedValueOnce(new Error('not found'));
31
- // Prompts for model details
32
- mockInquirer.prompt
33
- .mockResolvedValueOnce({ modelType: 'openai' })
34
- .mockResolvedValueOnce({
35
- modelVersion: 'gpt-4',
36
- baseUrl: 'https://api.openai.com/',
37
- })
38
- .mockResolvedValueOnce({ apiKey: 'secret-key' });
39
- // Secret operations succeed
40
- mockExeca.mockResolvedValueOnce({}); // delete secret (may not exist)
41
- mockExeca.mockResolvedValueOnce({}); // create secret
42
- mockExeca.mockResolvedValueOnce({}); // apply model
43
- const result = await createModel('test-model');
44
- expect(result).toBe(true);
45
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'model', 'test-model'], { stdio: 'pipe' });
46
- expect(mockOutput.success).toHaveBeenCalledWith('model test-model created successfully');
47
- });
48
- it('prompts for name when not provided', async () => {
49
- mockInquirer.prompt
50
- .mockResolvedValueOnce({ modelName: 'prompted-model' })
51
- .mockResolvedValueOnce({ modelType: 'azure' })
52
- .mockResolvedValueOnce({
53
- modelVersion: 'gpt-4',
54
- baseUrl: 'https://azure.com',
55
- })
56
- .mockResolvedValueOnce({ apiVersion: '2024-12-01' })
57
- .mockResolvedValueOnce({ apiKey: 'secret' });
58
- mockExeca.mockRejectedValueOnce(new Error('not found')); // model doesn't exist
59
- mockExeca.mockResolvedValue({}); // all kubectl ops succeed
60
- const result = await createModel();
61
- expect(result).toBe(true);
62
- expect(mockInquirer.prompt).toHaveBeenCalledWith([
63
- expect.objectContaining({
64
- name: 'modelName',
65
- message: 'model name:',
66
- }),
67
- ]);
68
- });
69
- it('handles overwrite confirmation when model exists', async () => {
70
- // Model exists
71
- mockExeca.mockResolvedValueOnce({});
72
- mockInquirer.prompt
73
- .mockResolvedValueOnce({ overwrite: true })
74
- .mockResolvedValueOnce({ modelType: 'openai' })
75
- .mockResolvedValueOnce({
76
- modelVersion: 'gpt-4',
77
- baseUrl: 'https://api.openai.com',
78
- })
79
- .mockResolvedValueOnce({ apiKey: 'secret' });
80
- mockExeca.mockResolvedValue({}); // remaining kubectl ops
81
- const result = await createModel('existing-model');
82
- expect(result).toBe(true);
83
- expect(mockOutput.warning).toHaveBeenCalledWith('model existing-model already exists');
84
- });
85
- it('cancels when user declines overwrite', async () => {
86
- mockExeca.mockResolvedValueOnce({}); // model exists
87
- mockInquirer.prompt.mockResolvedValueOnce({ overwrite: false });
88
- const result = await createModel('existing-model');
89
- expect(result).toBe(false);
90
- expect(mockOutput.info).toHaveBeenCalledWith('model creation cancelled');
91
- });
92
- it('handles secret creation failure', async () => {
93
- mockExeca.mockRejectedValueOnce(new Error('not found')); // model doesn't exist
94
- mockInquirer.prompt
95
- .mockResolvedValueOnce({ modelType: 'openai' })
96
- .mockResolvedValueOnce({
97
- modelVersion: 'gpt-4',
98
- baseUrl: 'https://api.openai.com',
99
- })
100
- .mockResolvedValueOnce({ apiKey: 'secret' });
101
- mockExeca.mockRejectedValueOnce(new Error('delete failed')); // delete secret may fail
102
- mockExeca.mockRejectedValueOnce(new Error('secret creation failed')); // create secret fails
103
- const result = await createModel('test-model');
104
- expect(result).toBe(false);
105
- expect(mockOutput.error).toHaveBeenCalledWith('failed to create secret');
106
- });
107
- it('cleans up secret if model creation fails', async () => {
108
- mockExeca.mockRejectedValueOnce(new Error('not found')); // model doesn't exist
109
- mockInquirer.prompt
110
- .mockResolvedValueOnce({ modelType: 'openai' })
111
- .mockResolvedValueOnce({
112
- modelVersion: 'gpt-4',
113
- baseUrl: 'https://api.openai.com',
114
- })
115
- .mockResolvedValueOnce({ apiKey: 'secret' });
116
- mockExeca.mockResolvedValueOnce({}); // delete secret
117
- mockExeca.mockResolvedValueOnce({}); // create secret
118
- mockExeca.mockRejectedValueOnce(new Error('apply failed')); // apply model fails
119
- mockExeca.mockResolvedValueOnce({}); // cleanup secret
120
- const result = await createModel('test-model');
121
- expect(result).toBe(false);
122
- expect(mockOutput.error).toHaveBeenCalledWith('failed to create model');
123
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['delete', 'secret', 'test-model-model-api-key'], { stdio: 'pipe' });
124
- });
125
- });
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- import type { ArkConfig } from '../../lib/config.js';
3
- export declare function createModelsCommand(_: ArkConfig): Command;
@@ -1,60 +0,0 @@
1
- import { Command } from 'commander';
2
- import { execa } from 'execa';
3
- import output from '../../lib/output.js';
4
- import { createModel } from './create.js';
5
- async function listModels(options) {
6
- try {
7
- // Use kubectl to get models
8
- const result = await execa('kubectl', ['get', 'models', '-o', 'json'], {
9
- stdio: 'pipe',
10
- });
11
- const data = JSON.parse(result.stdout);
12
- const models = data.items || [];
13
- if (options.output === 'json') {
14
- // Output the raw items for JSON format
15
- console.log(JSON.stringify(models, null, 2));
16
- }
17
- else {
18
- if (models.length === 0) {
19
- output.info('No models found');
20
- return;
21
- }
22
- // Just output the model names
23
- models.forEach((model) => {
24
- console.log(model.metadata.name);
25
- });
26
- }
27
- }
28
- catch (error) {
29
- output.error('fetching models:', error instanceof Error ? error.message : error);
30
- process.exit(1);
31
- }
32
- }
33
- export function createModelsCommand(_) {
34
- const modelsCommand = new Command('models');
35
- modelsCommand
36
- .description('List available models')
37
- .option('-o, --output <format>', 'Output format (json)', 'text')
38
- .action(async (options) => {
39
- await listModels(options);
40
- });
41
- const listCommand = new Command('list');
42
- listCommand
43
- .alias('ls')
44
- .description('List available models')
45
- .option('-o, --output <format>', 'Output format (json)', 'text')
46
- .action(async (options) => {
47
- await listModels(options);
48
- });
49
- modelsCommand.addCommand(listCommand);
50
- // Add create command
51
- const createCommand = new Command('create');
52
- createCommand
53
- .description('Create a new model')
54
- .argument('[name]', 'Model name (optional)')
55
- .action(async (name) => {
56
- await createModel(name);
57
- });
58
- modelsCommand.addCommand(createCommand);
59
- return modelsCommand;
60
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,76 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { Command } from 'commander';
3
- const mockExeca = jest.fn();
4
- jest.unstable_mockModule('execa', () => ({
5
- execa: mockExeca,
6
- }));
7
- const mockOutput = {
8
- info: jest.fn(),
9
- error: jest.fn(),
10
- };
11
- jest.unstable_mockModule('../../lib/output.js', () => ({
12
- default: mockOutput,
13
- }));
14
- const mockCreateModel = jest.fn();
15
- jest.unstable_mockModule('./create.js', () => ({
16
- createModel: mockCreateModel,
17
- }));
18
- const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
19
- throw new Error('process.exit called');
20
- }));
21
- const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
22
- const { createModelsCommand } = await import('./index.js');
23
- describe('models command', () => {
24
- beforeEach(() => {
25
- jest.clearAllMocks();
26
- });
27
- it('creates command with correct structure', () => {
28
- const command = createModelsCommand({});
29
- expect(command).toBeInstanceOf(Command);
30
- expect(command.name()).toBe('models');
31
- });
32
- it('lists models in text format', async () => {
33
- const mockModels = {
34
- items: [{ metadata: { name: 'gpt-4' } }, { metadata: { name: 'claude-3' } }],
35
- };
36
- mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockModels) });
37
- const command = createModelsCommand({});
38
- await command.parseAsync(['node', 'test']);
39
- expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'models', '-o', 'json'], { stdio: 'pipe' });
40
- expect(mockConsoleLog).toHaveBeenCalledWith('gpt-4');
41
- expect(mockConsoleLog).toHaveBeenCalledWith('claude-3');
42
- });
43
- it('lists models in json format', async () => {
44
- const mockModels = {
45
- items: [{ metadata: { name: 'gpt-4' } }],
46
- };
47
- mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockModels) });
48
- const command = createModelsCommand({});
49
- await command.parseAsync(['node', 'test', '-o', 'json']);
50
- expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(mockModels.items, null, 2));
51
- });
52
- it('shows info when no models', async () => {
53
- mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
54
- const command = createModelsCommand({});
55
- await command.parseAsync(['node', 'test']);
56
- expect(mockOutput.info).toHaveBeenCalledWith('No models found');
57
- });
58
- it('handles errors', async () => {
59
- mockExeca.mockRejectedValue(new Error('kubectl failed'));
60
- const command = createModelsCommand({});
61
- await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
62
- expect(mockOutput.error).toHaveBeenCalledWith('fetching models:', 'kubectl failed');
63
- expect(mockExit).toHaveBeenCalledWith(1);
64
- });
65
- it('list subcommand works', async () => {
66
- mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
67
- const command = createModelsCommand({});
68
- await command.parseAsync(['node', 'test', 'list']);
69
- expect(mockExeca).toHaveBeenCalled();
70
- });
71
- it('create subcommand works', async () => {
72
- const command = createModelsCommand({});
73
- await command.parseAsync(['node', 'test', 'create', 'my-model']);
74
- expect(mockCreateModel).toHaveBeenCalledWith('my-model');
75
- });
76
- });
@@ -1,8 +0,0 @@
1
- import { Model, ArkApiClient } from '../../lib/arkApiClient.js';
2
- interface ModelSelectorProps {
3
- arkApiClient: ArkApiClient;
4
- onSelect: (model: Model) => void;
5
- onExit: () => void;
6
- }
7
- export declare function ModelSelector({ arkApiClient, onSelect, onExit, }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,53 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- export function ModelSelector({ arkApiClient, onSelect, onExit, }) {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- const [models, setModels] = useState([]);
7
- const [loading, setLoading] = useState(true);
8
- const [error, setError] = useState(null);
9
- useEffect(() => {
10
- arkApiClient
11
- .getModels()
12
- .then((fetchedModels) => {
13
- setModels(fetchedModels);
14
- setLoading(false);
15
- })
16
- .catch((err) => {
17
- setError(err.message || 'Failed to fetch models');
18
- setLoading(false);
19
- });
20
- }, [arkApiClient]);
21
- useInput((input, key) => {
22
- if (key.escape) {
23
- onExit();
24
- }
25
- else if (key.upArrow || input === 'k') {
26
- setSelectedIndex((prev) => (prev === 0 ? models.length - 1 : prev - 1));
27
- }
28
- else if (key.downArrow || input === 'j') {
29
- setSelectedIndex((prev) => (prev === models.length - 1 ? 0 : prev + 1));
30
- }
31
- else if (key.return) {
32
- onSelect(models[selectedIndex]);
33
- }
34
- else {
35
- // Handle number keys for quick selection
36
- const num = parseInt(input, 10);
37
- if (!isNaN(num) && num >= 1 && num <= models.length) {
38
- onSelect(models[num - 1]);
39
- }
40
- }
41
- });
42
- if (loading) {
43
- return (_jsx(Box, { children: _jsx(Text, { children: "Loading models..." }) }));
44
- }
45
- if (error) {
46
- return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }));
47
- }
48
- if (models.length === 0) {
49
- return (_jsx(Box, { children: _jsx(Text, { children: "No models available" }) }));
50
- }
51
- const selectedModel = models[selectedIndex];
52
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 2, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Select Model" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Choose a model to start a conversation with" }) }), _jsx(Box, { flexDirection: "column", children: models.map((model, index) => (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: index === selectedIndex ? 'green' : undefined, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", model.name, model.type ? ` (${model.type})` : ''] }) }, model.name))) }), selectedModel && selectedModel.model && (_jsx(Box, { marginTop: 1, paddingLeft: 2, children: _jsxs(Text, { dimColor: true, wrap: "wrap", children: ["Model: ", selectedModel.model] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to confirm \u00B7 Number to select \u00B7 Esc to exit" }) })] }));
53
- }
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- import type { ArkConfig } from '../../lib/config.js';
3
- export declare function createRoutesCommand(_: ArkConfig): Command;
@@ -1,93 +0,0 @@
1
- import { Command } from 'commander';
2
- import chalk from 'chalk';
3
- import { execa } from 'execa';
4
- import output from '../../lib/output.js';
5
- async function listRoutes() {
6
- const namespace = 'ark-system';
7
- const port = 8080;
8
- const portSuffix = `:${port}`;
9
- try {
10
- // Check if localhost-gateway is installed
11
- const { stdout: gatewayCheck } = await execa('kubectl', ['get', 'gateway', 'localhost-gateway', '-n', namespace], { reject: false });
12
- if (!gatewayCheck) {
13
- output.error("localhost-gateway not installed in namespace 'ark-system'");
14
- output.info("run 'ark install' first");
15
- process.exit(1);
16
- }
17
- // Get HTTPRoutes
18
- const { stdout: routeOutput } = await execa('kubectl', [
19
- 'get',
20
- 'httproutes',
21
- '-A',
22
- '-o',
23
- 'custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,HOSTNAMES:.spec.hostnames',
24
- '--no-headers',
25
- ], { reject: false });
26
- if (!routeOutput || routeOutput.trim() === '') {
27
- console.log(chalk.white('available localhost gateway routes: 0'));
28
- output.info('no httproutes found. install services to see routes here.');
29
- return;
30
- }
31
- // Parse routes
32
- const lines = routeOutput.trim().split('\n');
33
- const routes = [];
34
- lines.forEach((line) => {
35
- const parts = line.split(/\s+/);
36
- if (parts.length >= 3) {
37
- const name = parts[1];
38
- // Remove brackets and split hostnames
39
- const hostnamesStr = parts.slice(2).join(' ').replace(/\[|\]/g, '');
40
- const hostnames = hostnamesStr
41
- .split(',')
42
- .map((h) => h.trim())
43
- .filter((h) => h && h !== '<none>');
44
- if (hostnames.length > 0) {
45
- routes.push({ name, hostnames });
46
- }
47
- }
48
- });
49
- // Count total routes (each hostname counts as a route)
50
- const routeCount = routes.reduce((count, r) => count + r.hostnames.length, 0);
51
- console.log(chalk.white(`available localhost gateway routes: ${routeCount}`));
52
- // Check port-forward status
53
- const { stdout: psOutput } = await execa('pgrep', ['-f', `kubectl.*port-forward.*${port}:80`], { reject: false });
54
- const portForwardActive = !!psOutput;
55
- if (portForwardActive) {
56
- output.info(`port-forward active on localhost${portSuffix}`);
57
- }
58
- else {
59
- output.error(`port-forward not running on localhost${portSuffix} - routes are not exposed`);
60
- console.log(chalk.blue('run:'), `kubectl port-forward -n ${namespace} service/localhost-gateway-nginx ${port}:80 > /dev/null 2>&1 &`);
61
- }
62
- console.log();
63
- // Display routes
64
- if (routes.length > 0) {
65
- const maxLength = Math.max(...routes.map((r) => r.name.length));
66
- routes.forEach((route) => {
67
- route.hostnames.forEach((hostname) => {
68
- const url = `http://${hostname}${portSuffix}/`;
69
- const padding = ' '.repeat(maxLength - route.name.length);
70
- if (portForwardActive) {
71
- console.log(` ${route.name}${padding}: ${chalk.blue(url)}`);
72
- }
73
- else {
74
- console.log(` ${route.name}${padding}: ${chalk.blue(url)} ${chalk.red('(unavailable)')}`);
75
- }
76
- });
77
- });
78
- }
79
- }
80
- catch (error) {
81
- output.error('failed to fetch routes:', error instanceof Error ? error.message : 'Unknown error');
82
- process.exit(1);
83
- }
84
- }
85
- export function createRoutesCommand(_) {
86
- const command = new Command('routes');
87
- command
88
- .description('show available gateway routes and their urls')
89
- .action(async () => {
90
- await listRoutes();
91
- });
92
- return command;
93
- }
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function createRoutesCommand(): Command;