@agents-at-scale/ark 0.1.35-rc.1 → 0.1.35-rc2

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 (130) hide show
  1. package/dist/arkServices.d.ts +4 -12
  2. package/dist/arkServices.js +19 -34
  3. package/dist/arkServices.spec.d.ts +1 -0
  4. package/dist/arkServices.spec.js +24 -0
  5. package/dist/commands/agents/index.d.ts +2 -1
  6. package/dist/commands/agents/index.js +2 -7
  7. package/dist/commands/agents/index.spec.d.ts +1 -0
  8. package/dist/commands/agents/index.spec.js +67 -0
  9. package/dist/commands/chat/index.d.ts +2 -1
  10. package/dist/commands/chat/index.js +5 -21
  11. package/dist/commands/cluster/get.spec.d.ts +1 -0
  12. package/dist/commands/cluster/get.spec.js +92 -0
  13. package/dist/commands/cluster/index.d.ts +2 -1
  14. package/dist/commands/cluster/index.js +1 -1
  15. package/dist/commands/cluster/index.spec.d.ts +1 -0
  16. package/dist/commands/cluster/index.spec.js +24 -0
  17. package/dist/commands/completion/index.d.ts +2 -1
  18. package/dist/commands/completion/index.js +24 -2
  19. package/dist/commands/completion/index.spec.d.ts +1 -0
  20. package/dist/commands/completion/index.spec.js +34 -0
  21. package/dist/commands/config/index.d.ts +2 -1
  22. package/dist/commands/config/index.js +2 -2
  23. package/dist/commands/config/index.spec.d.ts +1 -0
  24. package/dist/commands/config/index.spec.js +78 -0
  25. package/dist/commands/dashboard/index.d.ts +2 -1
  26. package/dist/commands/dashboard/index.js +1 -1
  27. package/dist/commands/dev/index.d.ts +2 -1
  28. package/dist/commands/dev/index.js +1 -1
  29. package/dist/commands/dev/tool-generate.spec.d.ts +1 -0
  30. package/dist/commands/dev/tool-generate.spec.js +163 -0
  31. package/dist/commands/dev/tool.spec.d.ts +1 -0
  32. package/dist/commands/dev/tool.spec.js +48 -0
  33. package/dist/commands/docs/index.d.ts +4 -0
  34. package/dist/commands/docs/index.js +18 -0
  35. package/dist/commands/generate/generators/project.js +22 -41
  36. package/dist/commands/generate/index.d.ts +2 -1
  37. package/dist/commands/generate/index.js +1 -1
  38. package/dist/commands/install/index.d.ts +4 -2
  39. package/dist/commands/install/index.js +225 -90
  40. package/dist/commands/install/index.spec.d.ts +1 -0
  41. package/dist/commands/install/index.spec.js +143 -0
  42. package/dist/commands/models/create.spec.d.ts +1 -0
  43. package/dist/commands/models/create.spec.js +125 -0
  44. package/dist/commands/models/index.d.ts +2 -1
  45. package/dist/commands/models/index.js +2 -7
  46. package/dist/commands/models/index.spec.d.ts +1 -0
  47. package/dist/commands/models/index.spec.js +76 -0
  48. package/dist/commands/query/index.d.ts +3 -0
  49. package/dist/commands/query/index.js +131 -0
  50. package/dist/commands/routes/index.d.ts +2 -1
  51. package/dist/commands/routes/index.js +1 -9
  52. package/dist/commands/status/index.d.ts +3 -2
  53. package/dist/commands/status/index.js +240 -11
  54. package/dist/commands/targets/index.d.ts +2 -1
  55. package/dist/commands/targets/index.js +1 -1
  56. package/dist/commands/targets/index.spec.d.ts +1 -0
  57. package/dist/commands/targets/index.spec.js +105 -0
  58. package/dist/commands/teams/index.d.ts +2 -1
  59. package/dist/commands/teams/index.js +2 -7
  60. package/dist/commands/teams/index.spec.d.ts +1 -0
  61. package/dist/commands/teams/index.spec.js +70 -0
  62. package/dist/commands/tools/index.d.ts +2 -1
  63. package/dist/commands/tools/index.js +2 -7
  64. package/dist/commands/tools/index.spec.d.ts +1 -0
  65. package/dist/commands/tools/index.spec.js +70 -0
  66. package/dist/commands/uninstall/index.d.ts +2 -1
  67. package/dist/commands/uninstall/index.js +66 -44
  68. package/dist/commands/uninstall/index.spec.d.ts +1 -0
  69. package/dist/commands/uninstall/index.spec.js +125 -0
  70. package/dist/components/ChatUI.js +4 -4
  71. package/dist/components/statusChecker.d.ts +5 -12
  72. package/dist/components/statusChecker.js +193 -90
  73. package/dist/config.d.ts +3 -22
  74. package/dist/config.js +7 -151
  75. package/dist/index.js +26 -19
  76. package/dist/lib/arkServiceProxy.js +4 -2
  77. package/dist/lib/arkStatus.d.ts +5 -0
  78. package/dist/lib/arkStatus.js +61 -2
  79. package/dist/lib/arkStatus.spec.d.ts +1 -0
  80. package/dist/lib/arkStatus.spec.js +49 -0
  81. package/dist/lib/chatClient.js +1 -3
  82. package/dist/lib/cluster.js +11 -14
  83. package/dist/lib/cluster.spec.d.ts +1 -0
  84. package/dist/lib/cluster.spec.js +338 -0
  85. package/dist/lib/commandUtils.js +7 -7
  86. package/dist/lib/commands.d.ts +16 -0
  87. package/dist/lib/commands.js +29 -0
  88. package/dist/lib/commands.spec.d.ts +1 -0
  89. package/dist/lib/commands.spec.js +146 -0
  90. package/dist/lib/config.d.ts +4 -0
  91. package/dist/lib/config.js +6 -4
  92. package/dist/lib/config.spec.d.ts +1 -0
  93. package/dist/lib/config.spec.js +99 -0
  94. package/dist/lib/consts.d.ts +0 -1
  95. package/dist/lib/consts.js +0 -2
  96. package/dist/lib/consts.spec.d.ts +1 -0
  97. package/dist/lib/consts.spec.js +15 -0
  98. package/dist/lib/errors.js +1 -1
  99. package/dist/lib/errors.spec.d.ts +1 -0
  100. package/dist/lib/errors.spec.js +221 -0
  101. package/dist/lib/exec.d.ts +0 -4
  102. package/dist/lib/exec.js +0 -11
  103. package/dist/lib/nextSteps.d.ts +4 -0
  104. package/dist/lib/nextSteps.js +20 -0
  105. package/dist/lib/nextSteps.spec.d.ts +1 -0
  106. package/dist/lib/nextSteps.spec.js +59 -0
  107. package/dist/lib/output.spec.d.ts +1 -0
  108. package/dist/lib/output.spec.js +123 -0
  109. package/dist/lib/portUtils.d.ts +8 -0
  110. package/dist/lib/portUtils.js +39 -0
  111. package/dist/lib/startup.d.ts +9 -0
  112. package/dist/lib/startup.js +93 -0
  113. package/dist/lib/startup.spec.d.ts +1 -0
  114. package/dist/lib/startup.spec.js +168 -0
  115. package/dist/lib/types.d.ts +9 -0
  116. package/dist/ui/AgentSelector.d.ts +8 -0
  117. package/dist/ui/AgentSelector.js +53 -0
  118. package/dist/ui/MainMenu.d.ts +5 -1
  119. package/dist/ui/MainMenu.js +117 -54
  120. package/dist/ui/ModelSelector.d.ts +8 -0
  121. package/dist/ui/ModelSelector.js +53 -0
  122. package/dist/ui/TeamSelector.d.ts +8 -0
  123. package/dist/ui/TeamSelector.js +55 -0
  124. package/dist/ui/ToolSelector.d.ts +8 -0
  125. package/dist/ui/ToolSelector.js +53 -0
  126. package/dist/ui/statusFormatter.d.ts +22 -10
  127. package/dist/ui/statusFormatter.js +37 -109
  128. package/dist/ui/statusFormatter.spec.d.ts +1 -0
  129. package/dist/ui/statusFormatter.spec.js +58 -0
  130. package/package.json +3 -3
@@ -1,11 +1,7 @@
1
- import { exec } from 'child_process';
2
- import { promisify } from 'util';
3
- import { KubernetesConfigManager } from '../lib/kubernetes.js';
4
- import * as k8s from '@kubernetes/client-node';
5
- import { isCommandAvailable } from '../lib/commandUtils.js';
1
+ import { execa } from 'execa';
2
+ import { checkCommandExists } from '../lib/commands.js';
6
3
  import { arkServices } from '../arkServices.js';
7
4
  import { isArkReady } from '../lib/arkStatus.js';
8
- const execAsync = promisify(exec);
9
5
  export const getNodeVersion = () => ({
10
6
  command: 'node',
11
7
  versionArgs: '--version',
@@ -66,30 +62,62 @@ function createErrorServiceStatus(name, url, error, defaultStatus = 'unhealthy',
66
62
  };
67
63
  }
68
64
  export class StatusChecker {
69
- constructor(arkClient) {
70
- this.arkClient = arkClient;
71
- this.kubernetesManager = new KubernetesConfigManager();
72
- }
73
65
  /**
74
66
  * Get version of a command
75
67
  */
76
68
  async getCommandVersion(config) {
77
69
  try {
78
- const cmd = `${config.command} ${config.versionArgs}`;
79
- const { stdout } = await execAsync(cmd);
70
+ const args = config.versionArgs.split(' ');
71
+ const { stdout } = await execa(config.command, args);
80
72
  return config.versionExtract(stdout);
81
73
  }
82
74
  catch (error) {
83
75
  throw new Error(`Failed to get ${config.command} version: ${error instanceof Error ? error.message : 'Unknown error'}`);
84
76
  }
85
77
  }
78
+ /**
79
+ * Check combined deployment and helm status
80
+ * Gets health from deployment, version/revision from Helm
81
+ */
82
+ async checkCombinedStatus(serviceName, deploymentName, helmReleaseName, namespace, devDeploymentName) {
83
+ // Get deployment status for health
84
+ const deploymentStatus = await this.checkDeploymentStatus(serviceName, deploymentName, namespace, devDeploymentName);
85
+ // Try to get version info from Helm if it's a healthy deployment
86
+ if (deploymentStatus.status === 'healthy' ||
87
+ deploymentStatus.status === 'warning') {
88
+ try {
89
+ const { stdout } = await execa('helm', ['list', '-n', namespace, '-o', 'json'], { timeout: 5000 });
90
+ const releases = JSON.parse(stdout);
91
+ const release = releases.find((r) => r.name === helmReleaseName);
92
+ if (release) {
93
+ // Merge Helm version info into deployment status
94
+ return {
95
+ ...deploymentStatus,
96
+ version: release.app_version,
97
+ revision: release.revision,
98
+ };
99
+ }
100
+ }
101
+ catch {
102
+ // If Helm check fails, return deployment status as-is
103
+ }
104
+ }
105
+ return deploymentStatus;
106
+ }
86
107
  /**
87
108
  * Check deployment status
88
109
  */
89
- async checkDeploymentStatus(serviceName, deploymentName, namespace) {
110
+ async checkDeploymentStatus(serviceName, deploymentName, namespace, devDeploymentName) {
90
111
  try {
91
- const cmd = `kubectl get deployment ${deploymentName} --namespace ${namespace} -o json`;
92
- const { stdout } = await execAsync(cmd);
112
+ const { stdout } = await execa('kubectl', [
113
+ 'get',
114
+ 'deployment',
115
+ deploymentName,
116
+ '--namespace',
117
+ namespace,
118
+ '-o',
119
+ 'json',
120
+ ]);
93
121
  const deployment = JSON.parse(stdout);
94
122
  const replicas = deployment.spec?.replicas || 0;
95
123
  const readyReplicas = deployment.status?.readyReplicas || 0;
@@ -109,19 +137,113 @@ export class StatusChecker {
109
137
  else {
110
138
  status = 'warning';
111
139
  }
140
+ // If main deployment has 0 replicas and we have a dev deployment, check it
141
+ if (replicas === 0 && devDeploymentName) {
142
+ try {
143
+ const { stdout: devStdout } = await execa('kubectl', [
144
+ 'get',
145
+ 'deployment',
146
+ devDeploymentName,
147
+ '--namespace',
148
+ namespace,
149
+ '-o',
150
+ 'json',
151
+ ]);
152
+ const devDeployment = JSON.parse(devStdout);
153
+ const devReplicas = devDeployment.spec?.replicas || 0;
154
+ const devReadyReplicas = devDeployment.status?.readyReplicas || 0;
155
+ const devAvailableReplicas = devDeployment.status?.availableReplicas || 0;
156
+ if (devReplicas > 0) {
157
+ const devAvailableCondition = devDeployment.status?.conditions?.find((condition) => condition.type === 'Available');
158
+ const devIsAvailable = devAvailableCondition?.status === 'True';
159
+ const devAllReplicasReady = devReadyReplicas === devReplicas &&
160
+ devAvailableReplicas === devReplicas;
161
+ let devStatus;
162
+ if (devReplicas === 0 || devReadyReplicas === 0) {
163
+ devStatus = 'not ready';
164
+ }
165
+ else if (devIsAvailable && devAllReplicasReady) {
166
+ devStatus = 'healthy';
167
+ }
168
+ else {
169
+ devStatus = 'warning';
170
+ }
171
+ return {
172
+ name: serviceName,
173
+ status: devStatus,
174
+ details: `${devReadyReplicas}/${devReplicas} replicas ready`,
175
+ isDev: true,
176
+ namespace,
177
+ };
178
+ }
179
+ }
180
+ catch {
181
+ // If dev deployment check fails, return the original status
182
+ }
183
+ }
112
184
  return {
113
185
  name: serviceName,
114
186
  status,
115
187
  details: `${readyReplicas}/${replicas} replicas ready`,
188
+ namespace,
116
189
  };
117
190
  }
118
191
  catch (error) {
119
192
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
120
- if (errorMessage.includes('not found')) {
193
+ // If main deployment not found or not healthy, try dev deployment
194
+ if (errorMessage.includes('not found') ||
195
+ errorMessage.includes('NotFound')) {
196
+ if (devDeploymentName) {
197
+ try {
198
+ const { stdout } = await execa('kubectl', [
199
+ 'get',
200
+ 'deployment',
201
+ devDeploymentName,
202
+ '--namespace',
203
+ namespace,
204
+ '-o',
205
+ 'json',
206
+ ]);
207
+ const devDeployment = JSON.parse(stdout);
208
+ const replicas = devDeployment.spec?.replicas || 0;
209
+ const readyReplicas = devDeployment.status?.readyReplicas || 0;
210
+ const availableReplicas = devDeployment.status?.availableReplicas || 0;
211
+ const availableCondition = devDeployment.status?.conditions?.find((condition) => condition.type === 'Available');
212
+ const isAvailable = availableCondition?.status === 'True';
213
+ const allReplicasReady = readyReplicas === replicas && availableReplicas === replicas;
214
+ let status;
215
+ if (replicas === 0 || readyReplicas === 0) {
216
+ status = 'not ready';
217
+ }
218
+ else if (isAvailable && allReplicasReady) {
219
+ status = 'healthy';
220
+ }
221
+ else {
222
+ status = 'warning';
223
+ }
224
+ return {
225
+ name: serviceName,
226
+ status,
227
+ details: `${readyReplicas}/${replicas} replicas ready`,
228
+ isDev: true,
229
+ namespace,
230
+ };
231
+ }
232
+ catch {
233
+ // If dev deployment also not found, return not installed
234
+ return {
235
+ name: serviceName,
236
+ status: 'not installed',
237
+ details: `Deployment '${deploymentName}' not found`,
238
+ namespace,
239
+ };
240
+ }
241
+ }
121
242
  return {
122
243
  name: serviceName,
123
244
  status: 'not installed',
124
- details: `Deployment '${deploymentName}' not found in namespace '${namespace}'`,
245
+ details: `Deployment '${deploymentName}' not found`,
246
+ namespace,
125
247
  };
126
248
  }
127
249
  return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check deployment: ${errorMessage}`);
@@ -132,14 +254,22 @@ export class StatusChecker {
132
254
  */
133
255
  async checkHelmStatus(serviceName, helmReleaseName, namespace) {
134
256
  try {
135
- const cmd = `helm list --filter ${helmReleaseName} --namespace ${namespace} --output json`;
136
- const { stdout } = await execAsync(cmd);
257
+ const { stdout } = await execa('helm', [
258
+ 'list',
259
+ '--filter',
260
+ helmReleaseName,
261
+ '--namespace',
262
+ namespace,
263
+ '--output',
264
+ 'json',
265
+ ]);
137
266
  const helmList = JSON.parse(stdout);
138
267
  if (helmList.length === 0) {
139
268
  return {
140
269
  name: serviceName,
141
270
  status: 'not installed',
142
- details: `Helm release '${helmReleaseName}' not found in namespace '${namespace}'`,
271
+ details: `Helm release '${helmReleaseName}' not found`,
272
+ namespace,
143
273
  };
144
274
  }
145
275
  const release = helmList[0];
@@ -153,6 +283,7 @@ export class StatusChecker {
153
283
  version: appVersion,
154
284
  revision: revision,
155
285
  details: `Status: ${status}`,
286
+ namespace,
156
287
  };
157
288
  }
158
289
  catch (error) {
@@ -160,69 +291,6 @@ export class StatusChecker {
160
291
  return createErrorServiceStatus(serviceName, '', error, 'unhealthy', `Failed to check helm status: ${errorMessage}`);
161
292
  }
162
293
  }
163
- /**
164
- * Return a "not installed" status for a service
165
- */
166
- createNotInstalledStatus(serviceName) {
167
- return {
168
- name: serviceName,
169
- status: 'not installed',
170
- details: `${serviceName} is not configured or not part of this deployment`,
171
- };
172
- }
173
- /**
174
- * Check Kubernetes service health via pods and endpoints
175
- */
176
- async checkKubernetesService(serviceName, kubernetesServiceName, namespace = 'default') {
177
- try {
178
- await this.kubernetesManager.initializeConfig();
179
- const kc = this.kubernetesManager.getKubeConfig();
180
- const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
181
- // Check if service exists and has endpoints
182
- const service = await k8sApi.readNamespacedService({
183
- name: kubernetesServiceName,
184
- namespace,
185
- });
186
- const endpoints = await k8sApi.readNamespacedEndpoints({
187
- name: kubernetesServiceName,
188
- namespace,
189
- });
190
- // Check if service has ready endpoints
191
- const readyAddresses = endpoints.subsets?.reduce((total, subset) => {
192
- return total + (subset.addresses?.length || 0);
193
- }, 0) || 0;
194
- if (readyAddresses > 0) {
195
- const serviceIP = service.spec?.clusterIP;
196
- const servicePort = service.spec?.ports?.[0]?.port;
197
- return {
198
- name: serviceName,
199
- status: 'healthy',
200
- url: `cluster://${serviceIP}:${servicePort}`,
201
- details: `${serviceName} running in cluster (${readyAddresses} ready endpoints)`,
202
- };
203
- }
204
- else {
205
- return {
206
- name: serviceName,
207
- status: 'unhealthy',
208
- details: `${serviceName} service exists but has no ready endpoints`,
209
- };
210
- }
211
- }
212
- catch (error) {
213
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
214
- // If service not found, it's not installed
215
- if (errorMessage.includes('not found')) {
216
- return this.createNotInstalledStatus(serviceName);
217
- }
218
- // Other errors indicate unhealthy
219
- return {
220
- name: serviceName,
221
- status: 'unhealthy',
222
- details: `Failed to check ${serviceName}: ${errorMessage}`,
223
- };
224
- }
225
- }
226
294
  /**
227
295
  * Check system dependencies
228
296
  */
@@ -238,7 +306,8 @@ export class StatusChecker {
238
306
  ];
239
307
  const results = [];
240
308
  for (const dep of dependencies) {
241
- const installed = await isCommandAvailable(dep.command);
309
+ const args = dep.versionArgs.split(' ');
310
+ const installed = await checkCommandExists(dep.command, args);
242
311
  const version = installed
243
312
  ? await this.getCommandVersion({
244
313
  command: dep.command,
@@ -264,8 +333,16 @@ export class StatusChecker {
264
333
  // Check dependencies first
265
334
  const dependencies = await this.checkDependencies();
266
335
  // Test cluster access
267
- const configManager = new (await import('../config.js')).ConfigManager();
268
- const clusterAccess = await configManager.testClusterAccess();
336
+ let clusterAccess = false;
337
+ try {
338
+ await execa('kubectl', ['get', 'namespaces', '-o', 'name'], {
339
+ timeout: 5000,
340
+ });
341
+ clusterAccess = true;
342
+ }
343
+ catch {
344
+ clusterAccess = false;
345
+ }
269
346
  // Get cluster info if accessible
270
347
  let clusterInfo;
271
348
  if (clusterAccess) {
@@ -277,11 +354,17 @@ export class StatusChecker {
277
354
  if (clusterAccess) {
278
355
  const serviceChecks = [];
279
356
  for (const [serviceName, service] of Object.entries(arkServices)) {
357
+ // Skip disabled services
358
+ if (!service.enabled)
359
+ continue;
360
+ // Use service namespace if defined, otherwise use current namespace from clusterInfo
361
+ const namespace = service.namespace || clusterInfo?.namespace || 'default';
280
362
  if (service.k8sDeploymentName) {
281
- serviceChecks.push(this.checkDeploymentStatus(serviceName, service.k8sDeploymentName, service.namespace));
363
+ // For deployments, get health from deployment and version from Helm
364
+ serviceChecks.push(this.checkCombinedStatus(serviceName, service.k8sDeploymentName, service.helmReleaseName, namespace, service.k8sDevDeploymentName));
282
365
  }
283
366
  else {
284
- serviceChecks.push(this.checkHelmStatus(serviceName, service.helmReleaseName, service.namespace));
367
+ serviceChecks.push(this.checkHelmStatus(serviceName, service.helmReleaseName, namespace));
285
368
  }
286
369
  }
287
370
  services = await Promise.all(serviceChecks);
@@ -289,16 +372,35 @@ export class StatusChecker {
289
372
  // Check if ARK is ready (controller is running)
290
373
  let arkReady = false;
291
374
  let defaultModelExists = false;
375
+ let defaultModel;
292
376
  if (clusterAccess) {
293
377
  arkReady = await isArkReady();
294
- // Check for default model
378
+ // Check for default model with detailed status
295
379
  if (arkReady) {
296
380
  try {
297
- await execAsync('kubectl get model default -o name');
381
+ const { stdout } = await execa('kubectl', [
382
+ 'get',
383
+ 'model',
384
+ 'default',
385
+ '-o',
386
+ 'json',
387
+ ]);
388
+ const model = JSON.parse(stdout);
298
389
  defaultModelExists = true;
390
+ // Extract model details
391
+ const available = model.status?.conditions?.find((c) => c.type === 'Available')?.status === 'True';
392
+ defaultModel = {
393
+ exists: true,
394
+ available,
395
+ provider: model.spec?.provider,
396
+ details: model.spec?.model || model.spec?.apiEndpoint,
397
+ };
299
398
  }
300
399
  catch {
301
400
  defaultModelExists = false;
401
+ defaultModel = {
402
+ exists: false,
403
+ };
302
404
  }
303
405
  }
304
406
  }
@@ -309,6 +411,7 @@ export class StatusChecker {
309
411
  clusterInfo,
310
412
  arkReady,
311
413
  defaultModelExists,
414
+ defaultModel,
312
415
  };
313
416
  }
314
417
  }
package/dist/config.d.ts CHANGED
@@ -1,27 +1,14 @@
1
- import { ArkConfig, KubernetesConfig } from './lib/types.js';
2
1
  /**
3
- * ConfigManager handles ARK CLI configuration with automatic service discovery
4
- * and multiple fallback mechanisms. Complex discovery logic can be debugged by
5
- * setting DEBUG=ark:config or DEBUG=ark:* environment variable.
2
+ * ConfigManager handles API URL discovery and cluster access testing.
3
+ * Complex discovery logic can be debugged by setting DEBUG=ark:config
6
4
  *
7
5
  * Example usage:
8
- * DEBUG=ark:config ark check status
9
- * DEBUG=ark:* ark dashboard
6
+ * DEBUG=ark:config ark status
10
7
  */
11
8
  export declare class ConfigManager {
12
- private configDir;
13
- private configFile;
14
9
  private kubernetesManager;
15
- private gatewayManager;
16
10
  private kubeConfig;
17
11
  constructor();
18
- ensureConfigDir(): Promise<void>;
19
- loadConfig(): Promise<ArkConfig>;
20
- saveConfig(config: ArkConfig): Promise<void>;
21
- updateConfig(updates: Partial<ArkConfig>): Promise<ArkConfig>;
22
- private getDefaultConfig;
23
- initializeConfig(): Promise<ArkConfig>;
24
- getConfigPath(): string;
25
12
  getApiBaseUrl(): Promise<string>;
26
13
  /**
27
14
  * Check if localhost-gateway is running by testing port 8080
@@ -32,11 +19,5 @@ export declare class ConfigManager {
32
19
  */
33
20
  private getLocalhostGatewayUrls;
34
21
  private initKubernetesConfig;
35
- getKubernetesConfig(): Promise<KubernetesConfig | null>;
36
22
  testClusterAccess(): Promise<boolean>;
37
- /**
38
- * Discover service URLs from ark-api service discovery
39
- */
40
- private discoverServicesFromApi;
41
- getServiceUrls(): Promise<Record<string, string>>;
42
23
  }
package/dist/config.js CHANGED
@@ -1,119 +1,22 @@
1
- import { promises as fs } from 'fs';
2
- import { homedir } from 'os';
3
- import { join } from 'path';
4
1
  import axios from 'axios';
5
2
  import Debug from 'debug';
6
- import { ArkClient } from './lib/arkClient.js';
7
- import { DEFAULT_ADDRESS_ARK_API, CONFIG_DIR_NAME, CONFIG_FILE_NAME, } from './lib/consts.js';
8
- import { GatewayManager } from './lib/gatewayManager.js';
3
+ import { DEFAULT_ADDRESS_ARK_API } from './lib/consts.js';
9
4
  import { KubernetesConfigManager } from './lib/kubernetes.js';
10
5
  import { getStatusCheckableServices } from './arkServices.js';
11
6
  const debug = Debug('ark:config');
12
7
  /**
13
- * ConfigManager handles ARK CLI configuration with automatic service discovery
14
- * and multiple fallback mechanisms. Complex discovery logic can be debugged by
15
- * setting DEBUG=ark:config or DEBUG=ark:* environment variable.
8
+ * ConfigManager handles API URL discovery and cluster access testing.
9
+ * Complex discovery logic can be debugged by setting DEBUG=ark:config
16
10
  *
17
11
  * Example usage:
18
- * DEBUG=ark:config ark check status
19
- * DEBUG=ark:* ark dashboard
12
+ * DEBUG=ark:config ark status
20
13
  */
21
14
  export class ConfigManager {
22
15
  constructor() {
23
16
  this.kubeConfig = null;
24
- this.configDir = join(homedir(), '.config', CONFIG_DIR_NAME);
25
- this.configFile = join(this.configDir, CONFIG_FILE_NAME);
26
17
  this.kubernetesManager = new KubernetesConfigManager();
27
- this.gatewayManager = new GatewayManager();
28
- }
29
- async ensureConfigDir() {
30
- try {
31
- await fs.mkdir(this.configDir, { recursive: true });
32
- }
33
- catch (_error) {
34
- // Directory might already exist
35
- }
36
- }
37
- async loadConfig() {
38
- await this.ensureConfigDir();
39
- try {
40
- // Check if config file exists
41
- await fs.access(this.configFile);
42
- }
43
- catch (error) {
44
- // Config file doesn't exist - start with default config and enrich with kube config
45
- if (error &&
46
- typeof error === 'object' &&
47
- 'code' in error &&
48
- error.code === 'ENOENT') {
49
- return await this.getDefaultConfig();
50
- }
51
- // Other access errors (permissions, etc.) should be thrown
52
- throw error;
53
- }
54
- try {
55
- // Config file exists - try to read and parse it
56
- const data = await fs.readFile(this.configFile, 'utf-8');
57
- const config = JSON.parse(data);
58
- // Merge with current Kubernetes config if not present
59
- await this.initKubernetesConfig();
60
- return {
61
- ...config,
62
- kubeconfig: config.kubeconfig || this.kubeConfig?.kubeconfig,
63
- currentContext: config.currentContext || this.kubeConfig?.currentContext,
64
- kubeNamespace: config.kubeNamespace || this.kubeConfig?.namespace,
65
- };
66
- }
67
- catch (error) {
68
- // If it's a JSON parsing error, throw a clear error message
69
- if (error instanceof SyntaxError) {
70
- throw new Error(`Invalid JSON in config file ${this.configFile}: ${error.message}`);
71
- }
72
- // Other errors (read errors, etc.) should be thrown as-is
73
- throw error;
74
- }
75
- }
76
- async saveConfig(config) {
77
- await this.ensureConfigDir();
78
- const jsonData = JSON.stringify(config, null, 2);
79
- await fs.writeFile(this.configFile, jsonData);
80
- }
81
- async updateConfig(updates) {
82
- const currentConfig = await this.loadConfig();
83
- const newConfig = { ...currentConfig, ...updates };
84
- await this.saveConfig(newConfig);
85
- return newConfig;
86
- }
87
- async getDefaultConfig() {
88
- // Initialize Kubernetes config to get defaults
89
- await this.initKubernetesConfig();
90
- return {
91
- defaultAgent: 'default',
92
- defaultModel: 'default',
93
- defaultNamespace: this.kubeConfig?.namespace || 'default',
94
- apiBaseUrl: DEFAULT_ADDRESS_ARK_API,
95
- kubeconfig: this.kubeConfig?.kubeconfig,
96
- currentContext: this.kubeConfig?.currentContext,
97
- kubeNamespace: this.kubeConfig?.namespace,
98
- };
99
- }
100
- async initializeConfig() {
101
- const config = await this.loadConfig();
102
- const defaultConfig = await this.getDefaultConfig();
103
- const mergedConfig = { ...defaultConfig, ...config };
104
- await this.saveConfig(mergedConfig);
105
- return mergedConfig;
106
- }
107
- getConfigPath() {
108
- return this.configFile;
109
18
  }
110
19
  async getApiBaseUrl() {
111
- const config = await this.loadConfig();
112
- // If apiBaseUrl is explicitly set in config, use it
113
- if (config.apiBaseUrl && config.apiBaseUrl !== DEFAULT_ADDRESS_ARK_API) {
114
- debug('using explicit config apiBaseUrl: %s', config.apiBaseUrl);
115
- return config.apiBaseUrl;
116
- }
117
20
  // First try to detect localhost-gateway (works for everyone with standard setup)
118
21
  if (await this.isLocalhostGatewayRunning()) {
119
22
  const gatewayUrls = this.getLocalhostGatewayUrls();
@@ -127,7 +30,7 @@ export class ConfigManager {
127
30
  await this.initKubernetesConfig();
128
31
  if (this.kubeConfig) {
129
32
  try {
130
- const namespace = config.kubeNamespace || config.defaultNamespace || 'default';
33
+ const namespace = 'default';
131
34
  const discoveredUrl = await this.kubernetesManager.getArkApiUrl(namespace);
132
35
  debug('kubernetes discovery successful in %s: %s', namespace, discoveredUrl);
133
36
  return discoveredUrl;
@@ -137,9 +40,8 @@ export class ConfigManager {
137
40
  // Fall back to default if discovery fails
138
41
  }
139
42
  }
140
- const fallbackUrl = config.apiBaseUrl || DEFAULT_ADDRESS_ARK_API;
141
- debug('falling back to default: %s', fallbackUrl);
142
- return fallbackUrl;
43
+ debug('falling back to default: %s', DEFAULT_ADDRESS_ARK_API);
44
+ return DEFAULT_ADDRESS_ARK_API;
143
45
  }
144
46
  /**
145
47
  * Check if localhost-gateway is running by testing port 8080
@@ -180,10 +82,6 @@ export class ConfigManager {
180
82
  }
181
83
  }
182
84
  }
183
- async getKubernetesConfig() {
184
- await this.initKubernetesConfig();
185
- return this.kubeConfig;
186
- }
187
85
  async testClusterAccess() {
188
86
  await this.initKubernetesConfig();
189
87
  if (!this.kubeConfig) {
@@ -191,46 +89,4 @@ export class ConfigManager {
191
89
  }
192
90
  return await this.kubernetesManager.testClusterAccess();
193
91
  }
194
- /**
195
- * Discover service URLs from ark-api service discovery
196
- */
197
- async discoverServicesFromApi() {
198
- try {
199
- const apiBaseUrl = await this.getApiBaseUrl();
200
- const arkClient = new ArkClient(apiBaseUrl);
201
- const config = await this.loadConfig();
202
- const namespace = config.kubeNamespace || config.defaultNamespace || 'default';
203
- debug('service discovery: querying ark-api at %s (namespace: %s)', apiBaseUrl, namespace);
204
- const services = await arkClient.getArkServices(namespace);
205
- const serviceUrls = {};
206
- // Dynamically map all discovered services with HTTP routes
207
- for (const service of services) {
208
- if (service.httproutes && service.httproutes.length > 0) {
209
- const serviceName = service.release_name || service.name;
210
- const serviceUrl = service.httproutes[0].url; // Use first route URL
211
- serviceUrls[serviceName] = serviceUrl;
212
- }
213
- }
214
- const discoveredServices = Object.entries(serviceUrls).map(([key, url]) => `${key}: ${url}`);
215
- debug('service discovery: found %d services - %s', discoveredServices.length, discoveredServices.join(', ') || 'none');
216
- return serviceUrls;
217
- }
218
- catch (error) {
219
- debug('service discovery failed: %s', error instanceof Error ? error.message : error);
220
- // Return empty object if discovery fails - will fall back to config/defaults
221
- return {};
222
- }
223
- }
224
- async getServiceUrls() {
225
- // Try localhost-gateway detection (works for everyone with standard setup)
226
- if (await this.isLocalhostGatewayRunning()) {
227
- const gatewayUrls = this.getLocalhostGatewayUrls();
228
- debug('localhost-gateway detected, using: %o', gatewayUrls);
229
- return gatewayUrls;
230
- }
231
- // Try to discover services from ark-api (requires kubeconfig)
232
- const discoveredUrls = await this.discoverServicesFromApi();
233
- debug('discovered services: %o', discoveredUrls);
234
- return discoveredUrls;
235
- }
236
92
  }