@agentuity/cli 0.0.43 → 0.0.45
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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +10 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/banner.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cmd/auth/api.d.ts +4 -4
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/signup.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/add.d.ts +2 -0
- package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/api.d.ts +16 -0
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/delete.d.ts +2 -0
- package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/index.d.ts +3 -0
- package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/list.d.ts +2 -0
- package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
- package/dist/cmd/auth/whoami.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.d.ts +14 -3
- package/dist/cmd/bundle/ast.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.test.d.ts +2 -0
- package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -1
- package/dist/cmd/bundle/bundler.d.ts.map +1 -1
- package/dist/cmd/bundle/file.d.ts.map +1 -1
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
- package/dist/cmd/bundle/plugin.d.ts +2 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/domain.d.ts +17 -0
- package/dist/cmd/cloud/domain.d.ts.map +1 -0
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/resource/add.d.ts +2 -0
- package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/delete.d.ts +2 -0
- package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/index.d.ts +3 -0
- package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/list.d.ts +2 -0
- package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/download.d.ts +2 -0
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/index.d.ts +3 -0
- package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/upload.d.ts +2 -0
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/ssh.d.ts +2 -0
- package/dist/cmd/cloud/ssh.d.ts.map +1 -0
- package/dist/cmd/dev/api.d.ts +18 -0
- package/dist/cmd/dev/api.d.ts.map +1 -0
- package/dist/cmd/dev/download.d.ts +11 -0
- package/dist/cmd/dev/download.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/templates.d.ts +3 -0
- package/dist/cmd/dev/templates.d.ts.map +1 -0
- package/dist/cmd/env/delete.d.ts.map +1 -1
- package/dist/cmd/env/get.d.ts.map +1 -1
- package/dist/cmd/env/import.d.ts.map +1 -1
- package/dist/cmd/env/list.d.ts.map +1 -1
- package/dist/cmd/env/pull.d.ts.map +1 -1
- package/dist/cmd/env/push.d.ts.map +1 -1
- package/dist/cmd/env/set.d.ts.map +1 -1
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/delete.d.ts.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +4 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/secret/delete.d.ts.map +1 -1
- package/dist/cmd/secret/get.d.ts.map +1 -1
- package/dist/cmd/secret/import.d.ts.map +1 -1
- package/dist/cmd/secret/list.d.ts.map +1 -1
- package/dist/cmd/secret/pull.d.ts.map +1 -1
- package/dist/cmd/secret/push.d.ts.map +1 -1
- package/dist/cmd/secret/set.d.ts.map +1 -1
- package/dist/config.d.ts +9 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/crypto/box.d.ts +65 -0
- package/dist/crypto/box.d.ts.map +1 -0
- package/dist/crypto/box.test.d.ts +2 -0
- package/dist/crypto/box.test.d.ts.map +1 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/steps.d.ts +4 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/tui.d.ts +31 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/types.d.ts +249 -126
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detectSubagent.d.ts +15 -0
- package/dist/utils/detectSubagent.d.ts.map +1 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/package.json +11 -3
- package/src/api-errors.md +2 -2
- package/src/api.ts +12 -7
- package/src/auth.ts +116 -7
- package/src/banner.ts +13 -6
- package/src/cli.ts +695 -63
- package/src/cmd/auth/api.ts +10 -16
- package/src/cmd/auth/index.ts +2 -1
- package/src/cmd/auth/login.ts +24 -8
- package/src/cmd/auth/signup.ts +15 -11
- package/src/cmd/auth/ssh/add.ts +263 -0
- package/src/cmd/auth/ssh/api.ts +94 -0
- package/src/cmd/auth/ssh/delete.ts +102 -0
- package/src/cmd/auth/ssh/index.ts +10 -0
- package/src/cmd/auth/ssh/list.ts +74 -0
- package/src/cmd/auth/whoami.ts +13 -13
- package/src/cmd/bundle/ast.test.ts +565 -0
- package/src/cmd/bundle/ast.ts +457 -44
- package/src/cmd/bundle/bundler.ts +255 -57
- package/src/cmd/bundle/file.ts +6 -12
- package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
- package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
- package/src/cmd/bundle/index.ts +9 -9
- package/src/cmd/bundle/patch/aisdk.ts +1 -1
- package/src/cmd/bundle/plugin.ts +373 -53
- package/src/cmd/cloud/deploy.ts +300 -93
- package/src/cmd/cloud/domain.ts +92 -0
- package/src/cmd/cloud/index.ts +4 -1
- package/src/cmd/cloud/resource/add.ts +56 -0
- package/src/cmd/cloud/resource/delete.ts +120 -0
- package/src/cmd/cloud/resource/index.ts +11 -0
- package/src/cmd/cloud/resource/list.ts +69 -0
- package/src/cmd/cloud/scp/download.ts +59 -0
- package/src/cmd/cloud/scp/index.ts +9 -0
- package/src/cmd/cloud/scp/upload.ts +62 -0
- package/src/cmd/cloud/ssh.ts +68 -0
- package/src/cmd/dev/api.ts +46 -0
- package/src/cmd/dev/download.ts +111 -0
- package/src/cmd/dev/index.ts +360 -34
- package/src/cmd/dev/templates.ts +84 -0
- package/src/cmd/env/delete.ts +5 -20
- package/src/cmd/env/get.ts +5 -18
- package/src/cmd/env/import.ts +5 -20
- package/src/cmd/env/list.ts +5 -18
- package/src/cmd/env/pull.ts +10 -23
- package/src/cmd/env/push.ts +5 -23
- package/src/cmd/env/set.ts +5 -20
- package/src/cmd/index.ts +2 -2
- package/src/cmd/profile/show.ts +15 -6
- package/src/cmd/project/create.ts +7 -2
- package/src/cmd/project/delete.ts +75 -18
- package/src/cmd/project/download.ts +2 -2
- package/src/cmd/project/list.ts +8 -8
- package/src/cmd/project/show.ts +3 -7
- package/src/cmd/project/template-flow.ts +170 -72
- package/src/cmd/secret/delete.ts +5 -20
- package/src/cmd/secret/get.ts +5 -18
- package/src/cmd/secret/import.ts +5 -20
- package/src/cmd/secret/list.ts +5 -18
- package/src/cmd/secret/pull.ts +10 -23
- package/src/cmd/secret/push.ts +5 -23
- package/src/cmd/secret/set.ts +5 -20
- package/src/config.ts +224 -24
- package/src/crypto/box.test.ts +431 -0
- package/src/crypto/box.ts +477 -0
- package/src/download.ts +1 -0
- package/src/env-util.test.ts +1 -1
- package/src/steps.ts +65 -6
- package/src/terminal.ts +24 -23
- package/src/tui.ts +192 -61
- package/src/types.ts +291 -201
- package/src/utils/detectSubagent.ts +31 -0
- package/src/utils/zip.ts +38 -0
- package/dist/cmd/example/create-user.d.ts +0 -2
- package/dist/cmd/example/create-user.d.ts.map +0 -1
- package/dist/cmd/example/create.d.ts +0 -2
- package/dist/cmd/example/create.d.ts.map +0 -1
- package/dist/cmd/example/deploy.d.ts +0 -2
- package/dist/cmd/example/deploy.d.ts.map +0 -1
- package/dist/cmd/example/index.d.ts +0 -2
- package/dist/cmd/example/index.d.ts.map +0 -1
- package/dist/cmd/example/list.d.ts +0 -2
- package/dist/cmd/example/list.d.ts.map +0 -1
- package/dist/cmd/example/optional-auth.d.ts +0 -3
- package/dist/cmd/example/optional-auth.d.ts.map +0 -1
- package/dist/cmd/example/run-command.d.ts +0 -2
- package/dist/cmd/example/run-command.d.ts.map +0 -1
- package/dist/cmd/example/sound.d.ts +0 -3
- package/dist/cmd/example/sound.d.ts.map +0 -1
- package/dist/cmd/example/spinner.d.ts +0 -2
- package/dist/cmd/example/spinner.d.ts.map +0 -1
- package/dist/cmd/example/steps.d.ts +0 -2
- package/dist/cmd/example/steps.d.ts.map +0 -1
- package/dist/cmd/example/version.d.ts +0 -2
- package/dist/cmd/example/version.d.ts.map +0 -1
- package/src/cmd/example/create-user.ts +0 -38
- package/src/cmd/example/create.ts +0 -31
- package/src/cmd/example/deploy.ts +0 -36
- package/src/cmd/example/index.ts +0 -29
- package/src/cmd/example/list.ts +0 -32
- package/src/cmd/example/optional-auth.ts +0 -38
- package/src/cmd/example/run-command.ts +0 -45
- package/src/cmd/example/sound.ts +0 -14
- package/src/cmd/example/spinner.ts +0 -44
- package/src/cmd/example/steps.ts +0 -66
- package/src/cmd/example/version.ts +0 -13
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createSubcommand } from '../../../types';
|
|
2
|
+
import { listSSHKeys } from './api';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
export const listCommand = createSubcommand({
|
|
7
|
+
name: 'list',
|
|
8
|
+
aliases: ['ls'],
|
|
9
|
+
description: 'List all SSH keys on your account',
|
|
10
|
+
requires: { apiClient: true, auth: true },
|
|
11
|
+
schema: {
|
|
12
|
+
options: z.object({
|
|
13
|
+
format: z.enum(['text', 'json']).default('text').describe('output format'),
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
async handler(ctx) {
|
|
17
|
+
const { logger, apiClient, opts } = ctx;
|
|
18
|
+
const format = opts.format;
|
|
19
|
+
|
|
20
|
+
if (!apiClient) {
|
|
21
|
+
logger.fatal('API client is not available');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const keys = await tui.spinner('Fetching SSH keys...', () => listSSHKeys(apiClient));
|
|
26
|
+
|
|
27
|
+
if (format === 'json') {
|
|
28
|
+
console.log(JSON.stringify(keys, null, 2));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tui.newline();
|
|
33
|
+
|
|
34
|
+
if (keys.length === 0) {
|
|
35
|
+
console.log('No SSH keys found');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(tui.bold('SSH Keys:'));
|
|
40
|
+
tui.newline();
|
|
41
|
+
|
|
42
|
+
// Create aligned table
|
|
43
|
+
const rows = keys.map((key) => [
|
|
44
|
+
key.keyType,
|
|
45
|
+
key.fingerprint,
|
|
46
|
+
key.comment || tui.muted('(no comment)'),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Calculate column widths
|
|
50
|
+
const widths = [
|
|
51
|
+
Math.max(4, ...rows.map((r) => r[0].length)),
|
|
52
|
+
Math.max(11, ...rows.map((r) => r[1].length)),
|
|
53
|
+
Math.max(7, ...rows.map((r) => Bun.stringWidth(r[2]))),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// Print header
|
|
57
|
+
console.log(
|
|
58
|
+
`${tui.bold('TYPE'.padEnd(widths[0]))} ${tui.bold('FINGERPRINT'.padEnd(widths[1]))} ${tui.bold('COMMENT')}`
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Print rows
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
console.log(`${row[0].padEnd(widths[0])} ${row[1].padEnd(widths[1])} ${row[2]}`);
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logger.trace(error);
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
logger.fatal(`Failed to list SSH keys: ${error.message}`);
|
|
69
|
+
} else {
|
|
70
|
+
logger.fatal('Failed to list SSH keys');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
package/src/cmd/auth/whoami.ts
CHANGED
|
@@ -2,12 +2,11 @@ import { z } from 'zod';
|
|
|
2
2
|
import { createSubcommand } from '../../types';
|
|
3
3
|
import * as tui from '../../tui';
|
|
4
4
|
import { whoami } from '@agentuity/server';
|
|
5
|
-
import { getAPIBaseURL, APIClient } from '../../api';
|
|
6
5
|
|
|
7
6
|
export const whoamiCommand = createSubcommand({
|
|
8
7
|
name: 'whoami',
|
|
9
8
|
description: 'Display information about the currently authenticated user',
|
|
10
|
-
|
|
9
|
+
requires: { auth: true, apiClient: true },
|
|
11
10
|
schema: {
|
|
12
11
|
options: z.object({
|
|
13
12
|
format: z
|
|
@@ -18,20 +17,21 @@ export const whoamiCommand = createSubcommand({
|
|
|
18
17
|
},
|
|
19
18
|
|
|
20
19
|
async handler(ctx) {
|
|
21
|
-
const {
|
|
20
|
+
const { apiClient, opts, auth } = ctx;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return whoami(client!);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (!result.data) {
|
|
31
|
-
tui.fatal('Failed to get user information');
|
|
22
|
+
if (!apiClient) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'API client is not available. This is likely a configuration or initialization issue.'
|
|
25
|
+
);
|
|
32
26
|
}
|
|
33
27
|
|
|
34
|
-
const user =
|
|
28
|
+
const user = await tui.spinner({
|
|
29
|
+
message: 'Fetching user information',
|
|
30
|
+
clearOnSuccess: true,
|
|
31
|
+
callback: () => {
|
|
32
|
+
return whoami(apiClient);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
35
|
|
|
36
36
|
if (opts?.format === 'json') {
|
|
37
37
|
console.log(
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { parseEvalMetadata } from './ast';
|
|
3
|
+
|
|
4
|
+
const TEST_ROOT_DIR = '/test/root';
|
|
5
|
+
const TEST_PROJECT_ID = 'test-project-id';
|
|
6
|
+
const TEST_DEPLOYMENT_ID = 'test-deployment-id';
|
|
7
|
+
|
|
8
|
+
describe('parseEvalMetadata', () => {
|
|
9
|
+
describe('eval with metadata.name', () => {
|
|
10
|
+
test('uses metadata.name when provided', () => {
|
|
11
|
+
const code = `
|
|
12
|
+
import agent from './agent';
|
|
13
|
+
export const myEval = agent.createEval({
|
|
14
|
+
metadata: {
|
|
15
|
+
name: 'custom-eval-name',
|
|
16
|
+
description: 'Test description'
|
|
17
|
+
},
|
|
18
|
+
handler: async () => ({ success: true, passed: true })
|
|
19
|
+
});
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const [, result] = parseEvalMetadata(
|
|
23
|
+
TEST_ROOT_DIR,
|
|
24
|
+
'/test/root/src/agents/test/eval.ts',
|
|
25
|
+
code,
|
|
26
|
+
TEST_PROJECT_ID,
|
|
27
|
+
TEST_DEPLOYMENT_ID
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(result).toHaveLength(1);
|
|
31
|
+
expect(result[0].name).toBe('custom-eval-name');
|
|
32
|
+
expect(result[0].description).toBe('Test description');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('eval with variable name only', () => {
|
|
37
|
+
test('uses camelToKebab of variable name when metadata.name is not provided', () => {
|
|
38
|
+
const code = `
|
|
39
|
+
import agent from './agent';
|
|
40
|
+
export const myTestEval = agent.createEval({
|
|
41
|
+
metadata: {
|
|
42
|
+
description: 'Test description'
|
|
43
|
+
},
|
|
44
|
+
handler: async () => ({ success: true, passed: true })
|
|
45
|
+
});
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const [, result] = parseEvalMetadata(
|
|
49
|
+
TEST_ROOT_DIR,
|
|
50
|
+
'/test/root/src/agents/test/eval.ts',
|
|
51
|
+
code,
|
|
52
|
+
TEST_PROJECT_ID,
|
|
53
|
+
TEST_DEPLOYMENT_ID
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(result).toHaveLength(1);
|
|
57
|
+
expect(result[0].name).toBe('my-test-eval');
|
|
58
|
+
expect(result[0].description).toBe('Test description');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('uses camelToKebab of variable name when metadata is not provided', () => {
|
|
62
|
+
const code = `
|
|
63
|
+
import agent from './agent';
|
|
64
|
+
export const noMetadataEval = agent.createEval({
|
|
65
|
+
handler: async () => ({ success: true, passed: true })
|
|
66
|
+
});
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const [, result] = parseEvalMetadata(
|
|
70
|
+
TEST_ROOT_DIR,
|
|
71
|
+
'/test/root/src/agents/test/eval.ts',
|
|
72
|
+
code,
|
|
73
|
+
TEST_PROJECT_ID,
|
|
74
|
+
TEST_DEPLOYMENT_ID
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(result).toHaveLength(1);
|
|
78
|
+
expect(result[0].name).toBe('no-metadata-eval');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('handles complex camelCase variable names correctly', () => {
|
|
82
|
+
const code = `
|
|
83
|
+
import agent from './agent';
|
|
84
|
+
export const complexCamelCaseEvalName = agent.createEval({
|
|
85
|
+
handler: async () => ({ success: true, passed: true })
|
|
86
|
+
});
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const [, result] = parseEvalMetadata(
|
|
90
|
+
TEST_ROOT_DIR,
|
|
91
|
+
'/test/root/src/agents/test/eval.ts',
|
|
92
|
+
code,
|
|
93
|
+
TEST_PROJECT_ID,
|
|
94
|
+
TEST_DEPLOYMENT_ID
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(result).toHaveLength(1);
|
|
98
|
+
expect(result[0].name).toBe('complex-camel-case-eval-name');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('eval with both metadata.name and variable name', () => {
|
|
103
|
+
test('prefers metadata.name over variable name', () => {
|
|
104
|
+
const code = `
|
|
105
|
+
import agent from './agent';
|
|
106
|
+
export const variableNameEval = agent.createEval({
|
|
107
|
+
metadata: {
|
|
108
|
+
name: 'metadata-name-takes-priority'
|
|
109
|
+
},
|
|
110
|
+
handler: async () => ({ success: true, passed: true })
|
|
111
|
+
});
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
const [, result] = parseEvalMetadata(
|
|
115
|
+
TEST_ROOT_DIR,
|
|
116
|
+
'/test/root/src/agents/test/eval.ts',
|
|
117
|
+
code,
|
|
118
|
+
TEST_PROJECT_ID,
|
|
119
|
+
TEST_DEPLOYMENT_ID
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(result).toHaveLength(1);
|
|
123
|
+
expect(result[0].name).toBe('metadata-name-takes-priority');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('eval with neither name', () => {
|
|
128
|
+
test('throws error when eval has neither metadata.name nor variable name', () => {
|
|
129
|
+
// This is a difficult case to test with valid JavaScript, as all valid
|
|
130
|
+
// variable declarations should have an identifier. However, we can verify
|
|
131
|
+
// the error handling exists by testing that the error message format is correct.
|
|
132
|
+
//
|
|
133
|
+
// In practice, this error should never occur with valid code, but we want
|
|
134
|
+
// to ensure the error is clear and helpful if it does happen.
|
|
135
|
+
//
|
|
136
|
+
// The error case would occur if:
|
|
137
|
+
// 1. vardecl.id.type is not 'Identifier' (e.g., destructuring pattern)
|
|
138
|
+
// 2. metadata.name is not provided
|
|
139
|
+
//
|
|
140
|
+
// Since acorn-loose parses valid JavaScript, and valid eval declarations
|
|
141
|
+
// should always have an identifier, this is primarily a defensive check.
|
|
142
|
+
//
|
|
143
|
+
// We verify the error path exists in the code by checking that normal
|
|
144
|
+
// cases work correctly, and the error handling is in place.
|
|
145
|
+
|
|
146
|
+
// Test that normal cases work (implicitly tests error path doesn't trigger)
|
|
147
|
+
const code = `
|
|
148
|
+
import agent from './agent';
|
|
149
|
+
export const validEval = agent.createEval({
|
|
150
|
+
handler: async () => ({ success: true, passed: true })
|
|
151
|
+
});
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
const [, result] = parseEvalMetadata(
|
|
155
|
+
TEST_ROOT_DIR,
|
|
156
|
+
'/test/root/src/agents/test/eval.ts',
|
|
157
|
+
code,
|
|
158
|
+
TEST_PROJECT_ID,
|
|
159
|
+
TEST_DEPLOYMENT_ID
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result).toHaveLength(1);
|
|
163
|
+
expect(result[0].name).toBe('valid-eval');
|
|
164
|
+
|
|
165
|
+
// Note: To fully test the error case, we would need to mock the AST structure
|
|
166
|
+
// or use code that parses but doesn't extract a variable name. This is
|
|
167
|
+
// difficult with acorn-loose parsing valid JavaScript. The error handling
|
|
168
|
+
// is verified to exist in the code, and will throw if the condition is met.
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('multiple evals', () => {
|
|
173
|
+
test('parses multiple evals in same file correctly', () => {
|
|
174
|
+
const code = `
|
|
175
|
+
import agent from './agent';
|
|
176
|
+
|
|
177
|
+
export const firstEval = agent.createEval({
|
|
178
|
+
metadata: {
|
|
179
|
+
name: 'first-eval',
|
|
180
|
+
description: 'First eval'
|
|
181
|
+
},
|
|
182
|
+
handler: async () => ({ success: true, passed: true })
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const secondEval = agent.createEval({
|
|
186
|
+
metadata: {
|
|
187
|
+
name: 'second-eval',
|
|
188
|
+
description: 'Second eval'
|
|
189
|
+
},
|
|
190
|
+
handler: async () => ({ success: true, passed: true })
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
export const thirdEval = agent.createEval({
|
|
194
|
+
handler: async () => ({ success: true, passed: true })
|
|
195
|
+
});
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
const [, result] = parseEvalMetadata(
|
|
199
|
+
TEST_ROOT_DIR,
|
|
200
|
+
'/test/root/src/agents/test/eval.ts',
|
|
201
|
+
code,
|
|
202
|
+
TEST_PROJECT_ID,
|
|
203
|
+
TEST_DEPLOYMENT_ID
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
expect(result).toHaveLength(3);
|
|
207
|
+
expect(result[0].name).toBe('first-eval');
|
|
208
|
+
expect(result[0].description).toBe('First eval');
|
|
209
|
+
expect(result[1].name).toBe('second-eval');
|
|
210
|
+
expect(result[1].description).toBe('Second eval');
|
|
211
|
+
expect(result[2].name).toBe('third-eval');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('handles mix of metadata.name and variable name evals', () => {
|
|
215
|
+
const code = `
|
|
216
|
+
import agent from './agent';
|
|
217
|
+
|
|
218
|
+
export const withMetadataName = agent.createEval({
|
|
219
|
+
metadata: {
|
|
220
|
+
name: 'custom-name'
|
|
221
|
+
},
|
|
222
|
+
handler: async () => ({ success: true, passed: true })
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
export const withoutMetadataName = agent.createEval({
|
|
226
|
+
handler: async () => ({ success: true, passed: true })
|
|
227
|
+
});
|
|
228
|
+
`;
|
|
229
|
+
|
|
230
|
+
const [, result] = parseEvalMetadata(
|
|
231
|
+
TEST_ROOT_DIR,
|
|
232
|
+
'/test/root/src/agents/test/eval.ts',
|
|
233
|
+
code,
|
|
234
|
+
TEST_PROJECT_ID,
|
|
235
|
+
TEST_DEPLOYMENT_ID
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(result).toHaveLength(2);
|
|
239
|
+
expect(result[0].name).toBe('custom-name');
|
|
240
|
+
expect(result[1].name).toBe('without-metadata-name');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('export patterns', () => {
|
|
245
|
+
test('handles ExportNamedDeclaration pattern', () => {
|
|
246
|
+
const code = `
|
|
247
|
+
import agent from './agent';
|
|
248
|
+
export const exportedEval = agent.createEval({
|
|
249
|
+
handler: async () => ({ success: true, passed: true })
|
|
250
|
+
});
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
const [, result] = parseEvalMetadata(
|
|
254
|
+
TEST_ROOT_DIR,
|
|
255
|
+
'/test/root/src/agents/test/eval.ts',
|
|
256
|
+
code,
|
|
257
|
+
TEST_PROJECT_ID,
|
|
258
|
+
TEST_DEPLOYMENT_ID
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(result).toHaveLength(1);
|
|
262
|
+
expect(result[0].name).toBe('exported-eval');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('handles VariableDeclaration pattern (non-exported)', () => {
|
|
266
|
+
const code = `
|
|
267
|
+
import agent from './agent';
|
|
268
|
+
const nonExportedEval = agent.createEval({
|
|
269
|
+
handler: async () => ({ success: true, passed: true })
|
|
270
|
+
});
|
|
271
|
+
`;
|
|
272
|
+
|
|
273
|
+
const [, result] = parseEvalMetadata(
|
|
274
|
+
TEST_ROOT_DIR,
|
|
275
|
+
'/test/root/src/agents/test/eval.ts',
|
|
276
|
+
code,
|
|
277
|
+
TEST_PROJECT_ID,
|
|
278
|
+
TEST_DEPLOYMENT_ID
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(result).toHaveLength(1);
|
|
282
|
+
expect(result[0].name).toBe('non-exported-eval');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('eval ID generation', () => {
|
|
287
|
+
test('generates unique IDs for evals', () => {
|
|
288
|
+
const code = `
|
|
289
|
+
import agent from './agent';
|
|
290
|
+
|
|
291
|
+
export const eval1 = agent.createEval({
|
|
292
|
+
handler: async () => ({ success: true, passed: true })
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
export const eval2 = agent.createEval({
|
|
296
|
+
handler: async () => ({ success: true, passed: true })
|
|
297
|
+
});
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
const [, result] = parseEvalMetadata(
|
|
301
|
+
TEST_ROOT_DIR,
|
|
302
|
+
'/test/root/src/agents/test/eval.ts',
|
|
303
|
+
code,
|
|
304
|
+
TEST_PROJECT_ID,
|
|
305
|
+
TEST_DEPLOYMENT_ID
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expect(result).toHaveLength(2);
|
|
309
|
+
expect(result[0].id).toBeDefined();
|
|
310
|
+
expect(result[1].id).toBeDefined();
|
|
311
|
+
expect(result[0].id).not.toBe(result[1].id);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('generates consistent IDs for same eval', () => {
|
|
315
|
+
const code = `
|
|
316
|
+
import agent from './agent';
|
|
317
|
+
export const myEval = agent.createEval({
|
|
318
|
+
metadata: {
|
|
319
|
+
name: 'test-eval'
|
|
320
|
+
},
|
|
321
|
+
handler: async () => ({ success: true, passed: true })
|
|
322
|
+
});
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
const [, result1] = parseEvalMetadata(
|
|
326
|
+
TEST_ROOT_DIR,
|
|
327
|
+
'/test/root/src/agents/test/eval.ts',
|
|
328
|
+
code,
|
|
329
|
+
TEST_PROJECT_ID,
|
|
330
|
+
TEST_DEPLOYMENT_ID
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const [, result2] = parseEvalMetadata(
|
|
334
|
+
TEST_ROOT_DIR,
|
|
335
|
+
'/test/root/src/agents/test/eval.ts',
|
|
336
|
+
code,
|
|
337
|
+
TEST_PROJECT_ID,
|
|
338
|
+
TEST_DEPLOYMENT_ID
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(result1[0].id).toBe(result2[0].id);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe('edge cases', () => {
|
|
346
|
+
test('handles empty metadata object', () => {
|
|
347
|
+
const code = `
|
|
348
|
+
import agent from './agent';
|
|
349
|
+
export const emptyMetadataEval = agent.createEval({
|
|
350
|
+
metadata: {},
|
|
351
|
+
handler: async () => ({ success: true, passed: true })
|
|
352
|
+
});
|
|
353
|
+
`;
|
|
354
|
+
|
|
355
|
+
const [, result] = parseEvalMetadata(
|
|
356
|
+
TEST_ROOT_DIR,
|
|
357
|
+
'/test/root/src/agents/test/eval.ts',
|
|
358
|
+
code,
|
|
359
|
+
TEST_PROJECT_ID,
|
|
360
|
+
TEST_DEPLOYMENT_ID
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result).toHaveLength(1);
|
|
364
|
+
expect(result[0].name).toBe('empty-metadata-eval');
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('handles eval with only description in metadata', () => {
|
|
368
|
+
const code = `
|
|
369
|
+
import agent from './agent';
|
|
370
|
+
export const descriptionOnlyEval = agent.createEval({
|
|
371
|
+
metadata: {
|
|
372
|
+
description: 'Only description, no name'
|
|
373
|
+
},
|
|
374
|
+
handler: async () => ({ success: true, passed: true })
|
|
375
|
+
});
|
|
376
|
+
`;
|
|
377
|
+
|
|
378
|
+
const [, result] = parseEvalMetadata(
|
|
379
|
+
TEST_ROOT_DIR,
|
|
380
|
+
'/test/root/src/agents/test/eval.ts',
|
|
381
|
+
code,
|
|
382
|
+
TEST_PROJECT_ID,
|
|
383
|
+
TEST_DEPLOYMENT_ID
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
expect(result).toHaveLength(1);
|
|
387
|
+
expect(result[0].name).toBe('description-only-eval');
|
|
388
|
+
expect(result[0].description).toBe('Only description, no name');
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('duplicate eval names', () => {
|
|
393
|
+
test('throws error when duplicate eval names are found in same file', () => {
|
|
394
|
+
const code = `
|
|
395
|
+
import agent from './agent';
|
|
396
|
+
|
|
397
|
+
export const firstEval = agent.createEval({
|
|
398
|
+
metadata: {
|
|
399
|
+
name: 'duplicate-name'
|
|
400
|
+
},
|
|
401
|
+
handler: async () => ({ success: true, passed: true })
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
export const secondEval = agent.createEval({
|
|
405
|
+
metadata: {
|
|
406
|
+
name: 'duplicate-name'
|
|
407
|
+
},
|
|
408
|
+
handler: async () => ({ success: true, passed: true })
|
|
409
|
+
});
|
|
410
|
+
`;
|
|
411
|
+
|
|
412
|
+
expect(() => {
|
|
413
|
+
parseEvalMetadata(
|
|
414
|
+
TEST_ROOT_DIR,
|
|
415
|
+
'/test/root/src/agents/test/eval.ts',
|
|
416
|
+
code,
|
|
417
|
+
TEST_PROJECT_ID,
|
|
418
|
+
TEST_DEPLOYMENT_ID
|
|
419
|
+
);
|
|
420
|
+
}).toThrow(/Duplicate eval names found in .*eval\.ts: duplicate-name/);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('throws error when multiple duplicate eval names are found', () => {
|
|
424
|
+
const code = `
|
|
425
|
+
import agent from './agent';
|
|
426
|
+
|
|
427
|
+
export const eval1 = agent.createEval({
|
|
428
|
+
metadata: {
|
|
429
|
+
name: 'first-duplicate'
|
|
430
|
+
},
|
|
431
|
+
handler: async () => ({ success: true, passed: true })
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
export const eval2 = agent.createEval({
|
|
435
|
+
metadata: {
|
|
436
|
+
name: 'first-duplicate'
|
|
437
|
+
},
|
|
438
|
+
handler: async () => ({ success: true, passed: true })
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
export const eval3 = agent.createEval({
|
|
442
|
+
metadata: {
|
|
443
|
+
name: 'second-duplicate'
|
|
444
|
+
},
|
|
445
|
+
handler: async () => ({ success: true, passed: true })
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
export const eval4 = agent.createEval({
|
|
449
|
+
metadata: {
|
|
450
|
+
name: 'second-duplicate'
|
|
451
|
+
},
|
|
452
|
+
handler: async () => ({ success: true, passed: true })
|
|
453
|
+
});
|
|
454
|
+
`;
|
|
455
|
+
|
|
456
|
+
expect(() => {
|
|
457
|
+
parseEvalMetadata(
|
|
458
|
+
TEST_ROOT_DIR,
|
|
459
|
+
'/test/root/src/agents/test/eval.ts',
|
|
460
|
+
code,
|
|
461
|
+
TEST_PROJECT_ID,
|
|
462
|
+
TEST_DEPLOYMENT_ID
|
|
463
|
+
);
|
|
464
|
+
}).toThrow(/Duplicate eval names found in .*eval\.ts: first-duplicate, second-duplicate/);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('throws error when duplicate names come from variable names', () => {
|
|
468
|
+
// Note: We can't have two variables with the exact same name in valid JavaScript,
|
|
469
|
+
// but we can test that the validation works by using metadata.name to override
|
|
470
|
+
// variable names to create duplicates
|
|
471
|
+
const code = `
|
|
472
|
+
import agent from './agent';
|
|
473
|
+
|
|
474
|
+
export const eval1 = agent.createEval({
|
|
475
|
+
metadata: {
|
|
476
|
+
name: 'duplicate-name'
|
|
477
|
+
},
|
|
478
|
+
handler: async () => ({ success: true, passed: true })
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
export const eval2 = agent.createEval({
|
|
482
|
+
metadata: {
|
|
483
|
+
name: 'duplicate-name'
|
|
484
|
+
},
|
|
485
|
+
handler: async () => ({ success: true, passed: true })
|
|
486
|
+
});
|
|
487
|
+
`;
|
|
488
|
+
|
|
489
|
+
expect(() => {
|
|
490
|
+
parseEvalMetadata(
|
|
491
|
+
TEST_ROOT_DIR,
|
|
492
|
+
'/test/root/src/agents/test/eval.ts',
|
|
493
|
+
code,
|
|
494
|
+
TEST_PROJECT_ID,
|
|
495
|
+
TEST_DEPLOYMENT_ID
|
|
496
|
+
);
|
|
497
|
+
}).toThrow(/Duplicate eval names found in .*eval\.ts: duplicate-name/);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('throws error when duplicate names mix metadata.name and variable name', () => {
|
|
501
|
+
const code = `
|
|
502
|
+
import agent from './agent';
|
|
503
|
+
|
|
504
|
+
export const myEval = agent.createEval({
|
|
505
|
+
metadata: {
|
|
506
|
+
name: 'mixed-duplicate'
|
|
507
|
+
},
|
|
508
|
+
handler: async () => ({ success: true, passed: true })
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
export const mixedDuplicate = agent.createEval({
|
|
512
|
+
handler: async () => ({ success: true, passed: true })
|
|
513
|
+
});
|
|
514
|
+
`;
|
|
515
|
+
|
|
516
|
+
expect(() => {
|
|
517
|
+
parseEvalMetadata(
|
|
518
|
+
TEST_ROOT_DIR,
|
|
519
|
+
'/test/root/src/agents/test/eval.ts',
|
|
520
|
+
code,
|
|
521
|
+
TEST_PROJECT_ID,
|
|
522
|
+
TEST_DEPLOYMENT_ID
|
|
523
|
+
);
|
|
524
|
+
}).toThrow(/Duplicate eval names found in .*eval\.ts: mixed-duplicate/);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('error message includes filename and all duplicate names', () => {
|
|
528
|
+
const code = `
|
|
529
|
+
import agent from './agent';
|
|
530
|
+
|
|
531
|
+
export const eval1 = agent.createEval({
|
|
532
|
+
metadata: { name: 'test-eval' },
|
|
533
|
+
handler: async () => ({ success: true, passed: true })
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
export const eval2 = agent.createEval({
|
|
537
|
+
metadata: { name: 'test-eval' },
|
|
538
|
+
handler: async () => ({ success: true, passed: true })
|
|
539
|
+
});
|
|
540
|
+
`;
|
|
541
|
+
|
|
542
|
+
let errorThrown = false;
|
|
543
|
+
try {
|
|
544
|
+
parseEvalMetadata(
|
|
545
|
+
TEST_ROOT_DIR,
|
|
546
|
+
'/test/root/src/agents/test/eval.ts',
|
|
547
|
+
code,
|
|
548
|
+
TEST_PROJECT_ID,
|
|
549
|
+
TEST_DEPLOYMENT_ID
|
|
550
|
+
);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
errorThrown = true;
|
|
553
|
+
expect(error).toBeInstanceOf(Error);
|
|
554
|
+
const errorMessage = (error as Error).message;
|
|
555
|
+
expect(errorMessage).toContain('Duplicate eval names found in');
|
|
556
|
+
expect(errorMessage).toContain('eval.ts');
|
|
557
|
+
expect(errorMessage).toContain('test-eval');
|
|
558
|
+
expect(errorMessage).toContain(
|
|
559
|
+
'Eval names must be unique within the same file to prevent ID collisions'
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
expect(errorThrown).toBe(true);
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
});
|