@agents-at-scale/ark 0.1.31

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 (76) hide show
  1. package/README.md +95 -0
  2. package/dist/commands/cluster/get-ip.d.ts +2 -0
  3. package/dist/commands/cluster/get-ip.js +32 -0
  4. package/dist/commands/cluster/get-type.d.ts +2 -0
  5. package/dist/commands/cluster/get-type.js +26 -0
  6. package/dist/commands/cluster/index.d.ts +2 -0
  7. package/dist/commands/cluster/index.js +10 -0
  8. package/dist/commands/completion.d.ts +2 -0
  9. package/dist/commands/completion.js +108 -0
  10. package/dist/commands/config.d.ts +5 -0
  11. package/dist/commands/config.js +327 -0
  12. package/dist/commands/generate/config.d.ts +145 -0
  13. package/dist/commands/generate/config.js +253 -0
  14. package/dist/commands/generate/generators/agent.d.ts +2 -0
  15. package/dist/commands/generate/generators/agent.js +156 -0
  16. package/dist/commands/generate/generators/index.d.ts +6 -0
  17. package/dist/commands/generate/generators/index.js +6 -0
  18. package/dist/commands/generate/generators/marketplace.d.ts +2 -0
  19. package/dist/commands/generate/generators/marketplace.js +304 -0
  20. package/dist/commands/generate/generators/mcpserver.d.ts +25 -0
  21. package/dist/commands/generate/generators/mcpserver.js +350 -0
  22. package/dist/commands/generate/generators/project.d.ts +2 -0
  23. package/dist/commands/generate/generators/project.js +784 -0
  24. package/dist/commands/generate/generators/query.d.ts +2 -0
  25. package/dist/commands/generate/generators/query.js +213 -0
  26. package/dist/commands/generate/generators/team.d.ts +2 -0
  27. package/dist/commands/generate/generators/team.js +407 -0
  28. package/dist/commands/generate/index.d.ts +24 -0
  29. package/dist/commands/generate/index.js +357 -0
  30. package/dist/commands/generate/templateDiscovery.d.ts +30 -0
  31. package/dist/commands/generate/templateDiscovery.js +94 -0
  32. package/dist/commands/generate/templateEngine.d.ts +78 -0
  33. package/dist/commands/generate/templateEngine.js +368 -0
  34. package/dist/commands/generate/utils/nameUtils.d.ts +35 -0
  35. package/dist/commands/generate/utils/nameUtils.js +110 -0
  36. package/dist/commands/generate/utils/projectUtils.d.ts +28 -0
  37. package/dist/commands/generate/utils/projectUtils.js +133 -0
  38. package/dist/components/DashboardCLI.d.ts +3 -0
  39. package/dist/components/DashboardCLI.js +149 -0
  40. package/dist/components/GeneratorUI.d.ts +3 -0
  41. package/dist/components/GeneratorUI.js +167 -0
  42. package/dist/components/statusChecker.d.ts +48 -0
  43. package/dist/components/statusChecker.js +251 -0
  44. package/dist/config.d.ts +42 -0
  45. package/dist/config.js +243 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +67 -0
  48. package/dist/lib/arkClient.d.ts +32 -0
  49. package/dist/lib/arkClient.js +43 -0
  50. package/dist/lib/cluster.d.ts +8 -0
  51. package/dist/lib/cluster.js +134 -0
  52. package/dist/lib/config.d.ts +82 -0
  53. package/dist/lib/config.js +223 -0
  54. package/dist/lib/consts.d.ts +10 -0
  55. package/dist/lib/consts.js +15 -0
  56. package/dist/lib/errors.d.ts +56 -0
  57. package/dist/lib/errors.js +208 -0
  58. package/dist/lib/exec.d.ts +5 -0
  59. package/dist/lib/exec.js +20 -0
  60. package/dist/lib/gatewayManager.d.ts +24 -0
  61. package/dist/lib/gatewayManager.js +85 -0
  62. package/dist/lib/kubernetes.d.ts +28 -0
  63. package/dist/lib/kubernetes.js +122 -0
  64. package/dist/lib/progress.d.ts +128 -0
  65. package/dist/lib/progress.js +273 -0
  66. package/dist/lib/security.d.ts +37 -0
  67. package/dist/lib/security.js +295 -0
  68. package/dist/lib/types.d.ts +37 -0
  69. package/dist/lib/types.js +1 -0
  70. package/dist/lib/wrappers/git.d.ts +2 -0
  71. package/dist/lib/wrappers/git.js +43 -0
  72. package/dist/ui/MainMenu.d.ts +3 -0
  73. package/dist/ui/MainMenu.js +116 -0
  74. package/dist/ui/statusFormatter.d.ts +9 -0
  75. package/dist/ui/statusFormatter.js +47 -0
  76. package/package.json +62 -0
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import fs from 'fs';
4
+ import path, { dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import axios from 'axios';
7
+ import { execa } from 'execa';
8
+ import { Text } from 'ink';
9
+ import open from 'open';
10
+ import { useState, useEffect } from 'react';
11
+ import YAML from 'yaml';
12
+ import { DEFAULT_ADDRESS_ARK_API, DEFAULT_ARK_DASHBOARD_URL, } from '../lib/consts.js';
13
+ const DashboardCLI = () => {
14
+ const [currentStep, setCurrentStep] = useState(0);
15
+ const [status, setStatus] = useState('Running');
16
+ const [dotsIndex, setDotsIndex] = useState(0);
17
+ const dotFrames = ['.', '..', '...'];
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+ const rootDir = path.resolve(__dirname, '../../../');
21
+ const steps = [
22
+ {
23
+ label: 'Building backend',
24
+ path: `${rootDir}/ark-api`,
25
+ checkUrl: `${DEFAULT_ADDRESS_ARK_API}/health`,
26
+ },
27
+ {
28
+ label: 'Starting frontend',
29
+ path: `${rootDir}/ark-dashboard`,
30
+ checkUrl: DEFAULT_ARK_DASHBOARD_URL,
31
+ },
32
+ ];
33
+ async function isServiceUp(url) {
34
+ try {
35
+ const res = await axios.get(url);
36
+ return res.status >= 200 && res.status < 400;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ async function runCommands(commands, cwd) {
43
+ for (const cmd of commands) {
44
+ // Run each command in the specified working directory
45
+ await execa(cmd, {
46
+ cwd: cwd,
47
+ stdio: ['ignore', 'pipe', 'pipe'],
48
+ shell: true,
49
+ });
50
+ }
51
+ }
52
+ function loadServiceManifest(servicePath) {
53
+ const manifestPath = path.join(servicePath, 'manifest.yaml');
54
+ const raw = fs.readFileSync(manifestPath, 'utf-8');
55
+ return YAML.parse(raw);
56
+ }
57
+ async function runStep(stepIndex) {
58
+ const step = steps[stepIndex];
59
+ const manifest = loadServiceManifest(step.path);
60
+ // 1. Check if service is already up
61
+ const alreadyUp = await isServiceUp(step.checkUrl);
62
+ if (alreadyUp) {
63
+ setCurrentStep((prev) => prev + 1);
64
+ return;
65
+ }
66
+ // 2. Install the dependencies
67
+ if (manifest.commands?.install) {
68
+ try {
69
+ await runCommands(manifest.commands.install, step.path);
70
+ }
71
+ catch (err) {
72
+ setStatus('Failed');
73
+ console.error(`Error running install commands for ${step.label}`, err);
74
+ process.exit(1);
75
+ }
76
+ }
77
+ // 3. Start the service
78
+ const startCommand = manifest.commands?.dev;
79
+ if (startCommand) {
80
+ const devCommandStr = Array.isArray(startCommand)
81
+ ? startCommand.join(' && ')
82
+ : startCommand;
83
+ const serviceProcess = execa(devCommandStr, {
84
+ cwd: step.path,
85
+ stdio: ['ignore', 'pipe', 'pipe'],
86
+ shell: true,
87
+ });
88
+ // 4. Poll until service is ready
89
+ return new Promise((resolve, reject) => {
90
+ let attempts = 0;
91
+ const maxAttempts = 30;
92
+ const interval = 3000;
93
+ const poll = async () => {
94
+ attempts++;
95
+ if (await isServiceUp(step.checkUrl)) {
96
+ setCurrentStep((prev) => prev + 1);
97
+ resolve();
98
+ return;
99
+ }
100
+ if (attempts >= maxAttempts) {
101
+ reject(new Error(`${step.label} did not start in time.`));
102
+ return;
103
+ }
104
+ setTimeout(poll, interval);
105
+ };
106
+ poll();
107
+ serviceProcess.catch((err) => reject(err));
108
+ });
109
+ }
110
+ }
111
+ async function startDashboardServices() {
112
+ try {
113
+ for (let i = 0; i < steps.length; i++) {
114
+ await runStep(i);
115
+ }
116
+ setStatus('Done');
117
+ await open(steps[steps.length - 1].checkUrl);
118
+ }
119
+ catch (err) {
120
+ setStatus('Failed');
121
+ console.error('Error:', err);
122
+ process.exit(1);
123
+ }
124
+ }
125
+ useEffect(() => {
126
+ startDashboardServices();
127
+ }, []);
128
+ useEffect(() => {
129
+ if (status !== 'Running') {
130
+ return;
131
+ }
132
+ const interval = setInterval(() => {
133
+ setDotsIndex((prev) => (prev + 1) % dotFrames.length);
134
+ }, 300);
135
+ return () => clearInterval(interval);
136
+ }, [status]);
137
+ return (_jsxs(_Fragment, { children: [_jsx(Text, { color: "yellow", children: "\u23F3 Starting Dashboard" }), steps.map((step, i) => {
138
+ if (i < currentStep) {
139
+ return (_jsxs(Text, { color: "green", children: ["\u2705 Step ", i + 1, ": ", step.label] }, i));
140
+ }
141
+ else if (i === currentStep && status === 'Running') {
142
+ return (_jsxs(Text, { children: ["Step ", i + 1, ": ", step.label, dotFrames[dotsIndex]] }, i));
143
+ }
144
+ else {
145
+ return null;
146
+ }
147
+ }), status === 'Done' && (_jsx(Text, { color: "green", children: "\uD83C\uDF89 Dashboard is up and running!" })), status === 'Failed' && (_jsx(Text, { color: "red", children: "\u274C Failed to start Dashboard" }))] }));
148
+ };
149
+ export default DashboardCLI;
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ declare const GeneratorUI: React.FC;
3
+ export default GeneratorUI;
@@ -0,0 +1,167 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text, Box, useInput, useApp } from 'ink';
3
+ import SelectInput from 'ink-select-input';
4
+ import * as React from 'react';
5
+ import { execa } from 'execa';
6
+ import { getUIGeneratorChoices, UI_CONFIG } from '../commands/generate/config.js';
7
+ const GeneratorUI = () => {
8
+ const [state, setState] = React.useState({
9
+ error: null,
10
+ });
11
+ const { exit } = useApp();
12
+ const generatorChoices = getUIGeneratorChoices();
13
+ useInput((input, key) => {
14
+ if (key.escape || input === 'q') {
15
+ // Exit the UI
16
+ exit();
17
+ }
18
+ });
19
+ const handleGeneratorTypeSelect = async (choice) => {
20
+ if (choice.value === 'back') {
21
+ // Exit the UI
22
+ exit();
23
+ return;
24
+ }
25
+ if (choice.value === 'project') {
26
+ try {
27
+ // Exit the UI and run the CLI command
28
+ exit();
29
+ // Run the CLI command
30
+ // We need to spawn this in a way that doesn't interfere with the current process
31
+ setTimeout(() => {
32
+ execa('ark', ['generate', 'project'], {
33
+ stdio: 'inherit',
34
+ cwd: process.cwd(),
35
+ }).catch((error) => {
36
+ console.error('Failed to run generator:', error.message);
37
+ process.exit(1);
38
+ });
39
+ }, 100); // Small delay to let UI exit cleanly
40
+ }
41
+ catch (error) {
42
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
43
+ setState((prev) => ({
44
+ ...prev,
45
+ error: errorMessage,
46
+ }));
47
+ }
48
+ }
49
+ if (choice.value === 'agent') {
50
+ try {
51
+ // Exit the UI and run the CLI command
52
+ exit();
53
+ // Run the CLI command
54
+ setTimeout(() => {
55
+ execa('ark', ['generate', 'agent'], {
56
+ stdio: 'inherit',
57
+ cwd: process.cwd(),
58
+ }).catch((error) => {
59
+ console.error('Failed to run generator:', error.message);
60
+ process.exit(1);
61
+ });
62
+ }, 100); // Small delay to let UI exit cleanly
63
+ }
64
+ catch (error) {
65
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
66
+ setState((prev) => ({
67
+ ...prev,
68
+ error: errorMessage,
69
+ }));
70
+ }
71
+ }
72
+ if (choice.value === 'team') {
73
+ try {
74
+ // Exit the UI and run the CLI command
75
+ exit();
76
+ // Run the CLI command
77
+ setTimeout(() => {
78
+ execa('ark', ['generate', 'team'], {
79
+ stdio: 'inherit',
80
+ cwd: process.cwd(),
81
+ }).catch((error) => {
82
+ console.error('Failed to run generator:', error.message);
83
+ process.exit(1);
84
+ });
85
+ }, 100); // Small delay to let UI exit cleanly
86
+ }
87
+ catch (error) {
88
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
89
+ setState((prev) => ({
90
+ ...prev,
91
+ error: errorMessage,
92
+ }));
93
+ }
94
+ }
95
+ if (choice.value === 'query') {
96
+ try {
97
+ // Exit the UI and run the CLI command
98
+ exit();
99
+ // Run the CLI command
100
+ setTimeout(() => {
101
+ execa('ark', ['generate', 'query'], {
102
+ stdio: 'inherit',
103
+ cwd: process.cwd(),
104
+ }).catch((error) => {
105
+ console.error('Failed to run generator:', error.message);
106
+ process.exit(1);
107
+ });
108
+ }, 100); // Small delay to let UI exit cleanly
109
+ }
110
+ catch (error) {
111
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
112
+ setState((prev) => ({
113
+ ...prev,
114
+ error: errorMessage,
115
+ }));
116
+ }
117
+ }
118
+ if (choice.value === 'mcp-server') {
119
+ try {
120
+ // Exit the UI and run the CLI command
121
+ exit();
122
+ // Run the CLI command
123
+ setTimeout(() => {
124
+ execa('ark', ['generate', 'mcp-server'], {
125
+ stdio: 'inherit',
126
+ cwd: process.cwd(),
127
+ }).catch((error) => {
128
+ console.error('Failed to run generator:', error.message);
129
+ process.exit(1);
130
+ });
131
+ }, 100); // Small delay to let UI exit cleanly
132
+ }
133
+ catch (error) {
134
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
135
+ setState((prev) => ({
136
+ ...prev,
137
+ error: errorMessage,
138
+ }));
139
+ }
140
+ }
141
+ if (choice.value === 'marketplace') {
142
+ try {
143
+ // Exit the UI and run the CLI command
144
+ exit();
145
+ // Run the CLI command
146
+ setTimeout(() => {
147
+ execa('ark', ['generate', 'marketplace'], {
148
+ stdio: 'inherit',
149
+ cwd: process.cwd(),
150
+ }).catch((error) => {
151
+ console.error('Failed to run generator:', error.message);
152
+ process.exit(1);
153
+ });
154
+ }, 100); // Small delay to let UI exit cleanly
155
+ }
156
+ catch (error) {
157
+ const errorMessage = error instanceof Error ? error.message : 'Failed to launch generator';
158
+ setState((prev) => ({
159
+ ...prev,
160
+ error: errorMessage,
161
+ }));
162
+ }
163
+ }
164
+ };
165
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: UI_CONFIG.colors.primary, bold: true, children: [UI_CONFIG.icons.generator, " Generator - Create new ARK resources"] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: UI_CONFIG.colors.secondary, children: UI_CONFIG.messages.generatorTypePrompt }) }), state.error && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: UI_CONFIG.colors.error, children: [UI_CONFIG.icons.error, " ", state.error] }) })), _jsx(SelectInput, { items: generatorChoices, onSelect: handleGeneratorTypeSelect }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press ESC or 'q' to exit" }) })] }));
166
+ };
167
+ export default GeneratorUI;
@@ -0,0 +1,48 @@
1
+ import { StatusData, CommandVersionConfig } from '../lib/types.js';
2
+ import { ArkClient } from '../lib/arkClient.js';
3
+ export declare const getNodeVersion: () => CommandVersionConfig;
4
+ export declare const getNpmVersion: () => CommandVersionConfig;
5
+ export declare const getKubectlVersion: () => CommandVersionConfig;
6
+ export declare const getDockerVersion: () => CommandVersionConfig;
7
+ export declare const getHelmVersion: () => CommandVersionConfig;
8
+ export declare class StatusChecker {
9
+ private arkClient;
10
+ private kubernetesManager;
11
+ constructor(arkClient: ArkClient);
12
+ /**
13
+ * Check if a command is available in the system
14
+ */
15
+ private isCommandAvailable;
16
+ /**
17
+ * Get version of a command
18
+ */
19
+ private getCommandVersion;
20
+ /**
21
+ * Check health of a service by URL
22
+ */
23
+ private checkServiceHealth;
24
+ /**
25
+ * Check if ark-api is running and healthy
26
+ */
27
+ private checkArkApi;
28
+ /**
29
+ * Return a "not installed" status for a service
30
+ */
31
+ private createNotInstalledStatus;
32
+ /**
33
+ * Check Kubernetes service health via pods and endpoints
34
+ */
35
+ private checkKubernetesService;
36
+ /**
37
+ * Check system dependencies
38
+ */
39
+ private checkDependencies;
40
+ /**
41
+ * Run all checks and return results
42
+ */
43
+ checkAll(serviceUrls?: Record<string, string>, arkApiUrl?: string): Promise<StatusData>;
44
+ /**
45
+ * Get appropriate health check path for different service types
46
+ */
47
+ private getHealthPath;
48
+ }
@@ -0,0 +1,251 @@
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 axios from 'axios';
6
+ const execAsync = promisify(exec);
7
+ export const getNodeVersion = () => ({
8
+ command: 'node',
9
+ versionArgs: '--version',
10
+ versionExtract: (output) => output.trim(),
11
+ });
12
+ export const getNpmVersion = () => ({
13
+ command: 'npm',
14
+ versionArgs: '--version',
15
+ versionExtract: (output) => output.trim(),
16
+ });
17
+ export const getKubectlVersion = () => ({
18
+ command: 'kubectl',
19
+ versionArgs: 'version --client --output=json',
20
+ versionExtract: (output) => {
21
+ try {
22
+ const versionInfo = JSON.parse(output);
23
+ if (versionInfo.clientVersion) {
24
+ return `v${versionInfo.clientVersion.major}.${versionInfo.clientVersion.minor}`;
25
+ }
26
+ throw new Error('kubectl version output missing clientVersion field');
27
+ }
28
+ catch (e) {
29
+ throw new Error(`Failed to parse kubectl version JSON: ${e instanceof Error ? e.message : 'Unknown error'}`);
30
+ }
31
+ },
32
+ });
33
+ export const getDockerVersion = () => ({
34
+ command: 'docker',
35
+ versionArgs: '--version',
36
+ versionExtract: (output) => output.trim(),
37
+ });
38
+ export const getHelmVersion = () => ({
39
+ command: 'helm',
40
+ versionArgs: 'version --short',
41
+ versionExtract: (output) => output.trim(),
42
+ });
43
+ function createErrorServiceStatus(name, url, error, defaultStatus = 'unhealthy', defaultDetails) {
44
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
45
+ return {
46
+ name,
47
+ status: defaultStatus,
48
+ url,
49
+ details: defaultDetails || `Error: ${errorMessage}`,
50
+ };
51
+ }
52
+ export class StatusChecker {
53
+ constructor(arkClient) {
54
+ this.arkClient = arkClient;
55
+ this.kubernetesManager = new KubernetesConfigManager();
56
+ }
57
+ /**
58
+ * Check if a command is available in the system
59
+ */
60
+ async isCommandAvailable(command) {
61
+ try {
62
+ const checkCommand = process.platform === 'win32'
63
+ ? `where ${command}`
64
+ : `command -v ${command}`;
65
+ await execAsync(checkCommand);
66
+ return true;
67
+ }
68
+ catch (_error) {
69
+ return false;
70
+ }
71
+ }
72
+ /**
73
+ * Get version of a command
74
+ */
75
+ async getCommandVersion(config) {
76
+ try {
77
+ const cmd = `${config.command} ${config.versionArgs}`;
78
+ const { stdout } = await execAsync(cmd);
79
+ return config.versionExtract(stdout);
80
+ }
81
+ catch (error) {
82
+ throw new Error(`Failed to get ${config.command} version: ${error instanceof Error ? error.message : 'Unknown error'}`);
83
+ }
84
+ }
85
+ /**
86
+ * Check health of a service by URL
87
+ */
88
+ async checkServiceHealth(serviceName, serviceUrl, successMessage, healthPath = '') {
89
+ const fullUrl = `${serviceUrl}${healthPath}`;
90
+ try {
91
+ await axios.get(fullUrl, { timeout: 5000 });
92
+ return {
93
+ name: serviceName,
94
+ status: 'healthy',
95
+ url: serviceUrl,
96
+ details: successMessage,
97
+ };
98
+ }
99
+ catch (error) {
100
+ return createErrorServiceStatus(serviceName, serviceUrl, error, 'unhealthy', `${serviceName} is not running or not accessible`);
101
+ }
102
+ }
103
+ /**
104
+ * Check if ark-api is running and healthy
105
+ */
106
+ async checkArkApi(customUrl) {
107
+ const url = customUrl || this.arkClient.getBaseURL();
108
+ return this.checkServiceHealth('ark-api', url, 'ARK API is running', '/health');
109
+ }
110
+ /**
111
+ * Return a "not installed" status for a service
112
+ */
113
+ createNotInstalledStatus(serviceName) {
114
+ return {
115
+ name: serviceName,
116
+ status: 'not installed',
117
+ details: `${serviceName} is not configured or not part of this deployment`,
118
+ };
119
+ }
120
+ /**
121
+ * Check Kubernetes service health via pods and endpoints
122
+ */
123
+ async checkKubernetesService(serviceName, kubernetesServiceName, namespace = 'default') {
124
+ try {
125
+ await this.kubernetesManager.initializeConfig();
126
+ const kc = this.kubernetesManager.getKubeConfig();
127
+ const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
128
+ // Check if service exists and has endpoints
129
+ const service = await k8sApi.readNamespacedService({
130
+ name: kubernetesServiceName,
131
+ namespace,
132
+ });
133
+ const endpoints = await k8sApi.readNamespacedEndpoints({
134
+ name: kubernetesServiceName,
135
+ namespace,
136
+ });
137
+ // Check if service has ready endpoints
138
+ const readyAddresses = endpoints.subsets?.reduce((total, subset) => {
139
+ return total + (subset.addresses?.length || 0);
140
+ }, 0) || 0;
141
+ if (readyAddresses > 0) {
142
+ const serviceIP = service.spec?.clusterIP;
143
+ const servicePort = service.spec?.ports?.[0]?.port;
144
+ return {
145
+ name: serviceName,
146
+ status: 'healthy',
147
+ url: `cluster://${serviceIP}:${servicePort}`,
148
+ details: `${serviceName} running in cluster (${readyAddresses} ready endpoints)`,
149
+ };
150
+ }
151
+ else {
152
+ return {
153
+ name: serviceName,
154
+ status: 'unhealthy',
155
+ details: `${serviceName} service exists but has no ready endpoints`,
156
+ };
157
+ }
158
+ }
159
+ catch (error) {
160
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
161
+ // If service not found, it's not installed
162
+ if (errorMessage.includes('not found')) {
163
+ return this.createNotInstalledStatus(serviceName);
164
+ }
165
+ // Other errors indicate unhealthy
166
+ return {
167
+ name: serviceName,
168
+ status: 'unhealthy',
169
+ details: `Failed to check ${serviceName}: ${errorMessage}`,
170
+ };
171
+ }
172
+ }
173
+ /**
174
+ * Check system dependencies
175
+ */
176
+ async checkDependencies() {
177
+ const dependencies = [
178
+ { name: 'node', ...getNodeVersion() },
179
+ { name: 'npm', ...getNpmVersion() },
180
+ { name: 'kubectl', ...getKubectlVersion() },
181
+ { name: 'docker', ...getDockerVersion() },
182
+ { name: 'helm', ...getHelmVersion() },
183
+ ];
184
+ const results = [];
185
+ for (const dep of dependencies) {
186
+ const installed = await this.isCommandAvailable(dep.command);
187
+ const version = installed
188
+ ? await this.getCommandVersion({
189
+ command: dep.command,
190
+ versionArgs: dep.versionArgs,
191
+ versionExtract: dep.versionExtract,
192
+ })
193
+ : undefined;
194
+ results.push({
195
+ name: dep.name,
196
+ installed,
197
+ version,
198
+ details: installed
199
+ ? `Found ${dep.name} ${version}`
200
+ : `${dep.name} not found in PATH`,
201
+ });
202
+ }
203
+ return results;
204
+ }
205
+ /**
206
+ * Run all checks and return results
207
+ */
208
+ async checkAll(serviceUrls = {}, arkApiUrl) {
209
+ // Always check ark-api if provided
210
+ const serviceChecks = [];
211
+ if (arkApiUrl) {
212
+ serviceChecks.push(this.checkArkApi(arkApiUrl));
213
+ }
214
+ // Dynamically check all discovered services
215
+ for (const [serviceName, serviceUrl] of Object.entries(serviceUrls)) {
216
+ if (serviceName === 'ark-api' && arkApiUrl) {
217
+ // Skip if we already added ark-api above
218
+ continue;
219
+ }
220
+ serviceChecks.push(this.checkServiceHealth(serviceName, serviceUrl, `${serviceName} is running`, this.getHealthPath(serviceName)));
221
+ }
222
+ // Always check dependencies
223
+ const dependenciesCheck = this.checkDependencies();
224
+ const [dependencies, ...serviceStatuses] = await Promise.all([
225
+ dependenciesCheck,
226
+ ...serviceChecks,
227
+ ]);
228
+ return {
229
+ services: serviceStatuses,
230
+ dependencies,
231
+ };
232
+ }
233
+ /**
234
+ * Get appropriate health check path for different service types
235
+ */
236
+ getHealthPath(serviceName) {
237
+ // Some services might need specific health check paths
238
+ switch (serviceName) {
239
+ case 'ark-api':
240
+ return '/health';
241
+ case 'ark-api-a2a':
242
+ return '/health'; // ark-api-a2a has a working /health endpoint
243
+ case 'ark-dashboard':
244
+ return ''; // Dashboard typically responds to root
245
+ case 'langfuse':
246
+ return ''; // Langfuse responds to root path
247
+ default:
248
+ return ''; // Default to root path
249
+ }
250
+ }
251
+ }
@@ -0,0 +1,42 @@
1
+ import { ArkConfig, KubernetesConfig } from './lib/types.js';
2
+ /**
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.
6
+ *
7
+ * Example usage:
8
+ * DEBUG=ark:config ark check status
9
+ * DEBUG=ark:* ark dashboard
10
+ */
11
+ export declare class ConfigManager {
12
+ private configDir;
13
+ private configFile;
14
+ private kubernetesManager;
15
+ private gatewayManager;
16
+ private kubeConfig;
17
+ 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
+ getApiBaseUrl(): Promise<string>;
26
+ /**
27
+ * Check if localhost-gateway is running by testing port 8080
28
+ */
29
+ private isLocalhostGatewayRunning;
30
+ /**
31
+ * Construct standard localhost-gateway URLs for known ARK services
32
+ */
33
+ private getLocalhostGatewayUrls;
34
+ private initKubernetesConfig;
35
+ getKubernetesConfig(): Promise<KubernetesConfig | null>;
36
+ testClusterAccess(): Promise<boolean>;
37
+ /**
38
+ * Discover service URLs from ark-api service discovery
39
+ */
40
+ private discoverServicesFromApi;
41
+ getServiceUrls(): Promise<Record<string, string>>;
42
+ }