@agents-at-scale/ark 0.1.41 → 0.1.42
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/commands/completion/index.js +11 -1
- package/dist/commands/evaluation/index.d.ts +3 -0
- package/dist/commands/evaluation/index.js +60 -0
- package/dist/commands/evaluation/index.spec.d.ts +1 -0
- package/dist/commands/evaluation/index.spec.js +166 -0
- package/dist/commands/memory/index.d.ts +15 -0
- package/dist/commands/memory/index.js +130 -0
- package/dist/commands/memory/index.spec.d.ts +1 -0
- package/dist/commands/memory/index.spec.js +124 -0
- package/dist/commands/models/create.d.ts +5 -6
- package/dist/commands/models/create.js +14 -119
- package/dist/commands/models/create.spec.js +47 -0
- package/dist/commands/models/kubernetes/manifest-builder.d.ts +11 -0
- package/dist/commands/models/kubernetes/manifest-builder.js +109 -0
- package/dist/commands/models/kubernetes/secret-manager.d.ts +7 -0
- package/dist/commands/models/kubernetes/secret-manager.js +20 -0
- package/dist/commands/models/providers/azure.d.ts +31 -0
- package/dist/commands/models/providers/azure.js +82 -0
- package/dist/commands/models/providers/azure.spec.d.ts +1 -0
- package/dist/commands/models/providers/azure.spec.js +230 -0
- package/dist/commands/models/providers/bedrock.d.ts +37 -0
- package/dist/commands/models/providers/bedrock.js +105 -0
- package/dist/commands/models/providers/bedrock.spec.d.ts +1 -0
- package/dist/commands/models/providers/bedrock.spec.js +241 -0
- package/dist/commands/models/providers/factory.d.ts +18 -0
- package/dist/commands/models/providers/factory.js +31 -0
- package/dist/commands/models/providers/index.d.ts +17 -0
- package/dist/commands/models/providers/index.js +9 -0
- package/dist/commands/models/providers/openai.d.ts +28 -0
- package/dist/commands/models/providers/openai.js +68 -0
- package/dist/commands/models/providers/openai.spec.d.ts +1 -0
- package/dist/commands/models/providers/openai.spec.js +180 -0
- package/dist/commands/models/providers/types.d.ts +51 -0
- package/dist/commands/models/providers/types.js +1 -0
- package/dist/commands/queries/index.d.ts +3 -0
- package/dist/commands/queries/index.js +70 -0
- package/dist/commands/query/index.js +3 -1
- package/dist/commands/query/index.spec.js +24 -0
- package/dist/components/ChatUI.js +2 -0
- package/dist/index.js +6 -0
- package/dist/lib/arkApiClient.d.ts +4 -0
- package/dist/lib/arkApiClient.js +55 -0
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.js +1 -0
- package/dist/lib/executeEvaluation.d.ts +16 -0
- package/dist/lib/executeEvaluation.js +155 -0
- package/dist/lib/executeQuery.d.ts +1 -0
- package/dist/lib/executeQuery.js +45 -9
- package/dist/lib/executeQuery.spec.js +3 -3
- package/dist/lib/kubectl.d.ts +8 -0
- package/dist/lib/kubectl.js +20 -0
- package/dist/lib/kubectl.spec.d.ts +1 -0
- package/dist/lib/kubectl.spec.js +88 -0
- package/dist/lib/stdin.d.ts +1 -0
- package/dist/lib/stdin.js +16 -0
- package/dist/lib/stdin.spec.d.ts +1 -0
- package/dist/lib/stdin.spec.js +82 -0
- package/dist/lib/types.d.ts +39 -0
- package/package.json +2 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import output from './output.js';
|
|
5
|
+
import { ExitCodes } from './errors.js';
|
|
6
|
+
import { parseDuration } from './duration.js';
|
|
7
|
+
async function waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay) {
|
|
8
|
+
const spinner = ora('Waiting for evaluation completion...').start();
|
|
9
|
+
try {
|
|
10
|
+
await execa('kubectl', [
|
|
11
|
+
'wait',
|
|
12
|
+
'--for=condition=Completed',
|
|
13
|
+
`evaluation/${evaluationName}`,
|
|
14
|
+
`--timeout=${Math.floor(watchTimeoutMs / 1000)}s`,
|
|
15
|
+
], { timeout: watchTimeoutMs });
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
spinner.stop();
|
|
19
|
+
if (error instanceof Error && error.message.includes('timed out waiting')) {
|
|
20
|
+
console.error(chalk.red(`Evaluation did not complete within ${watchTimeoutDisplay}`));
|
|
21
|
+
process.exit(ExitCodes.Timeout);
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
spinner.stop();
|
|
26
|
+
const { stdout } = await execa('kubectl', ['get', 'evaluation', evaluationName, '-o', 'json'], { stdio: 'pipe' });
|
|
27
|
+
const evaluation = JSON.parse(stdout);
|
|
28
|
+
const status = evaluation.status;
|
|
29
|
+
if (status?.phase === 'done') {
|
|
30
|
+
console.log(chalk.green('\nEvaluation completed successfully:'));
|
|
31
|
+
if (status.score !== undefined) {
|
|
32
|
+
console.log(`Score: ${status.score}`);
|
|
33
|
+
}
|
|
34
|
+
if (status.passed !== undefined) {
|
|
35
|
+
console.log(`Result: ${status.passed ? chalk.green('PASSED') : chalk.red('FAILED')}`);
|
|
36
|
+
}
|
|
37
|
+
if (status.message) {
|
|
38
|
+
console.log(`Message: ${status.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (status?.phase === 'error') {
|
|
42
|
+
console.error(chalk.red(status.message || 'Evaluation failed with unknown error'));
|
|
43
|
+
process.exit(ExitCodes.OperationError);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
output.warning(`Unexpected evaluation phase: ${status?.phase}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export async function executeDirectEvaluation(options) {
|
|
50
|
+
const spinner = ora('Creating evaluation...').start();
|
|
51
|
+
const queryTimeoutMs = options.timeout
|
|
52
|
+
? parseDuration(options.timeout)
|
|
53
|
+
: parseDuration('5m');
|
|
54
|
+
const watchTimeoutMs = options.watchTimeout
|
|
55
|
+
? parseDuration(options.watchTimeout)
|
|
56
|
+
: queryTimeoutMs + 60000;
|
|
57
|
+
const timestamp = Date.now();
|
|
58
|
+
const evaluationName = `cli-eval-${timestamp}`;
|
|
59
|
+
const evaluationManifest = {
|
|
60
|
+
apiVersion: 'ark.mckinsey.com/v1alpha1',
|
|
61
|
+
kind: 'Evaluation',
|
|
62
|
+
metadata: {
|
|
63
|
+
name: evaluationName,
|
|
64
|
+
},
|
|
65
|
+
spec: {
|
|
66
|
+
type: 'direct',
|
|
67
|
+
evaluator: {
|
|
68
|
+
name: options.evaluatorName,
|
|
69
|
+
},
|
|
70
|
+
config: {
|
|
71
|
+
input: options.input,
|
|
72
|
+
output: options.output,
|
|
73
|
+
},
|
|
74
|
+
...(options.timeout && { timeout: options.timeout }),
|
|
75
|
+
ttl: '1h',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
spinner.text = 'Submitting evaluation...';
|
|
80
|
+
await execa('kubectl', ['apply', '-f', '-'], {
|
|
81
|
+
input: JSON.stringify(evaluationManifest),
|
|
82
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
83
|
+
});
|
|
84
|
+
spinner.stop();
|
|
85
|
+
const watchTimeoutDisplay = options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`;
|
|
86
|
+
await waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
spinner.stop();
|
|
90
|
+
console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
|
|
91
|
+
process.exit(ExitCodes.CliError);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export async function executeQueryEvaluation(options) {
|
|
95
|
+
const spinner = ora('Creating evaluation...').start();
|
|
96
|
+
const queryTimeoutMs = options.timeout
|
|
97
|
+
? parseDuration(options.timeout)
|
|
98
|
+
: parseDuration('5m');
|
|
99
|
+
const watchTimeoutMs = options.watchTimeout
|
|
100
|
+
? parseDuration(options.watchTimeout)
|
|
101
|
+
: queryTimeoutMs + 60000;
|
|
102
|
+
const timestamp = Date.now();
|
|
103
|
+
const evaluationName = `cli-eval-${timestamp}`;
|
|
104
|
+
let responseTarget;
|
|
105
|
+
if (options.responseTarget) {
|
|
106
|
+
const parts = options.responseTarget.split(':');
|
|
107
|
+
if (parts.length === 2) {
|
|
108
|
+
responseTarget = {
|
|
109
|
+
type: parts[0],
|
|
110
|
+
name: parts[1],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
spinner.stop();
|
|
115
|
+
console.error(chalk.red('Invalid response-target format. Use: type:name (e.g., agent:my-agent)'));
|
|
116
|
+
process.exit(ExitCodes.CliError);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const evaluationManifest = {
|
|
120
|
+
apiVersion: 'ark.mckinsey.com/v1alpha1',
|
|
121
|
+
kind: 'Evaluation',
|
|
122
|
+
metadata: {
|
|
123
|
+
name: evaluationName,
|
|
124
|
+
},
|
|
125
|
+
spec: {
|
|
126
|
+
type: 'query',
|
|
127
|
+
evaluator: {
|
|
128
|
+
name: options.evaluatorName,
|
|
129
|
+
},
|
|
130
|
+
config: {
|
|
131
|
+
queryRef: {
|
|
132
|
+
name: options.queryName,
|
|
133
|
+
},
|
|
134
|
+
...(responseTarget && { responseTarget }),
|
|
135
|
+
},
|
|
136
|
+
...(options.timeout && { timeout: options.timeout }),
|
|
137
|
+
ttl: '1h',
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
spinner.text = 'Submitting evaluation...';
|
|
142
|
+
await execa('kubectl', ['apply', '-f', '-'], {
|
|
143
|
+
input: JSON.stringify(evaluationManifest),
|
|
144
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
145
|
+
});
|
|
146
|
+
spinner.stop();
|
|
147
|
+
const watchTimeoutDisplay = options.watchTimeout ?? `${Math.floor(watchTimeoutMs / 1000)}s`;
|
|
148
|
+
await waitForEvaluationAndDisplayResults(evaluationName, watchTimeoutMs, watchTimeoutDisplay);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
spinner.stop();
|
|
152
|
+
console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
|
|
153
|
+
process.exit(ExitCodes.CliError);
|
|
154
|
+
}
|
|
155
|
+
}
|
package/dist/lib/executeQuery.js
CHANGED
|
@@ -7,12 +7,15 @@ import chalk from 'chalk';
|
|
|
7
7
|
import output from './output.js';
|
|
8
8
|
import { ExitCodes } from './errors.js';
|
|
9
9
|
import { parseDuration } from './duration.js';
|
|
10
|
+
import { getResource } from './kubectl.js';
|
|
10
11
|
/**
|
|
11
12
|
* Execute a query against any ARK target (model, agent, team)
|
|
12
13
|
* This is the shared implementation used by all query commands
|
|
13
14
|
*/
|
|
14
15
|
export async function executeQuery(options) {
|
|
15
|
-
const spinner =
|
|
16
|
+
const spinner = options.outputFormat
|
|
17
|
+
? null
|
|
18
|
+
: ora('Creating query...').start();
|
|
16
19
|
const queryTimeoutMs = options.timeout
|
|
17
20
|
? parseDuration(options.timeout)
|
|
18
21
|
: parseDuration('5m');
|
|
@@ -40,13 +43,15 @@ export async function executeQuery(options) {
|
|
|
40
43
|
};
|
|
41
44
|
try {
|
|
42
45
|
// Apply the query
|
|
43
|
-
spinner
|
|
46
|
+
if (spinner)
|
|
47
|
+
spinner.text = 'Submitting query...';
|
|
44
48
|
await execa('kubectl', ['apply', '-f', '-'], {
|
|
45
49
|
input: JSON.stringify(queryManifest),
|
|
46
50
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
47
51
|
});
|
|
48
52
|
// Watch for query completion using kubectl wait
|
|
49
|
-
spinner
|
|
53
|
+
if (spinner)
|
|
54
|
+
spinner.text = 'Waiting for query completion...';
|
|
50
55
|
try {
|
|
51
56
|
await execa('kubectl', [
|
|
52
57
|
'wait',
|
|
@@ -56,7 +61,8 @@ export async function executeQuery(options) {
|
|
|
56
61
|
], { timeout: watchTimeoutMs });
|
|
57
62
|
}
|
|
58
63
|
catch (error) {
|
|
59
|
-
spinner
|
|
64
|
+
if (spinner)
|
|
65
|
+
spinner.stop();
|
|
60
66
|
// Check if it's a timeout or other error
|
|
61
67
|
if (error instanceof Error &&
|
|
62
68
|
error.message.includes('timed out waiting')) {
|
|
@@ -65,11 +71,35 @@ export async function executeQuery(options) {
|
|
|
65
71
|
}
|
|
66
72
|
// For other errors, fetch the query to check status
|
|
67
73
|
}
|
|
68
|
-
spinner
|
|
74
|
+
if (spinner)
|
|
75
|
+
spinner.stop();
|
|
76
|
+
// If output format is specified, output the resource and return
|
|
77
|
+
if (options.outputFormat) {
|
|
78
|
+
try {
|
|
79
|
+
if (options.outputFormat === 'name') {
|
|
80
|
+
console.log(queryName);
|
|
81
|
+
}
|
|
82
|
+
else if (options.outputFormat === 'json' ||
|
|
83
|
+
options.outputFormat === 'yaml') {
|
|
84
|
+
const { stdout } = await execa('kubectl', ['get', 'query', queryName, '-o', options.outputFormat], { stdio: 'pipe' });
|
|
85
|
+
console.log(stdout);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.error(chalk.red(`Invalid output format: ${options.outputFormat}. Use: yaml, json, or name`));
|
|
89
|
+
process.exit(ExitCodes.CliError);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error(chalk.red(error instanceof Error
|
|
95
|
+
? error.message
|
|
96
|
+
: 'Failed to fetch query resource'));
|
|
97
|
+
process.exit(ExitCodes.CliError);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
69
100
|
// Fetch final query state
|
|
70
101
|
try {
|
|
71
|
-
const
|
|
72
|
-
const query = JSON.parse(stdout);
|
|
102
|
+
const query = await getResource('queries', queryName);
|
|
73
103
|
const phase = query.status?.phase;
|
|
74
104
|
// Check if query completed successfully or with error
|
|
75
105
|
if (phase === 'done') {
|
|
@@ -88,7 +118,12 @@ export async function executeQuery(options) {
|
|
|
88
118
|
process.exit(ExitCodes.OperationError);
|
|
89
119
|
}
|
|
90
120
|
else if (phase === 'canceled') {
|
|
91
|
-
spinner
|
|
121
|
+
if (spinner) {
|
|
122
|
+
spinner.warn('Query canceled');
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
output.warning('Query canceled');
|
|
126
|
+
}
|
|
92
127
|
if (query.status?.message) {
|
|
93
128
|
output.warning(query.status.message);
|
|
94
129
|
}
|
|
@@ -103,7 +138,8 @@ export async function executeQuery(options) {
|
|
|
103
138
|
}
|
|
104
139
|
}
|
|
105
140
|
catch (error) {
|
|
106
|
-
spinner
|
|
141
|
+
if (spinner)
|
|
142
|
+
spinner.stop();
|
|
107
143
|
console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
|
|
108
144
|
process.exit(ExitCodes.CliError);
|
|
109
145
|
}
|
|
@@ -69,7 +69,7 @@ describe('executeQuery', () => {
|
|
|
69
69
|
if (args.includes('apply')) {
|
|
70
70
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
71
71
|
}
|
|
72
|
-
if (args.includes('get') && args.includes('
|
|
72
|
+
if (args.includes('get') && args.includes('queries')) {
|
|
73
73
|
return {
|
|
74
74
|
stdout: JSON.stringify(mockQueryResponse),
|
|
75
75
|
stderr: '',
|
|
@@ -98,7 +98,7 @@ describe('executeQuery', () => {
|
|
|
98
98
|
if (args.includes('apply')) {
|
|
99
99
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
100
100
|
}
|
|
101
|
-
if (args.includes('get') && args.includes('
|
|
101
|
+
if (args.includes('get') && args.includes('queries')) {
|
|
102
102
|
return {
|
|
103
103
|
stdout: JSON.stringify(mockQueryResponse),
|
|
104
104
|
stderr: '',
|
|
@@ -132,7 +132,7 @@ describe('executeQuery', () => {
|
|
|
132
132
|
if (args.includes('apply')) {
|
|
133
133
|
return { stdout: '', stderr: '', exitCode: 0 };
|
|
134
134
|
}
|
|
135
|
-
if (args.includes('get') && args.includes('
|
|
135
|
+
if (args.includes('get') && args.includes('queries')) {
|
|
136
136
|
return {
|
|
137
137
|
stdout: JSON.stringify(mockQueryResponse),
|
|
138
138
|
stderr: '',
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
export async function getResource(resourceType, name) {
|
|
3
|
+
if (name === '@latest') {
|
|
4
|
+
const result = await execa('kubectl', [
|
|
5
|
+
'get',
|
|
6
|
+
resourceType,
|
|
7
|
+
'--sort-by=.metadata.creationTimestamp',
|
|
8
|
+
'-o',
|
|
9
|
+
'json',
|
|
10
|
+
], { stdio: 'pipe' });
|
|
11
|
+
const data = JSON.parse(result.stdout);
|
|
12
|
+
const resources = data.items || [];
|
|
13
|
+
if (resources.length === 0) {
|
|
14
|
+
throw new Error(`No ${resourceType} found`);
|
|
15
|
+
}
|
|
16
|
+
return resources[resources.length - 1];
|
|
17
|
+
}
|
|
18
|
+
const result = await execa('kubectl', ['get', resourceType, name, '-o', 'json'], { stdio: 'pipe' });
|
|
19
|
+
return JSON.parse(result.stdout);
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
const mockExeca = jest.fn();
|
|
3
|
+
jest.unstable_mockModule('execa', () => ({
|
|
4
|
+
execa: mockExeca,
|
|
5
|
+
}));
|
|
6
|
+
const { getResource } = await import('./kubectl.js');
|
|
7
|
+
describe('kubectl', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
jest.clearAllMocks();
|
|
10
|
+
});
|
|
11
|
+
describe('getResource', () => {
|
|
12
|
+
it('should get a specific resource by name', async () => {
|
|
13
|
+
const mockResource = {
|
|
14
|
+
metadata: {
|
|
15
|
+
name: 'test-query',
|
|
16
|
+
creationTimestamp: '2024-01-01T00:00:00Z',
|
|
17
|
+
},
|
|
18
|
+
spec: {
|
|
19
|
+
value: 'test',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
mockExeca.mockResolvedValue({
|
|
23
|
+
stdout: JSON.stringify(mockResource),
|
|
24
|
+
});
|
|
25
|
+
const result = await getResource('queries', 'test-query');
|
|
26
|
+
expect(result).toEqual(mockResource);
|
|
27
|
+
expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'queries', 'test-query', '-o', 'json'], { stdio: 'pipe' });
|
|
28
|
+
});
|
|
29
|
+
it('should get latest resource when name is @latest', async () => {
|
|
30
|
+
const mockResources = [
|
|
31
|
+
{
|
|
32
|
+
metadata: {
|
|
33
|
+
name: 'query-1',
|
|
34
|
+
creationTimestamp: '2024-01-01T00:00:00Z',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
metadata: {
|
|
39
|
+
name: 'query-2',
|
|
40
|
+
creationTimestamp: '2024-01-02T00:00:00Z',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
metadata: {
|
|
45
|
+
name: 'query-3',
|
|
46
|
+
creationTimestamp: '2024-01-03T00:00:00Z',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
mockExeca.mockResolvedValue({
|
|
51
|
+
stdout: JSON.stringify({ items: mockResources }),
|
|
52
|
+
});
|
|
53
|
+
const result = await getResource('queries', '@latest');
|
|
54
|
+
expect(result).toEqual(mockResources[2]);
|
|
55
|
+
expect(mockExeca).toHaveBeenCalledWith('kubectl', [
|
|
56
|
+
'get',
|
|
57
|
+
'queries',
|
|
58
|
+
'--sort-by=.metadata.creationTimestamp',
|
|
59
|
+
'-o',
|
|
60
|
+
'json',
|
|
61
|
+
], { stdio: 'pipe' });
|
|
62
|
+
});
|
|
63
|
+
it('should throw error when @latest finds no resources', async () => {
|
|
64
|
+
mockExeca.mockResolvedValue({
|
|
65
|
+
stdout: JSON.stringify({ items: [] }),
|
|
66
|
+
});
|
|
67
|
+
await expect(getResource('queries', '@latest')).rejects.toThrow('No queries found');
|
|
68
|
+
});
|
|
69
|
+
it('should handle kubectl errors', async () => {
|
|
70
|
+
mockExeca.mockRejectedValue(new Error('kubectl error'));
|
|
71
|
+
await expect(getResource('queries', 'test-query')).rejects.toThrow('kubectl error');
|
|
72
|
+
});
|
|
73
|
+
it('should work with different resource types', async () => {
|
|
74
|
+
const mockAgent = {
|
|
75
|
+
metadata: {
|
|
76
|
+
name: 'test-agent',
|
|
77
|
+
creationTimestamp: '2024-01-01T00:00:00Z',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
mockExeca.mockResolvedValue({
|
|
81
|
+
stdout: JSON.stringify(mockAgent),
|
|
82
|
+
});
|
|
83
|
+
const result = await getResource('agents', 'test-agent');
|
|
84
|
+
expect(result).toEqual(mockAgent);
|
|
85
|
+
expect(mockExeca).toHaveBeenCalledWith('kubectl', ['get', 'agents', 'test-agent', '-o', 'json'], { stdio: 'pipe' });
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readStdin(): Promise<string>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export async function readStdin() {
|
|
2
|
+
return new Promise((resolve) => {
|
|
3
|
+
if (process.stdin.isTTY) {
|
|
4
|
+
resolve('');
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
let data = '';
|
|
8
|
+
process.stdin.setEncoding('utf8');
|
|
9
|
+
process.stdin.on('data', (chunk) => {
|
|
10
|
+
data += chunk;
|
|
11
|
+
});
|
|
12
|
+
process.stdin.on('end', () => {
|
|
13
|
+
resolve(data.trim());
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
|
|
2
|
+
import { readStdin } from './stdin.js';
|
|
3
|
+
import { Readable } from 'stream';
|
|
4
|
+
describe('readStdin', () => {
|
|
5
|
+
let originalStdin;
|
|
6
|
+
let mockStdin;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
originalStdin = process.stdin;
|
|
9
|
+
mockStdin = new Readable({
|
|
10
|
+
read() { },
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(process, 'stdin', {
|
|
13
|
+
value: mockStdin,
|
|
14
|
+
writable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
Object.defineProperty(process, 'stdin', {
|
|
20
|
+
value: originalStdin,
|
|
21
|
+
writable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
});
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
it('should return empty string when stdin is TTY', async () => {
|
|
27
|
+
mockStdin.isTTY = true;
|
|
28
|
+
const result = await readStdin();
|
|
29
|
+
expect(result).toBe('');
|
|
30
|
+
});
|
|
31
|
+
it('should read single chunk from stdin', async () => {
|
|
32
|
+
mockStdin.isTTY = false;
|
|
33
|
+
const promise = readStdin();
|
|
34
|
+
mockStdin.push('test-query');
|
|
35
|
+
mockStdin.push(null);
|
|
36
|
+
const result = await promise;
|
|
37
|
+
expect(result).toBe('test-query');
|
|
38
|
+
});
|
|
39
|
+
it('should read multiple chunks from stdin', async () => {
|
|
40
|
+
mockStdin.isTTY = false;
|
|
41
|
+
const promise = readStdin();
|
|
42
|
+
mockStdin.push('test-');
|
|
43
|
+
mockStdin.push('query-');
|
|
44
|
+
mockStdin.push('name');
|
|
45
|
+
mockStdin.push(null);
|
|
46
|
+
const result = await promise;
|
|
47
|
+
expect(result).toBe('test-query-name');
|
|
48
|
+
});
|
|
49
|
+
it('should trim whitespace from stdin input', async () => {
|
|
50
|
+
mockStdin.isTTY = false;
|
|
51
|
+
const promise = readStdin();
|
|
52
|
+
mockStdin.push(' test-query \n');
|
|
53
|
+
mockStdin.push(null);
|
|
54
|
+
const result = await promise;
|
|
55
|
+
expect(result).toBe('test-query');
|
|
56
|
+
});
|
|
57
|
+
it('should handle empty stdin input', async () => {
|
|
58
|
+
mockStdin.isTTY = false;
|
|
59
|
+
const promise = readStdin();
|
|
60
|
+
mockStdin.push(null);
|
|
61
|
+
const result = await promise;
|
|
62
|
+
expect(result).toBe('');
|
|
63
|
+
});
|
|
64
|
+
it('should handle stdin with only whitespace', async () => {
|
|
65
|
+
mockStdin.isTTY = false;
|
|
66
|
+
const promise = readStdin();
|
|
67
|
+
mockStdin.push(' \n\n ');
|
|
68
|
+
mockStdin.push(null);
|
|
69
|
+
const result = await promise;
|
|
70
|
+
expect(result).toBe('');
|
|
71
|
+
});
|
|
72
|
+
it('should handle multiline input', async () => {
|
|
73
|
+
mockStdin.isTTY = false;
|
|
74
|
+
const promise = readStdin();
|
|
75
|
+
mockStdin.push('line1\n');
|
|
76
|
+
mockStdin.push('line2\n');
|
|
77
|
+
mockStdin.push('line3');
|
|
78
|
+
mockStdin.push(null);
|
|
79
|
+
const result = await promise;
|
|
80
|
+
expect(result).toBe('line1\nline2\nline3');
|
|
81
|
+
});
|
|
82
|
+
});
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export interface CommandVersionConfig {
|
|
|
52
52
|
export interface K8sMetadata {
|
|
53
53
|
name: string;
|
|
54
54
|
namespace?: string;
|
|
55
|
+
creationTimestamp?: string;
|
|
55
56
|
}
|
|
56
57
|
export interface K8sCondition {
|
|
57
58
|
type: string;
|
|
@@ -119,3 +120,41 @@ export interface ClusterInfo {
|
|
|
119
120
|
user?: string;
|
|
120
121
|
namespace?: string;
|
|
121
122
|
}
|
|
123
|
+
export interface EvaluationManifest {
|
|
124
|
+
apiVersion: string;
|
|
125
|
+
kind: 'Evaluation';
|
|
126
|
+
metadata: {
|
|
127
|
+
name: string;
|
|
128
|
+
};
|
|
129
|
+
spec: {
|
|
130
|
+
type: 'direct' | 'query';
|
|
131
|
+
evaluator: {
|
|
132
|
+
name: string;
|
|
133
|
+
};
|
|
134
|
+
config: {
|
|
135
|
+
input?: string;
|
|
136
|
+
output?: string;
|
|
137
|
+
queryRef?: {
|
|
138
|
+
name: string;
|
|
139
|
+
};
|
|
140
|
+
responseTarget?: {
|
|
141
|
+
type: string;
|
|
142
|
+
name: string;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
timeout?: string;
|
|
146
|
+
ttl?: string;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export interface EvaluationStatus {
|
|
150
|
+
phase?: 'pending' | 'running' | 'done' | 'error';
|
|
151
|
+
score?: string;
|
|
152
|
+
passed?: boolean;
|
|
153
|
+
message?: string;
|
|
154
|
+
}
|
|
155
|
+
export interface Evaluation {
|
|
156
|
+
metadata: {
|
|
157
|
+
name: string;
|
|
158
|
+
};
|
|
159
|
+
status?: EvaluationStatus;
|
|
160
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agents-at-scale/ark",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.42",
|
|
4
4
|
"description": "Ark CLI - Interactive terminal interface for ARK agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"@types/debug": "^4.1.12",
|
|
68
68
|
"@types/inquirer": "^9.0.7",
|
|
69
69
|
"@types/jest": "^30.0.0",
|
|
70
|
+
"@types/marked-terminal": "^6.1.1",
|
|
70
71
|
"@types/node": "^22.10.2",
|
|
71
72
|
"@types/react": "^19.1.13",
|
|
72
73
|
"@typescript-eslint/eslint-plugin": "^8.20.0",
|