@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.
- 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 +24 -2
- 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/docs/index.d.ts +4 -0
- package/dist/commands/docs/index.js +18 -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 +225 -90
- package/dist/commands/install/index.spec.d.ts +1 -0
- package/dist/commands/install/index.spec.js +143 -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/query/index.d.ts +3 -0
- package/dist/commands/query/index.js +131 -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 +240 -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 +66 -44
- package/dist/commands/uninstall/index.spec.d.ts +1 -0
- package/dist/commands/uninstall/index.spec.js +125 -0
- package/dist/components/ChatUI.js +4 -4
- package/dist/components/statusChecker.d.ts +5 -12
- package/dist/components/statusChecker.js +193 -90
- package/dist/config.d.ts +3 -22
- package/dist/config.js +7 -151
- package/dist/index.js +26 -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 +4 -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/nextSteps.d.ts +4 -0
- package/dist/lib/nextSteps.js +20 -0
- package/dist/lib/nextSteps.spec.d.ts +1 -0
- package/dist/lib/nextSteps.spec.js +59 -0
- 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 +9 -0
- package/dist/lib/startup.js +93 -0
- package/dist/lib/startup.spec.d.ts +1 -0
- package/dist/lib/startup.spec.js +168 -0
- package/dist/lib/types.d.ts +9 -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'));
|
|
@@ -28,7 +28,7 @@ _ark_completion() {
|
|
|
28
28
|
|
|
29
29
|
case \${COMP_CWORD} in
|
|
30
30
|
1)
|
|
31
|
-
opts="agents chat cluster completion config dashboard dev generate install models routes status targets teams tools uninstall help"
|
|
31
|
+
opts="agents chat cluster completion config dashboard dev docs generate install models query routes status targets teams tools uninstall help"
|
|
32
32
|
COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
|
|
33
33
|
return 0
|
|
34
34
|
;;
|
|
@@ -95,6 +95,17 @@ _ark_completion() {
|
|
|
95
95
|
COMPREPLY=( $(compgen -W "\${targets}" -- \${cur}) )
|
|
96
96
|
return 0
|
|
97
97
|
;;
|
|
98
|
+
query)
|
|
99
|
+
# Dynamically fetch available targets for query command
|
|
100
|
+
local targets
|
|
101
|
+
targets=$(ark targets list 2>/dev/null)
|
|
102
|
+
if [ -z "$targets" ]; then
|
|
103
|
+
# Fallback to common targets if API is not available
|
|
104
|
+
targets="model/default agent/sample-agent"
|
|
105
|
+
fi
|
|
106
|
+
COMPREPLY=( $(compgen -W "\${targets}" -- \${cur}) )
|
|
107
|
+
return 0
|
|
108
|
+
;;
|
|
98
109
|
esac
|
|
99
110
|
;;
|
|
100
111
|
3)
|
|
@@ -159,9 +170,11 @@ _ark() {
|
|
|
159
170
|
'config[Configuration management]' \\
|
|
160
171
|
'dashboard[Open ARK dashboard]' \\
|
|
161
172
|
'dev[Development tools for ARK]' \\
|
|
173
|
+
'docs[Open ARK documentation]' \\
|
|
162
174
|
'generate[Generate ARK resources]' \\
|
|
163
175
|
'install[Install ARK services]' \\
|
|
164
176
|
'models[List available models]' \\
|
|
177
|
+
'query[Execute a single query]' \\
|
|
165
178
|
'routes[List available routes]' \\
|
|
166
179
|
'status[Check system status]' \\
|
|
167
180
|
'targets[List available query targets]' \\
|
|
@@ -234,6 +247,15 @@ _ark() {
|
|
|
234
247
|
fi
|
|
235
248
|
_values 'available targets' \${targets[@]}
|
|
236
249
|
;;
|
|
250
|
+
query)
|
|
251
|
+
# Get available targets dynamically for query
|
|
252
|
+
local -a targets
|
|
253
|
+
targets=($(ark targets list 2>/dev/null))
|
|
254
|
+
if [ \${#targets[@]} -eq 0 ]; then
|
|
255
|
+
targets=('model/default' 'agent/sample-agent')
|
|
256
|
+
fi
|
|
257
|
+
_values 'available targets' \${targets[@]}
|
|
258
|
+
;;
|
|
237
259
|
esac
|
|
238
260
|
;;
|
|
239
261
|
args)
|
|
@@ -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 {};
|