@agents-at-scale/ark 0.1.35-rc.1 → 0.1.35-rc1
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.
- package/dist/arkServices.d.ts +4 -12
- package/dist/arkServices.js +19 -34
- package/dist/arkServices.spec.d.ts +1 -0
- package/dist/arkServices.spec.js +24 -0
- package/dist/commands/agents/index.d.ts +2 -1
- package/dist/commands/agents/index.js +2 -7
- package/dist/commands/agents/index.spec.d.ts +1 -0
- package/dist/commands/agents/index.spec.js +67 -0
- package/dist/commands/chat/index.d.ts +2 -1
- package/dist/commands/chat/index.js +5 -21
- package/dist/commands/cluster/get.spec.d.ts +1 -0
- package/dist/commands/cluster/get.spec.js +92 -0
- package/dist/commands/cluster/index.d.ts +2 -1
- package/dist/commands/cluster/index.js +1 -1
- package/dist/commands/cluster/index.spec.d.ts +1 -0
- package/dist/commands/cluster/index.spec.js +24 -0
- package/dist/commands/completion/index.d.ts +2 -1
- package/dist/commands/completion/index.js +1 -1
- package/dist/commands/completion/index.spec.d.ts +1 -0
- package/dist/commands/completion/index.spec.js +34 -0
- package/dist/commands/config/index.d.ts +2 -1
- package/dist/commands/config/index.js +2 -2
- package/dist/commands/config/index.spec.d.ts +1 -0
- package/dist/commands/config/index.spec.js +78 -0
- package/dist/commands/dashboard/index.d.ts +2 -1
- package/dist/commands/dashboard/index.js +1 -1
- package/dist/commands/dev/index.d.ts +2 -1
- package/dist/commands/dev/index.js +1 -1
- package/dist/commands/dev/tool-generate.spec.d.ts +1 -0
- package/dist/commands/dev/tool-generate.spec.js +163 -0
- package/dist/commands/dev/tool.spec.d.ts +1 -0
- package/dist/commands/dev/tool.spec.js +48 -0
- package/dist/commands/generate/generators/project.js +22 -41
- package/dist/commands/generate/index.d.ts +2 -1
- package/dist/commands/generate/index.js +1 -1
- package/dist/commands/install/index.d.ts +4 -2
- package/dist/commands/install/index.js +215 -78
- package/dist/commands/install/index.spec.d.ts +1 -0
- package/dist/commands/install/index.spec.js +135 -0
- package/dist/commands/models/create.spec.d.ts +1 -0
- package/dist/commands/models/create.spec.js +125 -0
- package/dist/commands/models/index.d.ts +2 -1
- package/dist/commands/models/index.js +2 -7
- package/dist/commands/models/index.spec.d.ts +1 -0
- package/dist/commands/models/index.spec.js +76 -0
- package/dist/commands/routes/index.d.ts +2 -1
- package/dist/commands/routes/index.js +1 -9
- package/dist/commands/status/index.d.ts +3 -2
- package/dist/commands/status/index.js +210 -11
- package/dist/commands/targets/index.d.ts +2 -1
- package/dist/commands/targets/index.js +1 -1
- package/dist/commands/targets/index.spec.d.ts +1 -0
- package/dist/commands/targets/index.spec.js +105 -0
- package/dist/commands/teams/index.d.ts +2 -1
- package/dist/commands/teams/index.js +2 -7
- package/dist/commands/teams/index.spec.d.ts +1 -0
- package/dist/commands/teams/index.spec.js +70 -0
- package/dist/commands/tools/index.d.ts +2 -1
- package/dist/commands/tools/index.js +2 -7
- package/dist/commands/tools/index.spec.d.ts +1 -0
- package/dist/commands/tools/index.spec.js +70 -0
- package/dist/commands/uninstall/index.d.ts +2 -1
- package/dist/commands/uninstall/index.js +61 -38
- package/dist/commands/uninstall/index.spec.d.ts +1 -0
- package/dist/commands/uninstall/index.spec.js +117 -0
- package/dist/components/ChatUI.js +4 -4
- package/dist/components/statusChecker.d.ts +5 -12
- package/dist/components/statusChecker.js +172 -89
- package/dist/config.d.ts +3 -22
- package/dist/config.js +7 -151
- package/dist/index.js +22 -19
- package/dist/lib/arkServiceProxy.js +4 -2
- package/dist/lib/arkStatus.d.ts +5 -0
- package/dist/lib/arkStatus.js +61 -2
- package/dist/lib/arkStatus.spec.d.ts +1 -0
- package/dist/lib/arkStatus.spec.js +49 -0
- package/dist/lib/chatClient.js +1 -3
- package/dist/lib/cluster.js +11 -14
- package/dist/lib/cluster.spec.d.ts +1 -0
- package/dist/lib/cluster.spec.js +338 -0
- package/dist/lib/commandUtils.js +7 -7
- package/dist/lib/commands.d.ts +16 -0
- package/dist/lib/commands.js +29 -0
- package/dist/lib/commands.spec.d.ts +1 -0
- package/dist/lib/commands.spec.js +146 -0
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.js +6 -4
- package/dist/lib/config.spec.d.ts +1 -0
- package/dist/lib/config.spec.js +99 -0
- package/dist/lib/consts.d.ts +0 -1
- package/dist/lib/consts.js +0 -2
- package/dist/lib/consts.spec.d.ts +1 -0
- package/dist/lib/consts.spec.js +15 -0
- package/dist/lib/errors.js +1 -1
- package/dist/lib/errors.spec.d.ts +1 -0
- package/dist/lib/errors.spec.js +221 -0
- package/dist/lib/exec.d.ts +0 -4
- package/dist/lib/exec.js +0 -11
- package/dist/lib/output.spec.d.ts +1 -0
- package/dist/lib/output.spec.js +123 -0
- package/dist/lib/portUtils.d.ts +8 -0
- package/dist/lib/portUtils.js +39 -0
- package/dist/lib/startup.d.ts +5 -0
- package/dist/lib/startup.js +73 -0
- package/dist/lib/startup.spec.d.ts +1 -0
- package/dist/lib/startup.spec.js +168 -0
- package/dist/lib/types.d.ts +2 -0
- package/dist/ui/AgentSelector.d.ts +8 -0
- package/dist/ui/AgentSelector.js +53 -0
- package/dist/ui/MainMenu.d.ts +5 -1
- package/dist/ui/MainMenu.js +117 -54
- package/dist/ui/ModelSelector.d.ts +8 -0
- package/dist/ui/ModelSelector.js +53 -0
- package/dist/ui/TeamSelector.d.ts +8 -0
- package/dist/ui/TeamSelector.js +55 -0
- package/dist/ui/ToolSelector.d.ts +8 -0
- package/dist/ui/ToolSelector.js +53 -0
- package/dist/ui/statusFormatter.d.ts +22 -10
- package/dist/ui/statusFormatter.js +37 -109
- package/dist/ui/statusFormatter.spec.d.ts +1 -0
- package/dist/ui/statusFormatter.spec.js +58 -0
- package/package.json +3 -3
package/dist/arkServices.d.ts
CHANGED
|
@@ -5,15 +5,15 @@ export interface ArkService {
|
|
|
5
5
|
name: string;
|
|
6
6
|
helmReleaseName: string;
|
|
7
7
|
description: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
gatewayUrl?: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
namespace?: string;
|
|
11
10
|
chartPath?: string;
|
|
12
11
|
installArgs?: string[];
|
|
13
12
|
k8sServiceName?: string;
|
|
14
13
|
k8sServicePort?: number;
|
|
15
14
|
k8sPortForwardLocalPort?: number;
|
|
16
15
|
k8sDeploymentName?: string;
|
|
16
|
+
k8sDevDeploymentName?: string;
|
|
17
17
|
}
|
|
18
18
|
export interface ServiceCollection {
|
|
19
19
|
[key: string]: ArkService;
|
|
@@ -37,14 +37,6 @@ export declare const arkDependencies: DependencyCollection;
|
|
|
37
37
|
*/
|
|
38
38
|
export declare const arkServices: ServiceCollection;
|
|
39
39
|
/**
|
|
40
|
-
* Get services that can be installed via Helm charts
|
|
40
|
+
* Get services that can be installed via Helm charts (only enabled services)
|
|
41
41
|
*/
|
|
42
42
|
export declare function getInstallableServices(): ServiceCollection;
|
|
43
|
-
/**
|
|
44
|
-
* Get services that can be checked for status
|
|
45
|
-
*/
|
|
46
|
-
export declare function getStatusCheckableServices(): Record<string, string>;
|
|
47
|
-
/**
|
|
48
|
-
* Get health check path for a specific service
|
|
49
|
-
*/
|
|
50
|
-
export declare function getHealthPath(serviceName: string): string;
|
package/dist/arkServices.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized ARK service definitions used by both install and status commands
|
|
3
3
|
*/
|
|
4
|
-
const LOCALHOST_GATEWAY_PORT = 8080;
|
|
5
4
|
const REGISTRY_BASE = 'oci://ghcr.io/mckinsey/agents-at-scale-ark/charts';
|
|
6
5
|
/**
|
|
7
6
|
* Dependencies that should be installed before ARK services
|
|
@@ -61,93 +60,79 @@ export const arkServices = {
|
|
|
61
60
|
name: 'ark-controller',
|
|
62
61
|
helmReleaseName: 'ark-controller',
|
|
63
62
|
description: 'Core ARK controller for managing AI resources',
|
|
63
|
+
enabled: true,
|
|
64
64
|
namespace: 'ark-system',
|
|
65
65
|
chartPath: `${REGISTRY_BASE}/ark-controller`,
|
|
66
66
|
installArgs: ['--create-namespace', '--set', 'rbac.enable=true'],
|
|
67
67
|
k8sDeploymentName: 'ark-controller',
|
|
68
|
+
k8sDevDeploymentName: 'ark-controller-devspace',
|
|
68
69
|
},
|
|
69
70
|
'ark-api': {
|
|
70
71
|
name: 'ark-api',
|
|
71
72
|
helmReleaseName: 'ark-api',
|
|
72
73
|
description: 'ARK API service for interacting with ARK resources',
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
gatewayUrl: `http://ark-api.127.0.0.1.nip.io:${LOCALHOST_GATEWAY_PORT}`,
|
|
74
|
+
enabled: true,
|
|
75
|
+
// namespace: undefined - uses current context namespace
|
|
76
76
|
chartPath: `${REGISTRY_BASE}/ark-api`,
|
|
77
77
|
installArgs: [],
|
|
78
78
|
k8sServiceName: 'ark-api',
|
|
79
79
|
k8sServicePort: 80,
|
|
80
|
-
k8sPortForwardLocalPort: 34780,
|
|
81
80
|
k8sDeploymentName: 'ark-api',
|
|
81
|
+
k8sDevDeploymentName: 'ark-api-devspace',
|
|
82
|
+
k8sPortForwardLocalPort: 34780,
|
|
82
83
|
},
|
|
83
84
|
'ark-dashboard': {
|
|
84
85
|
name: 'ark-dashboard',
|
|
85
86
|
helmReleaseName: 'ark-dashboard',
|
|
86
87
|
description: 'Web-based dashboard for ARK',
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
gatewayUrl: `http://dashboard.127.0.0.1.nip.io:${LOCALHOST_GATEWAY_PORT}`,
|
|
88
|
+
enabled: true,
|
|
89
|
+
// namespace: undefined - uses current context namespace
|
|
90
90
|
chartPath: `${REGISTRY_BASE}/ark-dashboard`,
|
|
91
91
|
installArgs: [],
|
|
92
92
|
k8sServiceName: 'ark-dashboard',
|
|
93
93
|
k8sServicePort: 3000,
|
|
94
|
-
k8sPortForwardLocalPort: 3274,
|
|
95
94
|
k8sDeploymentName: 'ark-dashboard',
|
|
95
|
+
k8sDevDeploymentName: 'ark-dashboard-devspace',
|
|
96
|
+
k8sPortForwardLocalPort: 3274,
|
|
96
97
|
},
|
|
97
98
|
'ark-api-a2a': {
|
|
98
99
|
name: 'ark-api-a2a',
|
|
99
100
|
helmReleaseName: 'ark-api-a2a',
|
|
100
101
|
description: 'ARK API agent-to-agent communication service',
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
gatewayUrl: `http://ark-api-a2a.127.0.0.1.nip.io:${LOCALHOST_GATEWAY_PORT}`,
|
|
102
|
+
enabled: false, // Disabled - not currently used
|
|
103
|
+
// namespace: undefined - uses current context namespace
|
|
104
104
|
// Note: This service might be installed as part of ark-api or separately
|
|
105
105
|
},
|
|
106
106
|
'ark-mcp': {
|
|
107
107
|
name: 'ark-mcp',
|
|
108
108
|
helmReleaseName: 'ark-mcp',
|
|
109
109
|
description: 'MCP (Model Context Protocol) services for ARK',
|
|
110
|
-
|
|
110
|
+
enabled: true,
|
|
111
|
+
// namespace: undefined - uses current context namespace
|
|
111
112
|
chartPath: `${REGISTRY_BASE}/ark-mcp`,
|
|
112
113
|
installArgs: [],
|
|
114
|
+
k8sDeploymentName: 'ark-mcp',
|
|
115
|
+
k8sDevDeploymentName: 'ark-mcp-devspace',
|
|
113
116
|
},
|
|
114
117
|
'localhost-gateway': {
|
|
115
118
|
name: 'localhost-gateway',
|
|
116
119
|
helmReleaseName: 'localhost-gateway',
|
|
117
120
|
description: 'Gateway for local cluster access',
|
|
121
|
+
enabled: true,
|
|
118
122
|
namespace: 'ark-system',
|
|
119
123
|
chartPath: `${REGISTRY_BASE}/localhost-gateway`,
|
|
120
124
|
installArgs: [],
|
|
121
125
|
},
|
|
122
126
|
};
|
|
123
127
|
/**
|
|
124
|
-
* Get services that can be installed via Helm charts
|
|
128
|
+
* Get services that can be installed via Helm charts (only enabled services)
|
|
125
129
|
*/
|
|
126
130
|
export function getInstallableServices() {
|
|
127
131
|
const installable = {};
|
|
128
132
|
for (const [key, service] of Object.entries(arkServices)) {
|
|
129
|
-
if (service.chartPath) {
|
|
133
|
+
if (service.enabled && service.chartPath) {
|
|
130
134
|
installable[key] = service;
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
return installable;
|
|
134
138
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Get services that can be checked for status
|
|
137
|
-
*/
|
|
138
|
-
export function getStatusCheckableServices() {
|
|
139
|
-
const statusServices = {};
|
|
140
|
-
for (const [key, service] of Object.entries(arkServices)) {
|
|
141
|
-
if (service.gatewayUrl) {
|
|
142
|
-
statusServices[key] = service.gatewayUrl;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return statusServices;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Get health check path for a specific service
|
|
149
|
-
*/
|
|
150
|
-
export function getHealthPath(serviceName) {
|
|
151
|
-
const service = arkServices[serviceName];
|
|
152
|
-
return service?.healthPath || '';
|
|
153
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { arkDependencies, arkServices, getInstallableServices, } from './arkServices.js';
|
|
2
|
+
describe('arkServices', () => {
|
|
3
|
+
it('exports arkDependencies with expected structure', () => {
|
|
4
|
+
expect(arkDependencies).toBeDefined();
|
|
5
|
+
expect(arkDependencies['cert-manager']).toBeDefined();
|
|
6
|
+
expect(arkDependencies['cert-manager'].command).toBe('helm');
|
|
7
|
+
});
|
|
8
|
+
it('exports arkServices with expected structure', () => {
|
|
9
|
+
expect(arkServices).toBeDefined();
|
|
10
|
+
expect(arkServices['ark-controller']).toBeDefined();
|
|
11
|
+
expect(arkServices['ark-controller'].namespace).toBe('ark-system');
|
|
12
|
+
// User services should have undefined namespace (use current context)
|
|
13
|
+
expect(arkServices['ark-api'].namespace).toBeUndefined();
|
|
14
|
+
expect(arkServices['ark-dashboard'].namespace).toBeUndefined();
|
|
15
|
+
// System services should have explicit namespace
|
|
16
|
+
expect(arkServices['localhost-gateway'].namespace).toBe('ark-system');
|
|
17
|
+
});
|
|
18
|
+
it('getInstallableServices returns services with chartPath', () => {
|
|
19
|
+
const installable = getInstallableServices();
|
|
20
|
+
expect(installable['ark-controller']).toBeDefined();
|
|
21
|
+
expect(installable['ark-api']).toBeDefined();
|
|
22
|
+
expect(installable['ark-api-a2a']).toBeUndefined(); // no chartPath
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -25,16 +25,11 @@ async function listAgents(options) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
|
-
|
|
29
|
-
output.error('Agent CRDs not installed. Is the ARK controller running?');
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
output.error('fetching agents:', error instanceof Error ? error.message : error);
|
|
33
|
-
}
|
|
28
|
+
output.error('fetching agents:', error instanceof Error ? error.message : error);
|
|
34
29
|
process.exit(1);
|
|
35
30
|
}
|
|
36
31
|
}
|
|
37
|
-
export function createAgentsCommand() {
|
|
32
|
+
export function createAgentsCommand(_) {
|
|
38
33
|
const agentsCommand = new Command('agents');
|
|
39
34
|
agentsCommand
|
|
40
35
|
.description('list available agents')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
warning: jest.fn(),
|
|
9
|
+
error: jest.fn(),
|
|
10
|
+
};
|
|
11
|
+
jest.unstable_mockModule('../../lib/output.js', () => ({
|
|
12
|
+
default: mockOutput,
|
|
13
|
+
}));
|
|
14
|
+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
15
|
+
throw new Error('process.exit called');
|
|
16
|
+
}));
|
|
17
|
+
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
18
|
+
const { createAgentsCommand } = await import('./index.js');
|
|
19
|
+
describe('agents command', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it('creates command with correct structure', () => {
|
|
24
|
+
const command = createAgentsCommand({});
|
|
25
|
+
expect(command).toBeInstanceOf(Command);
|
|
26
|
+
expect(command.name()).toBe('agents');
|
|
27
|
+
});
|
|
28
|
+
it('lists agents in text format', async () => {
|
|
29
|
+
const mockAgents = {
|
|
30
|
+
items: [{ metadata: { name: 'agent1' } }, { metadata: { name: 'agent2' } }],
|
|
31
|
+
};
|
|
32
|
+
mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockAgents) });
|
|
33
|
+
const command = createAgentsCommand({});
|
|
34
|
+
await command.parseAsync(['node', 'test']);
|
|
35
|
+
expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'agents', '-o', 'json'], { stdio: 'pipe' });
|
|
36
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('agent1');
|
|
37
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('agent2');
|
|
38
|
+
});
|
|
39
|
+
it('lists agents in json format', async () => {
|
|
40
|
+
const mockAgents = {
|
|
41
|
+
items: [{ metadata: { name: 'agent1' } }],
|
|
42
|
+
};
|
|
43
|
+
mockExeca.mockResolvedValue({ stdout: JSON.stringify(mockAgents) });
|
|
44
|
+
const command = createAgentsCommand({});
|
|
45
|
+
await command.parseAsync(['node', 'test', '-o', 'json']);
|
|
46
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(mockAgents.items, null, 2));
|
|
47
|
+
});
|
|
48
|
+
it('shows warning when no agents', async () => {
|
|
49
|
+
mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
|
|
50
|
+
const command = createAgentsCommand({});
|
|
51
|
+
await command.parseAsync(['node', 'test']);
|
|
52
|
+
expect(mockOutput.warning).toHaveBeenCalledWith('no agents available');
|
|
53
|
+
});
|
|
54
|
+
it('handles errors', async () => {
|
|
55
|
+
mockExeca.mockRejectedValue(new Error('kubectl failed'));
|
|
56
|
+
const command = createAgentsCommand({});
|
|
57
|
+
await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
|
|
58
|
+
expect(mockOutput.error).toHaveBeenCalledWith('fetching agents:', 'kubectl failed');
|
|
59
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
60
|
+
});
|
|
61
|
+
it('list subcommand works', async () => {
|
|
62
|
+
mockExeca.mockResolvedValue({ stdout: JSON.stringify({ items: [] }) });
|
|
63
|
+
const command = createAgentsCommand({});
|
|
64
|
+
await command.parseAsync(['node', 'test', 'list']);
|
|
65
|
+
expect(mockExeca).toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -3,32 +3,16 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { render } from 'ink';
|
|
4
4
|
import ChatUI from '../../components/ChatUI.js';
|
|
5
5
|
import { ArkApiProxy } from '../../lib/arkApiProxy.js';
|
|
6
|
-
import { loadConfig } from '../../lib/config.js';
|
|
7
6
|
import output from '../../lib/output.js';
|
|
8
|
-
export function createChatCommand() {
|
|
7
|
+
export function createChatCommand(config) {
|
|
9
8
|
const chatCommand = new Command('chat');
|
|
10
9
|
chatCommand
|
|
11
10
|
.description('Start an interactive chat session with ARK agents or models')
|
|
12
11
|
.argument('[target]', 'Target to connect to (e.g., agent/sample-agent, model/default)')
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
let initialTargetId;
|
|
18
|
-
if (targetArg) {
|
|
19
|
-
// Direct target argument (e.g., "agent/sample-agent")
|
|
20
|
-
initialTargetId = targetArg;
|
|
21
|
-
}
|
|
22
|
-
else if (options.agent) {
|
|
23
|
-
// Agent option
|
|
24
|
-
initialTargetId = `agent/${options.agent}`;
|
|
25
|
-
}
|
|
26
|
-
else if (options.model) {
|
|
27
|
-
// Model option
|
|
28
|
-
initialTargetId = `model/${options.model}`;
|
|
29
|
-
}
|
|
30
|
-
// Load config
|
|
31
|
-
const config = loadConfig();
|
|
12
|
+
.action(async (targetArg) => {
|
|
13
|
+
// Direct target argument (e.g., "agent/sample-agent")
|
|
14
|
+
const initialTargetId = targetArg;
|
|
15
|
+
// Config is passed from main
|
|
32
16
|
// Initialize proxy first - no spinner, just let ChatUI handle loading state
|
|
33
17
|
try {
|
|
34
18
|
const proxy = new ArkApiProxy();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
const mockGetClusterInfo = jest.fn();
|
|
4
|
+
jest.unstable_mockModule('../../lib/cluster.js', () => ({
|
|
5
|
+
getClusterInfo: mockGetClusterInfo,
|
|
6
|
+
}));
|
|
7
|
+
const mockOutput = {
|
|
8
|
+
error: jest.fn(),
|
|
9
|
+
};
|
|
10
|
+
jest.unstable_mockModule('../../lib/output.js', () => ({
|
|
11
|
+
default: mockOutput,
|
|
12
|
+
}));
|
|
13
|
+
const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {
|
|
14
|
+
throw new Error('process.exit called');
|
|
15
|
+
}));
|
|
16
|
+
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
17
|
+
const { createGetCommand } = await import('./get.js');
|
|
18
|
+
describe('cluster get command', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
it('creates command with correct structure', () => {
|
|
23
|
+
const command = createGetCommand();
|
|
24
|
+
expect(command).toBeInstanceOf(Command);
|
|
25
|
+
expect(command.name()).toBe('get');
|
|
26
|
+
});
|
|
27
|
+
it('displays cluster info in text format by default', async () => {
|
|
28
|
+
mockGetClusterInfo.mockResolvedValue({
|
|
29
|
+
context: 'test-cluster',
|
|
30
|
+
namespace: 'default',
|
|
31
|
+
type: 'minikube',
|
|
32
|
+
ip: '192.168.1.1',
|
|
33
|
+
});
|
|
34
|
+
const command = createGetCommand();
|
|
35
|
+
await command.parseAsync(['node', 'test']);
|
|
36
|
+
expect(mockGetClusterInfo).toHaveBeenCalledWith(undefined);
|
|
37
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('context: test-cluster');
|
|
38
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('namespace: default');
|
|
39
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('type: minikube');
|
|
40
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('ip: 192.168.1.1');
|
|
41
|
+
});
|
|
42
|
+
it('displays cluster info in json format when requested', async () => {
|
|
43
|
+
const clusterInfo = {
|
|
44
|
+
context: 'prod-cluster',
|
|
45
|
+
namespace: 'production',
|
|
46
|
+
type: 'eks',
|
|
47
|
+
ip: '10.0.0.1',
|
|
48
|
+
};
|
|
49
|
+
mockGetClusterInfo.mockResolvedValue(clusterInfo);
|
|
50
|
+
const command = createGetCommand();
|
|
51
|
+
await command.parseAsync(['node', 'test', '-o', 'json']);
|
|
52
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(JSON.stringify(clusterInfo, null, 2));
|
|
53
|
+
});
|
|
54
|
+
it('uses specified context when provided', async () => {
|
|
55
|
+
mockGetClusterInfo.mockResolvedValue({
|
|
56
|
+
context: 'custom-context',
|
|
57
|
+
namespace: 'custom',
|
|
58
|
+
type: 'kind',
|
|
59
|
+
ip: '127.0.0.1',
|
|
60
|
+
});
|
|
61
|
+
const command = createGetCommand();
|
|
62
|
+
await command.parseAsync(['node', 'test', '-c', 'custom-context']);
|
|
63
|
+
expect(mockGetClusterInfo).toHaveBeenCalledWith('custom-context');
|
|
64
|
+
});
|
|
65
|
+
it('handles missing ip gracefully', async () => {
|
|
66
|
+
mockGetClusterInfo.mockResolvedValue({
|
|
67
|
+
context: 'test-cluster',
|
|
68
|
+
namespace: 'default',
|
|
69
|
+
type: 'unknown',
|
|
70
|
+
ip: undefined,
|
|
71
|
+
});
|
|
72
|
+
const command = createGetCommand();
|
|
73
|
+
await command.parseAsync(['node', 'test']);
|
|
74
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('ip: unknown');
|
|
75
|
+
});
|
|
76
|
+
it('exits with error when cluster info has error', async () => {
|
|
77
|
+
mockGetClusterInfo.mockResolvedValue({
|
|
78
|
+
error: 'No cluster found',
|
|
79
|
+
});
|
|
80
|
+
const command = createGetCommand();
|
|
81
|
+
await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
|
|
82
|
+
expect(mockOutput.error).toHaveBeenCalledWith('getting cluster info:', 'No cluster found');
|
|
83
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
84
|
+
});
|
|
85
|
+
it('handles exceptions gracefully', async () => {
|
|
86
|
+
mockGetClusterInfo.mockRejectedValue(new Error('Connection failed'));
|
|
87
|
+
const command = createGetCommand();
|
|
88
|
+
await expect(command.parseAsync(['node', 'test'])).rejects.toThrow('process.exit called');
|
|
89
|
+
expect(mockOutput.error).toHaveBeenCalledWith('failed to get cluster info:', 'Connection failed');
|
|
90
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createGetCommand } from './get.js';
|
|
3
|
-
export function createClusterCommand() {
|
|
3
|
+
export function createClusterCommand(_) {
|
|
4
4
|
const cluster = new Command('cluster');
|
|
5
5
|
cluster.description('Cluster management commands');
|
|
6
6
|
cluster.addCommand(createGetCommand());
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
const mockCreateGetCommand = jest.fn();
|
|
4
|
+
jest.unstable_mockModule('./get.js', () => ({
|
|
5
|
+
createGetCommand: mockCreateGetCommand,
|
|
6
|
+
}));
|
|
7
|
+
const { createClusterCommand } = await import('./index.js');
|
|
8
|
+
describe('cluster command', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
mockCreateGetCommand.mockReturnValue(new Command('get'));
|
|
12
|
+
});
|
|
13
|
+
it('creates command with correct structure', () => {
|
|
14
|
+
const command = createClusterCommand({});
|
|
15
|
+
expect(command).toBeInstanceOf(Command);
|
|
16
|
+
expect(command.name()).toBe('cluster');
|
|
17
|
+
});
|
|
18
|
+
it('adds get subcommand', () => {
|
|
19
|
+
const command = createClusterCommand({});
|
|
20
|
+
expect(mockCreateGetCommand).toHaveBeenCalled();
|
|
21
|
+
const getCommand = command.commands.find((cmd) => cmd.name() === 'get');
|
|
22
|
+
expect(getCommand).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
export function createCompletionCommand() {
|
|
3
|
+
export function createCompletionCommand(_) {
|
|
4
4
|
const completion = new Command('completion');
|
|
5
5
|
completion.description('Generate shell completion scripts').action(() => {
|
|
6
6
|
console.log(chalk.cyan('Shell completion for ARK CLI'));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
4
|
+
const { createCompletionCommand } = await import('./index.js');
|
|
5
|
+
describe('completion command', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
jest.clearAllMocks();
|
|
8
|
+
});
|
|
9
|
+
it('creates command with correct structure', () => {
|
|
10
|
+
const command = createCompletionCommand({});
|
|
11
|
+
expect(command).toBeInstanceOf(Command);
|
|
12
|
+
expect(command.name()).toBe('completion');
|
|
13
|
+
});
|
|
14
|
+
it('shows help when called without subcommand', async () => {
|
|
15
|
+
const command = createCompletionCommand({});
|
|
16
|
+
await command.parseAsync(['node', 'test']);
|
|
17
|
+
// Check first call contains the title (strip ANSI color codes)
|
|
18
|
+
expect(mockConsoleLog.mock.calls[0][0]).toContain('Shell completion for ARK CLI');
|
|
19
|
+
// Check that bash completion instructions are shown
|
|
20
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('ark completion bash'));
|
|
21
|
+
});
|
|
22
|
+
it('outputs bash completion script', async () => {
|
|
23
|
+
const command = createCompletionCommand({});
|
|
24
|
+
await command.parseAsync(['node', 'test', 'bash']);
|
|
25
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('_ark_completion()'));
|
|
26
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('COMPREPLY'));
|
|
27
|
+
});
|
|
28
|
+
it('outputs zsh completion script', async () => {
|
|
29
|
+
const command = createCompletionCommand({});
|
|
30
|
+
await command.parseAsync(['node', 'test', 'zsh']);
|
|
31
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('#compdef ark'));
|
|
32
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('_ark()'));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { loadConfig, getConfigPaths, formatConfig } from '../../lib/config.js';
|
|
3
|
+
import { loadConfig, getConfigPaths, formatConfig, } from '../../lib/config.js';
|
|
4
4
|
import fs from 'fs';
|
|
5
|
-
export function createConfigCommand() {
|
|
5
|
+
export function createConfigCommand(_) {
|
|
6
6
|
const configCommand = new Command('config');
|
|
7
7
|
configCommand.description('Show current configuration').action(() => {
|
|
8
8
|
const config = loadConfig();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
const mockLoadConfig = jest.fn();
|
|
4
|
+
const mockGetConfigPaths = jest.fn();
|
|
5
|
+
const mockFormatConfig = jest.fn();
|
|
6
|
+
jest.unstable_mockModule('../../lib/config.js', () => ({
|
|
7
|
+
loadConfig: mockLoadConfig,
|
|
8
|
+
getConfigPaths: mockGetConfigPaths,
|
|
9
|
+
formatConfig: mockFormatConfig,
|
|
10
|
+
}));
|
|
11
|
+
const mockExistsSync = jest.fn();
|
|
12
|
+
jest.unstable_mockModule('fs', () => ({
|
|
13
|
+
default: {
|
|
14
|
+
existsSync: mockExistsSync,
|
|
15
|
+
},
|
|
16
|
+
existsSync: mockExistsSync,
|
|
17
|
+
}));
|
|
18
|
+
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
19
|
+
const { createConfigCommand } = await import('./index.js');
|
|
20
|
+
describe('config command', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks();
|
|
23
|
+
// Reset environment variables
|
|
24
|
+
delete process.env.ARK_CHAT_STREAMING;
|
|
25
|
+
delete process.env.ARK_CHAT_OUTPUT_FORMAT;
|
|
26
|
+
});
|
|
27
|
+
it('creates command with correct structure', () => {
|
|
28
|
+
const command = createConfigCommand({});
|
|
29
|
+
expect(command).toBeInstanceOf(Command);
|
|
30
|
+
expect(command.name()).toBe('config');
|
|
31
|
+
});
|
|
32
|
+
it('displays config paths and environment variables', async () => {
|
|
33
|
+
const mockConfig = { defaultModel: 'test-model' };
|
|
34
|
+
const mockPaths = {
|
|
35
|
+
user: '/home/user/.arkrc.yaml',
|
|
36
|
+
project: '/project/.arkrc.yaml',
|
|
37
|
+
};
|
|
38
|
+
mockLoadConfig.mockReturnValue(mockConfig);
|
|
39
|
+
mockGetConfigPaths.mockReturnValue(mockPaths);
|
|
40
|
+
mockFormatConfig.mockReturnValue('formatted config');
|
|
41
|
+
mockExistsSync.mockReturnValue(true);
|
|
42
|
+
const command = createConfigCommand({});
|
|
43
|
+
await command.parseAsync(['node', 'test']);
|
|
44
|
+
expect(mockLoadConfig).toHaveBeenCalled();
|
|
45
|
+
expect(mockGetConfigPaths).toHaveBeenCalled();
|
|
46
|
+
expect(mockFormatConfig).toHaveBeenCalledWith(mockConfig);
|
|
47
|
+
expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.user);
|
|
48
|
+
expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.project);
|
|
49
|
+
});
|
|
50
|
+
it('shows when config files do not exist', async () => {
|
|
51
|
+
const mockPaths = {
|
|
52
|
+
user: '/home/user/.arkrc.yaml',
|
|
53
|
+
project: '/project/.arkrc.yaml',
|
|
54
|
+
};
|
|
55
|
+
mockLoadConfig.mockReturnValue({});
|
|
56
|
+
mockGetConfigPaths.mockReturnValue(mockPaths);
|
|
57
|
+
mockFormatConfig.mockReturnValue('');
|
|
58
|
+
mockExistsSync.mockReturnValue(false);
|
|
59
|
+
const command = createConfigCommand({});
|
|
60
|
+
await command.parseAsync(['node', 'test']);
|
|
61
|
+
expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.user);
|
|
62
|
+
expect(mockExistsSync).toHaveBeenCalledWith(mockPaths.project);
|
|
63
|
+
// Should show that files don't exist
|
|
64
|
+
expect(mockConsoleLog).toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
it('displays environment variables when set', async () => {
|
|
67
|
+
process.env.ARK_CHAT_STREAMING = 'true';
|
|
68
|
+
process.env.ARK_CHAT_OUTPUT_FORMAT = 'json';
|
|
69
|
+
mockLoadConfig.mockReturnValue({});
|
|
70
|
+
mockGetConfigPaths.mockReturnValue({ user: '', project: '' });
|
|
71
|
+
mockFormatConfig.mockReturnValue('');
|
|
72
|
+
mockExistsSync.mockReturnValue(false);
|
|
73
|
+
const command = createConfigCommand({});
|
|
74
|
+
await command.parseAsync(['node', 'test']);
|
|
75
|
+
// Should display the environment variables
|
|
76
|
+
expect(mockConsoleLog).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import type { ArkConfig } from '../../lib/config.js';
|
|
2
3
|
export declare function openDashboard(): Promise<void>;
|
|
3
|
-
export declare function createDashboardCommand(): Command;
|
|
4
|
+
export declare function createDashboardCommand(_: ArkConfig): Command;
|
|
@@ -30,7 +30,7 @@ export async function openDashboard() {
|
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
export function createDashboardCommand() {
|
|
33
|
+
export function createDashboardCommand(_) {
|
|
34
34
|
const dashboardCommand = new Command('dashboard');
|
|
35
35
|
dashboardCommand
|
|
36
36
|
.description('Open the ARK dashboard in your browser')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createToolCommand } from './tool/index.js';
|
|
3
|
-
export function createDevCommand() {
|
|
3
|
+
export function createDevCommand(_) {
|
|
4
4
|
const devCommand = new Command('dev');
|
|
5
5
|
devCommand.description('Development tools for ARK');
|
|
6
6
|
// Add subcommands
|