@agents-at-scale/ark 0.1.35 → 0.1.36-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 +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/charts/charts.d.ts +5 -0
- package/dist/charts/charts.js +6 -0
- package/dist/charts/dependencies.d.ts +6 -0
- package/dist/charts/dependencies.js +50 -0
- package/dist/charts/types.d.ts +40 -0
- package/dist/charts/types.js +1 -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/agents/selector.d.ts +8 -0
- package/dist/commands/agents/selector.js +53 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +53 -0
- package/dist/commands/chat/index.d.ts +3 -0
- package/dist/commands/chat/index.js +29 -0
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +45 -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/completion.js +159 -2
- 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/config.d.ts +0 -3
- package/dist/commands/config.js +38 -321
- package/dist/commands/dashboard/index.d.ts +4 -0
- package/dist/commands/dashboard/index.js +39 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +39 -0
- package/dist/commands/dev/index.d.ts +3 -0
- package/dist/commands/dev/index.js +9 -0
- package/dist/commands/dev/tool/check.d.ts +2 -0
- package/dist/commands/dev/tool/check.js +142 -0
- package/dist/commands/dev/tool/clean.d.ts +2 -0
- package/dist/commands/dev/tool/clean.js +153 -0
- package/dist/commands/dev/tool/generate.d.ts +2 -0
- package/dist/commands/dev/tool/generate.js +28 -0
- package/dist/commands/dev/tool/index.d.ts +2 -0
- package/dist/commands/dev/tool/index.js +14 -0
- package/dist/commands/dev/tool/init.d.ts +2 -0
- package/dist/commands/dev/tool/init.js +320 -0
- package/dist/commands/dev/tool/shared.d.ts +5 -0
- package/dist/commands/dev/tool/shared.js +258 -0
- package/dist/commands/dev/tool/status.d.ts +2 -0
- package/dist/commands/dev/tool/status.js +136 -0
- 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.d.ts +2 -0
- package/dist/commands/dev/tool.js +559 -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/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/install.d.ts +3 -0
- package/dist/commands/install.js +147 -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/models/selector.d.ts +8 -0
- package/dist/commands/models/selector.js +53 -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/routes.d.ts +2 -0
- package/dist/commands/routes.js +101 -0
- package/dist/commands/status/index.d.ts +3 -0
- package/dist/commands/status/index.js +281 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +33 -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/targets.d.ts +2 -0
- package/dist/commands/targets.js +65 -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/teams/selector.d.ts +8 -0
- package/dist/commands/teams/selector.js +55 -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/tools/selector.d.ts +8 -0
- package/dist/commands/tools/selector.js +53 -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/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +83 -0
- package/dist/components/ChatUI.d.ts +16 -0
- package/dist/components/ChatUI.js +801 -0
- package/dist/components/StatusView.d.ts +10 -0
- package/dist/components/StatusView.js +39 -0
- package/dist/components/statusChecker.d.ts +14 -24
- package/dist/components/statusChecker.js +295 -129
- package/dist/config.d.ts +3 -22
- package/dist/config.js +10 -161
- 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/commandUtils.d.ts +4 -0
- package/dist/lib/commandUtils.js +18 -0
- package/dist/lib/commandUtils.test.d.ts +1 -0
- package/dist/lib/commandUtils.test.js +44 -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/config.test.d.ts +1 -0
- package/dist/lib/config.test.js +93 -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/dev/tools/analyzer.d.ts +30 -0
- package/dist/lib/dev/tools/analyzer.js +190 -0
- package/dist/lib/dev/tools/discover_tools.py +392 -0
- package/dist/lib/dev/tools/mcp-types.d.ts +28 -0
- package/dist/lib/dev/tools/mcp-types.js +86 -0
- package/dist/lib/dev/tools/types.d.ts +50 -0
- package/dist/lib/dev/tools/types.js +1 -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/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/portUtils.d.ts +8 -0
- package/dist/lib/portUtils.js +39 -0
- package/dist/lib/queryRunner.d.ts +22 -0
- package/dist/lib/queryRunner.js +142 -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
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { printNextSteps } from './nextSteps.js';
|
|
3
|
+
describe('printNextSteps', () => {
|
|
4
|
+
let consoleLogSpy;
|
|
5
|
+
let output = [];
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
output = [];
|
|
8
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation((...args) => {
|
|
9
|
+
output.push(args.join(' '));
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
consoleLogSpy.mockRestore();
|
|
14
|
+
});
|
|
15
|
+
it('prints successful installation message', () => {
|
|
16
|
+
printNextSteps();
|
|
17
|
+
const fullOutput = output.join('\n');
|
|
18
|
+
expect(fullOutput).toContain('ARK installed successfully!');
|
|
19
|
+
});
|
|
20
|
+
it('includes all required commands', () => {
|
|
21
|
+
printNextSteps();
|
|
22
|
+
const fullOutput = output.join('\n');
|
|
23
|
+
// Check for each command
|
|
24
|
+
expect(fullOutput).toContain('ark models create default');
|
|
25
|
+
expect(fullOutput).toContain('ark dashboard');
|
|
26
|
+
expect(fullOutput).toContain('kubectl get agents');
|
|
27
|
+
expect(fullOutput).toContain('ark query model/default "What are large language models?"');
|
|
28
|
+
expect(fullOutput).toContain('ark');
|
|
29
|
+
expect(fullOutput).toContain("# then choose 'Chat'");
|
|
30
|
+
expect(fullOutput).toContain('ark generate project my-agents');
|
|
31
|
+
});
|
|
32
|
+
it('includes all required links', () => {
|
|
33
|
+
printNextSteps();
|
|
34
|
+
const fullOutput = output.join('\n');
|
|
35
|
+
// Check for documentation links
|
|
36
|
+
expect(fullOutput).toContain('https://mckinsey.github.io/agents-at-scale-ark/');
|
|
37
|
+
expect(fullOutput).toContain('https://mckinsey.github.io/agents-at-scale-ark/developer-guide/cli-tools/');
|
|
38
|
+
});
|
|
39
|
+
it('includes all section labels', () => {
|
|
40
|
+
printNextSteps();
|
|
41
|
+
const fullOutput = output.join('\n');
|
|
42
|
+
// Check for labels
|
|
43
|
+
expect(fullOutput).toContain('Next steps:');
|
|
44
|
+
expect(fullOutput).toContain('docs:');
|
|
45
|
+
expect(fullOutput).toContain('create model:');
|
|
46
|
+
expect(fullOutput).toContain('open dashboard:');
|
|
47
|
+
expect(fullOutput).toContain('show agents:');
|
|
48
|
+
expect(fullOutput).toContain('run a query:');
|
|
49
|
+
expect(fullOutput).toContain('interactive chat:');
|
|
50
|
+
expect(fullOutput).toContain('new project:');
|
|
51
|
+
expect(fullOutput).toContain('install fark:');
|
|
52
|
+
});
|
|
53
|
+
it('has correct structure with empty lines', () => {
|
|
54
|
+
printNextSteps();
|
|
55
|
+
// Should have empty lines for formatting
|
|
56
|
+
expect(output[0]).toBe('');
|
|
57
|
+
expect(output[output.length - 1]).toBe('');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type StatusType = 'success' | 'warning' | 'info' | 'error';
|
|
2
|
+
declare const output: {
|
|
3
|
+
/**
|
|
4
|
+
* Display a status message with flexible formatting
|
|
5
|
+
*/
|
|
6
|
+
statusMessage(type: StatusType, title: string, message?: string, ...args: unknown[]): void;
|
|
7
|
+
/**
|
|
8
|
+
* Display an error message with consistent formatting
|
|
9
|
+
*/
|
|
10
|
+
error(message: string, ...args: unknown[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Display a success message with consistent formatting
|
|
13
|
+
*/
|
|
14
|
+
success(message: string, ...args: unknown[]): void;
|
|
15
|
+
/**
|
|
16
|
+
* Display an info message (indented gray text)
|
|
17
|
+
*/
|
|
18
|
+
info(message: string, ...args: unknown[]): void;
|
|
19
|
+
/**
|
|
20
|
+
* Display a warning message with consistent formatting
|
|
21
|
+
*/
|
|
22
|
+
warning(message: string, ...args: unknown[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Display a status check item (like ark status format)
|
|
25
|
+
* @param status - 'found', 'missing', 'warning', 'error'
|
|
26
|
+
* @param label - The label to show (e.g., 'platform')
|
|
27
|
+
* @param value - The value in bright white (e.g., 'python3')
|
|
28
|
+
* @param details - Optional grey details
|
|
29
|
+
*/
|
|
30
|
+
statusCheck(status: "found" | "missing" | "warning" | "error", label: string, value?: string, details?: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Display a section header (like 'ark services:')
|
|
33
|
+
*/
|
|
34
|
+
section(title: string): void;
|
|
35
|
+
};
|
|
36
|
+
export default output;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const output = {
|
|
3
|
+
/**
|
|
4
|
+
* Display a status message with flexible formatting
|
|
5
|
+
*/
|
|
6
|
+
statusMessage(type, title, message, ...args) {
|
|
7
|
+
const icons = {
|
|
8
|
+
success: chalk.green('✓'),
|
|
9
|
+
warning: chalk.yellow.bold('!'),
|
|
10
|
+
info: chalk.blue('ℹ'),
|
|
11
|
+
error: chalk.red('✗'),
|
|
12
|
+
};
|
|
13
|
+
const colors = {
|
|
14
|
+
success: chalk.green,
|
|
15
|
+
warning: chalk.yellow,
|
|
16
|
+
info: chalk.blue,
|
|
17
|
+
error: chalk.red,
|
|
18
|
+
};
|
|
19
|
+
const icon = icons[type];
|
|
20
|
+
const color = colors[type];
|
|
21
|
+
const logFn = type === 'error' ? console.error : console.log;
|
|
22
|
+
if (message) {
|
|
23
|
+
logFn(icon, color(`${title}:`), message, ...args);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
logFn(icon, title, ...args);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Display an error message with consistent formatting
|
|
31
|
+
*/
|
|
32
|
+
error(message, ...args) {
|
|
33
|
+
this.statusMessage('error', 'error', message, ...args);
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* Display a success message with consistent formatting
|
|
37
|
+
*/
|
|
38
|
+
success(message, ...args) {
|
|
39
|
+
this.statusMessage('success', message, undefined, ...args);
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* Display an info message (indented gray text)
|
|
43
|
+
*/
|
|
44
|
+
info(message, ...args) {
|
|
45
|
+
console.log(chalk.gray(message), ...args);
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Display a warning message with consistent formatting
|
|
49
|
+
*/
|
|
50
|
+
warning(message, ...args) {
|
|
51
|
+
this.statusMessage('warning', 'warning', message, ...args);
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Display a status check item (like ark status format)
|
|
55
|
+
* @param status - 'found', 'missing', 'warning', 'error'
|
|
56
|
+
* @param label - The label to show (e.g., 'platform')
|
|
57
|
+
* @param value - The value in bright white (e.g., 'python3')
|
|
58
|
+
* @param details - Optional grey details
|
|
59
|
+
*/
|
|
60
|
+
statusCheck(status, label, value, details) {
|
|
61
|
+
const icons = {
|
|
62
|
+
found: chalk.green('✓'),
|
|
63
|
+
missing: chalk.yellow('?'),
|
|
64
|
+
warning: chalk.yellow('!'),
|
|
65
|
+
error: chalk.red('✗'),
|
|
66
|
+
};
|
|
67
|
+
const statusText = {
|
|
68
|
+
found: chalk.green(label),
|
|
69
|
+
missing: chalk.yellow(label),
|
|
70
|
+
warning: chalk.yellow(label),
|
|
71
|
+
error: chalk.red(label),
|
|
72
|
+
};
|
|
73
|
+
let output = ` ${icons[status]} ${statusText[status]}`;
|
|
74
|
+
if (value) {
|
|
75
|
+
output += ` ${chalk.bold.white(value)}`;
|
|
76
|
+
}
|
|
77
|
+
if (details) {
|
|
78
|
+
output += chalk.gray(` ${details}`);
|
|
79
|
+
}
|
|
80
|
+
console.log(output);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Display a section header (like 'ark services:')
|
|
84
|
+
*/
|
|
85
|
+
section(title) {
|
|
86
|
+
console.log(chalk.cyan.bold(`${title}:`));
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
export default output;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import output from './output.js';
|
|
4
|
+
describe('output', () => {
|
|
5
|
+
let consoleErrorSpy;
|
|
6
|
+
let consoleLogSpy;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
consoleErrorSpy = jest
|
|
9
|
+
.spyOn(console, 'error')
|
|
10
|
+
.mockImplementation(() => undefined);
|
|
11
|
+
consoleLogSpy = jest
|
|
12
|
+
.spyOn(console, 'log')
|
|
13
|
+
.mockImplementation(() => undefined);
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
describe('error', () => {
|
|
19
|
+
it('should output error message with red cross and prefix', () => {
|
|
20
|
+
output.error('Something went wrong');
|
|
21
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('error:'), 'Something went wrong');
|
|
22
|
+
});
|
|
23
|
+
it('should handle additional arguments', () => {
|
|
24
|
+
const error = new Error('Test error');
|
|
25
|
+
output.error('Failed to connect', error, 123);
|
|
26
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('error:'), 'Failed to connect', error, 123);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('success', () => {
|
|
30
|
+
it('should output success message with green checkmark', () => {
|
|
31
|
+
output.success('Operation completed');
|
|
32
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Operation completed');
|
|
33
|
+
});
|
|
34
|
+
it('should handle additional arguments', () => {
|
|
35
|
+
output.success('Deployed', 'v1.0.0', { status: 'ok' });
|
|
36
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Deployed', 'v1.0.0', { status: 'ok' });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('info', () => {
|
|
40
|
+
it('should output info message in gray', () => {
|
|
41
|
+
output.info('Processing request...');
|
|
42
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.gray('Processing request...'));
|
|
43
|
+
});
|
|
44
|
+
it('should handle additional arguments', () => {
|
|
45
|
+
output.info('Status:', 'running', 42);
|
|
46
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.gray('Status:'), 'running', 42);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('warning', () => {
|
|
50
|
+
it('should output warning message with yellow exclamation and prefix', () => {
|
|
51
|
+
output.warning('Resource limit approaching');
|
|
52
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('warning:'), 'Resource limit approaching');
|
|
53
|
+
});
|
|
54
|
+
it('should handle additional arguments', () => {
|
|
55
|
+
const details = { cpu: '85%', memory: '92%' };
|
|
56
|
+
output.warning('High resource usage', details);
|
|
57
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('warning:'), 'High resource usage', details);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('statusCheck', () => {
|
|
61
|
+
it('should display found status with value and details', () => {
|
|
62
|
+
output.statusCheck('found', 'platform', 'python3', 'v3.10');
|
|
63
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('platform')} ${chalk.bold.white('python3')}${chalk.gray(' v3.10')}`);
|
|
64
|
+
});
|
|
65
|
+
it('should display found status with value only', () => {
|
|
66
|
+
output.statusCheck('found', 'fastmcp', 'v0.1.0');
|
|
67
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('fastmcp')} ${chalk.bold.white('v0.1.0')}`);
|
|
68
|
+
});
|
|
69
|
+
it('should display found status with label only', () => {
|
|
70
|
+
output.statusCheck('found', '3 tools');
|
|
71
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.green('✓')} ${chalk.green('3 tools')}`);
|
|
72
|
+
});
|
|
73
|
+
it('should display missing status with details', () => {
|
|
74
|
+
output.statusCheck('missing', 'fastmcp', undefined, 'not in dependencies');
|
|
75
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.yellow('?')} ${chalk.yellow('fastmcp')}${chalk.gray(' not in dependencies')}`);
|
|
76
|
+
});
|
|
77
|
+
it('should display warning status', () => {
|
|
78
|
+
output.statusCheck('warning', 'version', '0.0.1', 'pre-release');
|
|
79
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.yellow('!')} ${chalk.yellow('version')} ${chalk.bold.white('0.0.1')}${chalk.gray(' pre-release')}`);
|
|
80
|
+
});
|
|
81
|
+
it('should display error status', () => {
|
|
82
|
+
output.statusCheck('error', 'tools', undefined, 'discovery failed');
|
|
83
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(` ${chalk.red('✗')} ${chalk.red('tools')}${chalk.gray(' discovery failed')}`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('section', () => {
|
|
87
|
+
it('should display section header with colon', () => {
|
|
88
|
+
output.section('dev-tests');
|
|
89
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.cyan.bold('dev-tests:'));
|
|
90
|
+
});
|
|
91
|
+
it('should display section header with custom text', () => {
|
|
92
|
+
output.section('ark services');
|
|
93
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.cyan.bold('ark services:'));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('statusMessage', () => {
|
|
97
|
+
it('should output success status with title and message', () => {
|
|
98
|
+
output.statusMessage('success', 'fastmcp', 'installed (v0.1.0)');
|
|
99
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), chalk.green('fastmcp:'), 'installed (v0.1.0)');
|
|
100
|
+
});
|
|
101
|
+
it('should output warning status with title and message', () => {
|
|
102
|
+
output.statusMessage('warning', 'virtual environment', 'not found');
|
|
103
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.yellow.bold('!'), chalk.yellow('virtual environment:'), 'not found');
|
|
104
|
+
});
|
|
105
|
+
it('should output error status with title and message', () => {
|
|
106
|
+
output.statusMessage('error', 'fastmcp', 'not found in dependencies');
|
|
107
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(chalk.red('✗'), chalk.red('fastmcp:'), 'not found in dependencies');
|
|
108
|
+
});
|
|
109
|
+
it('should output info status with title and message', () => {
|
|
110
|
+
output.statusMessage('info', 'platform', 'python3');
|
|
111
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.blue('ℹ'), chalk.blue('platform:'), 'python3');
|
|
112
|
+
});
|
|
113
|
+
it('should handle title-only messages', () => {
|
|
114
|
+
output.statusMessage('success', 'Operation completed');
|
|
115
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.green('✓'), 'Operation completed');
|
|
116
|
+
});
|
|
117
|
+
it('should handle additional arguments', () => {
|
|
118
|
+
const extra = { version: '1.0' };
|
|
119
|
+
output.statusMessage('info', 'status', 'running', extra, 42);
|
|
120
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(chalk.blue('ℹ'), chalk.blue('status:'), 'running', extra, 42);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a port is available
|
|
3
|
+
*/
|
|
4
|
+
export declare function isPortAvailable(port: number): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Find an available port, starting from the preferred port
|
|
7
|
+
*/
|
|
8
|
+
export declare function findAvailablePort(preferredPort: number, maxAttempts?: number): Promise<number>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a port is available
|
|
4
|
+
*/
|
|
5
|
+
export async function isPortAvailable(port) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
const server = net.createServer();
|
|
8
|
+
server.once('error', (err) => {
|
|
9
|
+
if (err.code === 'EADDRINUSE') {
|
|
10
|
+
resolve(false);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
resolve(false);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
server.once('listening', () => {
|
|
17
|
+
server.close();
|
|
18
|
+
resolve(true);
|
|
19
|
+
});
|
|
20
|
+
server.listen(port, '127.0.0.1');
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Find an available port, starting from the preferred port
|
|
25
|
+
*/
|
|
26
|
+
export async function findAvailablePort(preferredPort, maxAttempts = 10) {
|
|
27
|
+
// First try the preferred port
|
|
28
|
+
if (await isPortAvailable(preferredPort)) {
|
|
29
|
+
return preferredPort;
|
|
30
|
+
}
|
|
31
|
+
// Try random ports
|
|
32
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
33
|
+
const randomPort = Math.floor(Math.random() * (65535 - 1024) + 1024);
|
|
34
|
+
if (await isPortAvailable(randomPort)) {
|
|
35
|
+
return randomPort;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Could not find an available port after ${maxAttempts} attempts`);
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared query execution logic for both universal and resource-specific query commands
|
|
3
|
+
*/
|
|
4
|
+
export interface QueryOptions {
|
|
5
|
+
targetType: string;
|
|
6
|
+
targetName: string;
|
|
7
|
+
message: string;
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Execute a query against any ARK target (model, agent, team)
|
|
12
|
+
* This is the shared implementation used by all query commands
|
|
13
|
+
*/
|
|
14
|
+
export declare function executeQuery(options: QueryOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a target string like "model/default" or "agent/weather"
|
|
17
|
+
* Returns { targetType, targetName } or null if invalid
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseTarget(target: string): {
|
|
20
|
+
targetType: string;
|
|
21
|
+
targetName: string;
|
|
22
|
+
} | null;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared query execution logic for both universal and resource-specific query commands
|
|
3
|
+
*/
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import output from './output.js';
|
|
7
|
+
/**
|
|
8
|
+
* Execute a query against any ARK target (model, agent, team)
|
|
9
|
+
* This is the shared implementation used by all query commands
|
|
10
|
+
*/
|
|
11
|
+
export async function executeQuery(options) {
|
|
12
|
+
const spinner = ora('Creating query...').start();
|
|
13
|
+
// Generate a unique query name
|
|
14
|
+
const timestamp = Date.now();
|
|
15
|
+
const queryName = `cli-query-${timestamp}`;
|
|
16
|
+
// Create the Query resource
|
|
17
|
+
const queryManifest = {
|
|
18
|
+
apiVersion: 'ark.mckinsey.com/v1alpha1',
|
|
19
|
+
kind: 'Query',
|
|
20
|
+
metadata: {
|
|
21
|
+
name: queryName,
|
|
22
|
+
},
|
|
23
|
+
spec: {
|
|
24
|
+
input: options.message,
|
|
25
|
+
targets: [
|
|
26
|
+
{
|
|
27
|
+
type: options.targetType,
|
|
28
|
+
name: options.targetName,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
// Apply the query
|
|
35
|
+
spinner.text = 'Submitting query...';
|
|
36
|
+
await execa('kubectl', ['apply', '-f', '-'], {
|
|
37
|
+
input: JSON.stringify(queryManifest),
|
|
38
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
|
+
});
|
|
40
|
+
// Watch for query completion
|
|
41
|
+
spinner.text = 'Query status: initializing';
|
|
42
|
+
let queryComplete = false;
|
|
43
|
+
let attempts = 0;
|
|
44
|
+
const maxAttempts = 300; // 5 minutes with 1 second intervals
|
|
45
|
+
while (!queryComplete && attempts < maxAttempts) {
|
|
46
|
+
attempts++;
|
|
47
|
+
try {
|
|
48
|
+
const { stdout } = await execa('kubectl', [
|
|
49
|
+
'get',
|
|
50
|
+
'query',
|
|
51
|
+
queryName,
|
|
52
|
+
'-o',
|
|
53
|
+
'json',
|
|
54
|
+
], { stdio: 'pipe' });
|
|
55
|
+
const query = JSON.parse(stdout);
|
|
56
|
+
const phase = query.status?.phase;
|
|
57
|
+
// Update spinner with current phase
|
|
58
|
+
if (phase) {
|
|
59
|
+
spinner.text = `Query status: ${phase}`;
|
|
60
|
+
}
|
|
61
|
+
// Check if query is complete based on phase
|
|
62
|
+
if (phase === 'done') {
|
|
63
|
+
queryComplete = true;
|
|
64
|
+
spinner.succeed('Query completed');
|
|
65
|
+
// Extract and display the response from responses array
|
|
66
|
+
if (query.status?.responses && query.status.responses.length > 0) {
|
|
67
|
+
const response = query.status.responses[0];
|
|
68
|
+
console.log('\n' + (response.content || response));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
output.warning('No response received');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (phase === 'error') {
|
|
75
|
+
queryComplete = true;
|
|
76
|
+
spinner.fail('Query failed');
|
|
77
|
+
// Try to get error message from conditions or status
|
|
78
|
+
const errorCondition = query.status?.conditions?.find((c) => {
|
|
79
|
+
const condition = c;
|
|
80
|
+
return condition.type === 'Complete' && condition.status === 'False';
|
|
81
|
+
});
|
|
82
|
+
if (errorCondition?.message) {
|
|
83
|
+
output.error(errorCondition.message);
|
|
84
|
+
}
|
|
85
|
+
else if (query.status?.error) {
|
|
86
|
+
output.error(query.status.error);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
output.error('Query failed with unknown error');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (phase === 'canceled') {
|
|
93
|
+
queryComplete = true;
|
|
94
|
+
spinner.warn('Query canceled');
|
|
95
|
+
// Try to get cancellation reason if available
|
|
96
|
+
if (query.status?.message) {
|
|
97
|
+
output.warning(query.status.message);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Query might not exist yet, continue waiting
|
|
103
|
+
spinner.text = 'Query status: waiting for query to be created';
|
|
104
|
+
}
|
|
105
|
+
if (!queryComplete) {
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!queryComplete) {
|
|
110
|
+
spinner.fail('Query timed out');
|
|
111
|
+
output.error('Query did not complete within 5 minutes');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
spinner.fail('Query failed');
|
|
116
|
+
output.error(error instanceof Error ? error.message : 'Unknown error');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
// Clean up the query resource
|
|
121
|
+
try {
|
|
122
|
+
await execa('kubectl', ['delete', 'query', queryName], { stdio: 'pipe' });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Ignore cleanup errors
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse a target string like "model/default" or "agent/weather"
|
|
131
|
+
* Returns { targetType, targetName } or null if invalid
|
|
132
|
+
*/
|
|
133
|
+
export function parseTarget(target) {
|
|
134
|
+
const parts = target.split('/');
|
|
135
|
+
if (parts.length !== 2) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
targetType: parts[0],
|
|
140
|
+
targetName: parts[1],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ArkConfig } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Show error message when no cluster is detected
|
|
4
|
+
*/
|
|
5
|
+
export declare function showNoClusterError(): void;
|
|
6
|
+
/**
|
|
7
|
+
* Initialize the CLI with minimal checks for fast startup
|
|
8
|
+
*/
|
|
9
|
+
export declare function startup(): Promise<ArkConfig>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { execa } from 'execa';
|
|
3
|
+
import { checkCommandExists } from './commands.js';
|
|
4
|
+
import { loadConfig } from './config.js';
|
|
5
|
+
const REQUIRED_COMMANDS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'kubectl',
|
|
8
|
+
command: 'kubectl',
|
|
9
|
+
args: ['version', '--client'],
|
|
10
|
+
installUrl: 'https://kubernetes.io/docs/tasks/tools/',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'helm',
|
|
14
|
+
command: 'helm',
|
|
15
|
+
args: ['version', '--short'],
|
|
16
|
+
installUrl: 'https://helm.sh/docs/intro/install/',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
async function checkRequirements() {
|
|
20
|
+
const missing = [];
|
|
21
|
+
for (const cmd of REQUIRED_COMMANDS) {
|
|
22
|
+
const exists = await checkCommandExists(cmd.command, cmd.args);
|
|
23
|
+
if (!exists) {
|
|
24
|
+
missing.push(cmd);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (missing.length > 0) {
|
|
28
|
+
for (const cmd of missing) {
|
|
29
|
+
console.error(chalk.red('error:') + ` ${cmd.name} is required`);
|
|
30
|
+
console.error(' ' + chalk.blue(cmd.installUrl));
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Show error message when no cluster is detected
|
|
37
|
+
*/
|
|
38
|
+
export function showNoClusterError() {
|
|
39
|
+
console.log(chalk.red.bold('\n✗ No Kubernetes cluster detected\n'));
|
|
40
|
+
console.log('Please ensure you have configured a connection to a Kubernetes cluster.');
|
|
41
|
+
console.log('For local development, you can use:');
|
|
42
|
+
console.log(` • Minikube: ${chalk.blue('https://minikube.sigs.k8s.io/docs/start')}`);
|
|
43
|
+
console.log(` • Docker Desktop: ${chalk.blue('https://docs.docker.com/desktop/kubernetes/')}`);
|
|
44
|
+
console.log(` • Kind: ${chalk.blue('https://kind.sigs.k8s.io/docs/user/quick-start/')}`);
|
|
45
|
+
console.log('');
|
|
46
|
+
console.log('And more. For help, check the Quickstart guide:');
|
|
47
|
+
console.log(chalk.blue(' https://mckinsey.github.io/agents-at-scale-ark/quickstart/'));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if a Kubernetes context is configured
|
|
51
|
+
* This is a fast local check that doesn't hit the cluster
|
|
52
|
+
*/
|
|
53
|
+
async function hasKubernetesContext() {
|
|
54
|
+
try {
|
|
55
|
+
const { stdout } = await execa('kubectl', ['config', 'current-context']);
|
|
56
|
+
return stdout.trim().length > 0;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Initialize the CLI with minimal checks for fast startup
|
|
64
|
+
*/
|
|
65
|
+
export async function startup() {
|
|
66
|
+
// Check required commands (kubectl, helm) - fast local checks
|
|
67
|
+
await checkRequirements();
|
|
68
|
+
// Load config from disk (fast - just file I/O)
|
|
69
|
+
const config = loadConfig();
|
|
70
|
+
// Check if we have a kubernetes context configured (fast local check)
|
|
71
|
+
// We don't check cluster connectivity here - that's expensive
|
|
72
|
+
const hasContext = await hasKubernetesContext();
|
|
73
|
+
if (hasContext) {
|
|
74
|
+
try {
|
|
75
|
+
const { stdout } = await execa('kubectl', ['config', 'current-context']);
|
|
76
|
+
config.clusterInfo = {
|
|
77
|
+
type: 'unknown', // We don't detect cluster type here - too slow
|
|
78
|
+
context: stdout.trim(),
|
|
79
|
+
// We don't fetch namespace or cluster details here - too slow
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Ignore - no context
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return config;
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|