@agents-at-scale/ark 0.1.35 → 0.1.36
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 +42 -0
- package/dist/arkServices.js +138 -0
- package/dist/arkServices.spec.d.ts +1 -0
- package/dist/arkServices.spec.js +24 -0
- package/dist/commands/agents/index.d.ts +3 -0
- package/dist/commands/agents/index.js +65 -0
- 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 +3 -0
- package/dist/commands/chat/index.js +29 -0
- package/dist/commands/cluster/get.d.ts +2 -0
- package/dist/commands/cluster/get.js +39 -0
- 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 +3 -5
- 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 +3 -0
- package/dist/commands/completion/index.js +230 -0
- 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 +3 -0
- package/dist/commands/config/index.js +42 -0
- 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 +4 -0
- package/dist/commands/dashboard/index.js +39 -0
- package/dist/commands/docs/index.d.ts +4 -0
- package/dist/commands/docs/index.js +18 -0
- package/dist/commands/generate/config.js +5 -24
- package/dist/commands/generate/generators/mcpserver.d.ts +2 -1
- package/dist/commands/generate/generators/mcpserver.js +26 -5
- 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 +8 -0
- package/dist/commands/install/index.js +295 -0
- package/dist/commands/install/index.spec.d.ts +1 -0
- package/dist/commands/install/index.spec.js +143 -0
- package/dist/commands/models/create.d.ts +1 -0
- package/dist/commands/models/create.js +213 -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 +3 -0
- package/dist/commands/models/index.js +75 -0
- package/dist/commands/models/index.spec.d.ts +1 -0
- package/dist/commands/models/index.spec.js +96 -0
- package/dist/commands/query/index.d.ts +3 -0
- package/dist/commands/query/index.js +24 -0
- package/dist/commands/query/index.spec.d.ts +1 -0
- package/dist/commands/query/index.spec.js +53 -0
- package/dist/commands/routes/index.d.ts +3 -0
- package/dist/commands/routes/index.js +93 -0
- package/dist/commands/status/index.d.ts +3 -0
- package/dist/commands/status/index.js +281 -0
- package/dist/commands/targets/index.d.ts +3 -0
- package/dist/commands/targets/index.js +72 -0
- package/dist/commands/targets/index.spec.d.ts +1 -0
- package/dist/commands/targets/index.spec.js +154 -0
- package/dist/commands/teams/index.d.ts +3 -0
- package/dist/commands/teams/index.js +64 -0
- 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 +3 -0
- package/dist/commands/tools/index.js +49 -0
- 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 +3 -0
- package/dist/commands/uninstall/index.js +101 -0
- package/dist/commands/uninstall/index.spec.d.ts +1 -0
- package/dist/commands/uninstall/index.spec.js +125 -0
- package/dist/components/ChatUI.d.ts +16 -0
- package/dist/components/ChatUI.js +801 -0
- package/dist/components/statusChecker.d.ts +14 -24
- package/dist/components/statusChecker.js +295 -129
- package/dist/index.d.ts +1 -1
- package/dist/index.js +42 -42
- package/dist/lib/arkApiClient.d.ts +53 -0
- package/dist/lib/arkApiClient.js +102 -0
- package/dist/lib/arkApiProxy.d.ts +9 -0
- package/dist/lib/arkApiProxy.js +22 -0
- package/dist/lib/arkServiceProxy.d.ts +14 -0
- package/dist/lib/arkServiceProxy.js +95 -0
- package/dist/lib/arkStatus.d.ts +10 -0
- package/dist/lib/arkStatus.js +79 -0
- package/dist/lib/arkStatus.spec.d.ts +1 -0
- package/dist/lib/arkStatus.spec.js +49 -0
- package/dist/lib/chatClient.d.ts +33 -0
- package/dist/lib/chatClient.js +93 -0
- package/dist/lib/cluster.d.ts +2 -1
- package/dist/lib/cluster.js +37 -16
- package/dist/lib/cluster.spec.d.ts +1 -0
- package/dist/lib/cluster.spec.js +338 -0
- 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 +26 -80
- package/dist/lib/config.js +70 -205
- package/dist/lib/config.spec.d.ts +1 -0
- package/dist/lib/config.spec.js +99 -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/executeQuery.d.ts +20 -0
- package/dist/lib/executeQuery.js +135 -0
- package/dist/lib/executeQuery.spec.d.ts +1 -0
- package/dist/lib/executeQuery.spec.js +170 -0
- 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.d.ts +36 -0
- package/dist/lib/output.js +89 -0
- package/dist/lib/output.spec.d.ts +1 -0
- package/dist/lib/output.spec.js +123 -0
- package/dist/lib/startup.d.ts +9 -0
- package/dist/lib/startup.js +87 -0
- package/dist/lib/startup.spec.d.ts +1 -0
- package/dist/lib/startup.spec.js +152 -0
- package/dist/lib/types.d.ts +87 -3
- package/dist/lib/versions.d.ts +23 -0
- package/dist/lib/versions.js +51 -0
- package/dist/types/types.d.ts +40 -0
- package/dist/types/types.js +1 -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 +226 -91
- 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 -7
- package/dist/ui/statusFormatter.js +39 -39
- package/dist/ui/statusFormatter.spec.d.ts +1 -0
- package/dist/ui/statusFormatter.spec.js +58 -0
- package/package.json +16 -5
- package/dist/commands/cluster/get-ip.d.ts +0 -2
- package/dist/commands/cluster/get-ip.js +0 -32
- package/dist/commands/cluster/get-type.d.ts +0 -2
- package/dist/commands/cluster/get-type.js +0 -26
- package/dist/commands/completion.d.ts +0 -2
- package/dist/commands/completion.js +0 -108
- package/dist/commands/config.d.ts +0 -5
- package/dist/commands/config.js +0 -327
- package/dist/components/DashboardCLI.d.ts +0 -3
- package/dist/components/DashboardCLI.js +0 -149
- package/dist/config.d.ts +0 -42
- package/dist/config.js +0 -243
- package/dist/lib/arkClient.d.ts +0 -32
- package/dist/lib/arkClient.js +0 -43
- package/dist/lib/consts.d.ts +0 -10
- package/dist/lib/consts.js +0 -15
- package/dist/lib/exec.d.ts +0 -5
- package/dist/lib/exec.js +0 -20
- package/dist/lib/gatewayManager.d.ts +0 -24
- package/dist/lib/gatewayManager.js +0 -85
- package/dist/lib/kubernetes.d.ts +0 -28
- package/dist/lib/kubernetes.js +0 -122
- package/dist/lib/progress.d.ts +0 -128
- package/dist/lib/progress.js +0 -273
- package/dist/lib/wrappers/git.d.ts +0 -2
- package/dist/lib/wrappers/git.js +0 -43
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized ARK service definitions used by both install and status commands
|
|
3
|
+
*/
|
|
4
|
+
export interface ArkService {
|
|
5
|
+
name: string;
|
|
6
|
+
helmReleaseName: string;
|
|
7
|
+
description: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
namespace?: string;
|
|
10
|
+
chartPath?: string;
|
|
11
|
+
installArgs?: string[];
|
|
12
|
+
k8sServiceName?: string;
|
|
13
|
+
k8sServicePort?: number;
|
|
14
|
+
k8sPortForwardLocalPort?: number;
|
|
15
|
+
k8sDeploymentName?: string;
|
|
16
|
+
k8sDevDeploymentName?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ServiceCollection {
|
|
19
|
+
[key: string]: ArkService;
|
|
20
|
+
}
|
|
21
|
+
export interface ArkDependency {
|
|
22
|
+
name: string;
|
|
23
|
+
command: string;
|
|
24
|
+
args: string[];
|
|
25
|
+
description: string;
|
|
26
|
+
}
|
|
27
|
+
export interface DependencyCollection {
|
|
28
|
+
[key: string]: ArkDependency;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Dependencies that should be installed before ARK services
|
|
32
|
+
* Note: Dependencies will be installed in the order they are defined here
|
|
33
|
+
*/
|
|
34
|
+
export declare const arkDependencies: DependencyCollection;
|
|
35
|
+
/**
|
|
36
|
+
* Core ARK services with their installation and status check configurations
|
|
37
|
+
*/
|
|
38
|
+
export declare const arkServices: ServiceCollection;
|
|
39
|
+
/**
|
|
40
|
+
* Get services that can be installed via Helm charts (only enabled services)
|
|
41
|
+
*/
|
|
42
|
+
export declare function getInstallableServices(): ServiceCollection;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized ARK service definitions used by both install and status commands
|
|
3
|
+
*/
|
|
4
|
+
const REGISTRY_BASE = 'oci://ghcr.io/mckinsey/agents-at-scale-ark/charts';
|
|
5
|
+
/**
|
|
6
|
+
* Dependencies that should be installed before ARK services
|
|
7
|
+
* Note: Dependencies will be installed in the order they are defined here
|
|
8
|
+
*/
|
|
9
|
+
export const arkDependencies = {
|
|
10
|
+
'cert-manager-repo': {
|
|
11
|
+
name: 'cert-manager-repo',
|
|
12
|
+
command: 'helm',
|
|
13
|
+
args: [
|
|
14
|
+
'repo',
|
|
15
|
+
'add',
|
|
16
|
+
'jetstack',
|
|
17
|
+
'https://charts.jetstack.io',
|
|
18
|
+
'--force-update',
|
|
19
|
+
],
|
|
20
|
+
description: 'Add Jetstack Helm repository',
|
|
21
|
+
},
|
|
22
|
+
'helm-repo-update': {
|
|
23
|
+
name: 'helm-repo-update',
|
|
24
|
+
command: 'helm',
|
|
25
|
+
args: ['repo', 'update'],
|
|
26
|
+
description: 'Update Helm repositories',
|
|
27
|
+
},
|
|
28
|
+
'cert-manager': {
|
|
29
|
+
name: 'cert-manager',
|
|
30
|
+
command: 'helm',
|
|
31
|
+
args: [
|
|
32
|
+
'upgrade',
|
|
33
|
+
'--install',
|
|
34
|
+
'cert-manager',
|
|
35
|
+
'jetstack/cert-manager',
|
|
36
|
+
'--namespace',
|
|
37
|
+
'cert-manager',
|
|
38
|
+
'--create-namespace',
|
|
39
|
+
'--set',
|
|
40
|
+
'crds.enabled=true',
|
|
41
|
+
],
|
|
42
|
+
description: 'Certificate management for Kubernetes',
|
|
43
|
+
},
|
|
44
|
+
'gateway-api-crds': {
|
|
45
|
+
name: 'gateway-api-crds',
|
|
46
|
+
command: 'kubectl',
|
|
47
|
+
args: [
|
|
48
|
+
'apply',
|
|
49
|
+
'-f',
|
|
50
|
+
'https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml',
|
|
51
|
+
],
|
|
52
|
+
description: 'Gateway API CRDs',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Core ARK services with their installation and status check configurations
|
|
57
|
+
*/
|
|
58
|
+
export const arkServices = {
|
|
59
|
+
'ark-controller': {
|
|
60
|
+
name: 'ark-controller',
|
|
61
|
+
helmReleaseName: 'ark-controller',
|
|
62
|
+
description: 'Core Ark controller for managing AI resources',
|
|
63
|
+
enabled: true,
|
|
64
|
+
namespace: 'ark-system',
|
|
65
|
+
chartPath: `${REGISTRY_BASE}/ark-controller`,
|
|
66
|
+
installArgs: ['--create-namespace', '--set', 'rbac.enable=true'],
|
|
67
|
+
k8sDeploymentName: 'ark-controller',
|
|
68
|
+
k8sDevDeploymentName: 'ark-controller-devspace',
|
|
69
|
+
},
|
|
70
|
+
'ark-api': {
|
|
71
|
+
name: 'ark-api',
|
|
72
|
+
helmReleaseName: 'ark-api',
|
|
73
|
+
description: 'API layer for interacting with Ark resources',
|
|
74
|
+
enabled: true,
|
|
75
|
+
// namespace: undefined - uses current context namespace
|
|
76
|
+
chartPath: `${REGISTRY_BASE}/ark-api`,
|
|
77
|
+
installArgs: [],
|
|
78
|
+
k8sServiceName: 'ark-api',
|
|
79
|
+
k8sServicePort: 80,
|
|
80
|
+
k8sDeploymentName: 'ark-api',
|
|
81
|
+
k8sDevDeploymentName: 'ark-api-devspace',
|
|
82
|
+
k8sPortForwardLocalPort: 34780,
|
|
83
|
+
},
|
|
84
|
+
'ark-dashboard': {
|
|
85
|
+
name: 'ark-dashboard',
|
|
86
|
+
helmReleaseName: 'ark-dashboard',
|
|
87
|
+
description: 'Ark Dashboard',
|
|
88
|
+
enabled: true,
|
|
89
|
+
// namespace: undefined - uses current context namespace
|
|
90
|
+
chartPath: `${REGISTRY_BASE}/ark-dashboard`,
|
|
91
|
+
installArgs: [],
|
|
92
|
+
k8sServiceName: 'ark-dashboard',
|
|
93
|
+
k8sServicePort: 3000,
|
|
94
|
+
k8sDeploymentName: 'ark-dashboard',
|
|
95
|
+
k8sDevDeploymentName: 'ark-dashboard-devspace',
|
|
96
|
+
k8sPortForwardLocalPort: 3274,
|
|
97
|
+
},
|
|
98
|
+
'ark-api-a2a': {
|
|
99
|
+
name: 'ark-api-a2a',
|
|
100
|
+
helmReleaseName: 'ark-api-a2a',
|
|
101
|
+
description: 'Ark API agent-to-agent communication service',
|
|
102
|
+
enabled: false, // Disabled - not currently used
|
|
103
|
+
// namespace: undefined - uses current context namespace
|
|
104
|
+
// Note: This service might be installed as part of ark-api or separately
|
|
105
|
+
},
|
|
106
|
+
'ark-mcp': {
|
|
107
|
+
name: 'ark-mcp',
|
|
108
|
+
helmReleaseName: 'ark-mcp',
|
|
109
|
+
description: 'Ark Model Context Protocol server',
|
|
110
|
+
enabled: true,
|
|
111
|
+
// namespace: undefined - uses current context namespace
|
|
112
|
+
chartPath: `${REGISTRY_BASE}/ark-mcp`,
|
|
113
|
+
installArgs: [],
|
|
114
|
+
k8sDeploymentName: 'ark-mcp',
|
|
115
|
+
k8sDevDeploymentName: 'ark-mcp-devspace',
|
|
116
|
+
},
|
|
117
|
+
'localhost-gateway': {
|
|
118
|
+
name: 'localhost-gateway',
|
|
119
|
+
helmReleaseName: 'localhost-gateway',
|
|
120
|
+
description: 'Gateway for local development clusters',
|
|
121
|
+
enabled: false, // Disabled - not needed for most users
|
|
122
|
+
namespace: 'ark-system',
|
|
123
|
+
chartPath: `${REGISTRY_BASE}/localhost-gateway`,
|
|
124
|
+
installArgs: [],
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Get services that can be installed via Helm charts (only enabled services)
|
|
129
|
+
*/
|
|
130
|
+
export function getInstallableServices() {
|
|
131
|
+
const installable = {};
|
|
132
|
+
for (const [key, service] of Object.entries(arkServices)) {
|
|
133
|
+
if (service.enabled && service.chartPath) {
|
|
134
|
+
installable[key] = service;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return installable;
|
|
138
|
+
}
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { execa } from 'execa';
|
|
3
|
+
import output from '../../lib/output.js';
|
|
4
|
+
import { executeQuery } from '../../lib/executeQuery.js';
|
|
5
|
+
async function listAgents(options) {
|
|
6
|
+
try {
|
|
7
|
+
// Use kubectl to get agents
|
|
8
|
+
const result = await execa('kubectl', ['get', 'agents', '-o', 'json'], {
|
|
9
|
+
stdio: 'pipe',
|
|
10
|
+
});
|
|
11
|
+
const data = JSON.parse(result.stdout);
|
|
12
|
+
const agents = data.items || [];
|
|
13
|
+
if (options.output === 'json') {
|
|
14
|
+
// Output the raw items for JSON format
|
|
15
|
+
console.log(JSON.stringify(agents, null, 2));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
if (agents.length === 0) {
|
|
19
|
+
output.warning('no agents available');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Simple list output - just agent names
|
|
23
|
+
agents.forEach((agent) => {
|
|
24
|
+
console.log(agent.metadata.name);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
output.error('fetching agents:', error instanceof Error ? error.message : error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function createAgentsCommand(_) {
|
|
34
|
+
const agentsCommand = new Command('agents');
|
|
35
|
+
agentsCommand
|
|
36
|
+
.description('list available agents')
|
|
37
|
+
.alias('agent')
|
|
38
|
+
.option('-o, --output <format>', 'output format (json or text)', 'text')
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
await listAgents(options);
|
|
41
|
+
});
|
|
42
|
+
// Add list subcommand (same as default action)
|
|
43
|
+
agentsCommand
|
|
44
|
+
.command('list')
|
|
45
|
+
.alias('ls')
|
|
46
|
+
.description('list all available agents')
|
|
47
|
+
.option('-o, --output <format>', 'output format (json or text)', 'text')
|
|
48
|
+
.action(async (options) => {
|
|
49
|
+
await listAgents(options);
|
|
50
|
+
});
|
|
51
|
+
// Add query subcommand
|
|
52
|
+
agentsCommand
|
|
53
|
+
.command('query')
|
|
54
|
+
.description('Query an agent')
|
|
55
|
+
.argument('<name>', 'Agent name')
|
|
56
|
+
.argument('<message>', 'Message to send')
|
|
57
|
+
.action(async (name, message) => {
|
|
58
|
+
await executeQuery({
|
|
59
|
+
targetType: 'agent',
|
|
60
|
+
targetName: name,
|
|
61
|
+
message,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
return agentsCommand;
|
|
65
|
+
}
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import ChatUI from '../../components/ChatUI.js';
|
|
5
|
+
import { ArkApiProxy } from '../../lib/arkApiProxy.js';
|
|
6
|
+
import output from '../../lib/output.js';
|
|
7
|
+
export function createChatCommand(config) {
|
|
8
|
+
const chatCommand = new Command('chat');
|
|
9
|
+
chatCommand
|
|
10
|
+
.description('Start an interactive chat session with ARK agents or models')
|
|
11
|
+
.argument('[target]', 'Target to connect to (e.g., agent/sample-agent, model/default)')
|
|
12
|
+
.action(async (targetArg) => {
|
|
13
|
+
// Direct target argument (e.g., "agent/sample-agent")
|
|
14
|
+
const initialTargetId = targetArg;
|
|
15
|
+
// Config is passed from main
|
|
16
|
+
// Initialize proxy first - no spinner, just let ChatUI handle loading state
|
|
17
|
+
try {
|
|
18
|
+
const proxy = new ArkApiProxy();
|
|
19
|
+
const arkApiClient = await proxy.start();
|
|
20
|
+
// Pass the initialized client and config to ChatUI
|
|
21
|
+
render(_jsx(ChatUI, { initialTargetId: initialTargetId, arkApiClient: arkApiClient, arkApiProxy: proxy, config: config }));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
output.error(error instanceof Error ? error.message : 'ARK API connection failed');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return chatCommand;
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import output from '../../lib/output.js';
|
|
3
|
+
import { getClusterInfo } from '../../lib/cluster.js';
|
|
4
|
+
export function createGetCommand() {
|
|
5
|
+
const get = new Command('get');
|
|
6
|
+
get
|
|
7
|
+
.description('get current kubernetes cluster information')
|
|
8
|
+
.option('-c, --context <context>', 'kubernetes context to use')
|
|
9
|
+
.option('-o, --output <format>', 'output format (text|json)', 'text')
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
try {
|
|
12
|
+
const clusterInfo = await getClusterInfo(options.context);
|
|
13
|
+
if (clusterInfo.error) {
|
|
14
|
+
output.error('getting cluster info:', clusterInfo.error);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
if (options.output === 'json') {
|
|
18
|
+
console.log(JSON.stringify({
|
|
19
|
+
context: clusterInfo.context,
|
|
20
|
+
namespace: clusterInfo.namespace,
|
|
21
|
+
type: clusterInfo.type,
|
|
22
|
+
ip: clusterInfo.ip,
|
|
23
|
+
}, null, 2));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Text format (default)
|
|
27
|
+
console.log(`context: ${clusterInfo.context}`);
|
|
28
|
+
console.log(`namespace: ${clusterInfo.namespace}`);
|
|
29
|
+
console.log(`type: ${clusterInfo.type}`);
|
|
30
|
+
console.log(`ip: ${clusterInfo.ip || 'unknown'}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
output.error('failed to get cluster info:', error instanceof Error ? error.message : 'Unknown error');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return get;
|
|
39
|
+
}
|
|
@@ -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,10 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export function createClusterCommand() {
|
|
2
|
+
import { createGetCommand } from './get.js';
|
|
3
|
+
export function createClusterCommand(_) {
|
|
5
4
|
const cluster = new Command('cluster');
|
|
6
5
|
cluster.description('Cluster management commands');
|
|
7
|
-
cluster.addCommand(
|
|
8
|
-
cluster.addCommand(createGetIpCommand());
|
|
6
|
+
cluster.addCommand(createGetCommand());
|
|
9
7
|
return cluster;
|
|
10
8
|
}
|
|
@@ -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
|
+
});
|