@agents-at-scale/ark 0.1.40 → 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/arkServices.js +12 -0
- 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 +82 -16
- 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 +48 -10
- 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/dist/ui/TargetSelector.d.ts +19 -0
- package/dist/ui/TargetSelector.js +48 -0
- package/package.json +2 -1
- package/dist/ui/AgentSelector.d.ts +0 -8
- package/dist/ui/AgentSelector.js +0 -53
- package/dist/ui/ModelSelector.d.ts +0 -8
- package/dist/ui/ModelSelector.js +0 -53
- package/dist/ui/TeamSelector.d.ts +0 -8
- package/dist/ui/TeamSelector.js +0 -55
- package/dist/ui/ToolSelector.d.ts +0 -8
- package/dist/ui/ToolSelector.js +0 -53
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
const mockInquirer = {
|
|
3
|
+
prompt: jest.fn(),
|
|
4
|
+
};
|
|
5
|
+
jest.unstable_mockModule('inquirer', () => ({
|
|
6
|
+
default: mockInquirer,
|
|
7
|
+
}));
|
|
8
|
+
const { AzureConfigCollector } = await import('./azure.js');
|
|
9
|
+
describe('AzureConfigCollector', () => {
|
|
10
|
+
let collector;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
collector = new AzureConfigCollector();
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
describe('collectConfig', () => {
|
|
16
|
+
it('uses provided options without prompting', async () => {
|
|
17
|
+
const options = {
|
|
18
|
+
model: 'gpt-4o-mini',
|
|
19
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
20
|
+
apiKey: 'azure-key-12345',
|
|
21
|
+
apiVersion: '2024-12-01-preview',
|
|
22
|
+
};
|
|
23
|
+
const config = await collector.collectConfig(options);
|
|
24
|
+
expect(mockInquirer.prompt).not.toHaveBeenCalled();
|
|
25
|
+
expect(config).toEqual({
|
|
26
|
+
type: 'azure',
|
|
27
|
+
modelValue: 'gpt-4o-mini',
|
|
28
|
+
secretName: '',
|
|
29
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
30
|
+
apiKey: 'azure-key-12345',
|
|
31
|
+
apiVersion: '2024-12-01-preview',
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it('uses default apiVersion when not provided', async () => {
|
|
35
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
36
|
+
apiVersion: '2024-12-01-preview',
|
|
37
|
+
});
|
|
38
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiKey: 'azure-key' });
|
|
39
|
+
const options = {
|
|
40
|
+
model: 'gpt-4',
|
|
41
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
42
|
+
};
|
|
43
|
+
const config = await collector.collectConfig(options);
|
|
44
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
45
|
+
expect.objectContaining({
|
|
46
|
+
type: 'input',
|
|
47
|
+
name: 'apiVersion',
|
|
48
|
+
message: 'Azure API version:',
|
|
49
|
+
default: '2024-12-01-preview',
|
|
50
|
+
}),
|
|
51
|
+
]);
|
|
52
|
+
expect(config.apiVersion).toBe('2024-12-01-preview');
|
|
53
|
+
});
|
|
54
|
+
it('prompts for missing baseUrl', async () => {
|
|
55
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
56
|
+
baseUrl: 'https://contoso.openai.azure.com',
|
|
57
|
+
});
|
|
58
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiVersion: '2024-10-01' });
|
|
59
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiKey: 'azure-key' });
|
|
60
|
+
const options = {
|
|
61
|
+
model: 'gpt-4',
|
|
62
|
+
};
|
|
63
|
+
const config = await collector.collectConfig(options);
|
|
64
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
type: 'input',
|
|
67
|
+
name: 'baseUrl',
|
|
68
|
+
message: 'base URL:',
|
|
69
|
+
validate: expect.any(Function),
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
72
|
+
expect(config.baseUrl).toBe('https://contoso.openai.azure.com');
|
|
73
|
+
});
|
|
74
|
+
it('validates baseUrl is required', async () => {
|
|
75
|
+
mockInquirer.prompt.mockResolvedValueOnce({ baseUrl: '' });
|
|
76
|
+
const options = {
|
|
77
|
+
model: 'gpt-4',
|
|
78
|
+
};
|
|
79
|
+
await expect(collector.collectConfig(options)).rejects.toThrow('base URL is required');
|
|
80
|
+
});
|
|
81
|
+
it('validates baseUrl is a valid URL', async () => {
|
|
82
|
+
const options = {
|
|
83
|
+
model: 'gpt-4',
|
|
84
|
+
};
|
|
85
|
+
// Get the validate function from the prompt call
|
|
86
|
+
mockInquirer.prompt.mockImplementationOnce(async (questions) => {
|
|
87
|
+
const validate = questions[0].validate;
|
|
88
|
+
// Test invalid URL
|
|
89
|
+
expect(validate('not-a-url')).toBe('please enter a valid URL');
|
|
90
|
+
// Test empty string
|
|
91
|
+
expect(validate('')).toBe('base URL is required');
|
|
92
|
+
// Test valid URL
|
|
93
|
+
expect(validate('https://test.openai.azure.com')).toBe(true);
|
|
94
|
+
return { baseUrl: 'https://test.openai.azure.com' };
|
|
95
|
+
});
|
|
96
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiVersion: '2024-12-01-preview' });
|
|
97
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiKey: 'azure-key' });
|
|
98
|
+
await collector.collectConfig(options);
|
|
99
|
+
});
|
|
100
|
+
it('removes trailing slash from baseUrl', async () => {
|
|
101
|
+
const options = {
|
|
102
|
+
model: 'gpt-4',
|
|
103
|
+
baseUrl: 'https://my-resource.openai.azure.com/',
|
|
104
|
+
apiKey: 'azure-key',
|
|
105
|
+
apiVersion: '2024-12-01-preview',
|
|
106
|
+
};
|
|
107
|
+
const config = await collector.collectConfig(options);
|
|
108
|
+
expect(config.baseUrl).toBe('https://my-resource.openai.azure.com');
|
|
109
|
+
});
|
|
110
|
+
it('prompts for missing apiKey as password field', async () => {
|
|
111
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
112
|
+
apiKey: 'azure-secret-key',
|
|
113
|
+
});
|
|
114
|
+
const options = {
|
|
115
|
+
model: 'gpt-4',
|
|
116
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
117
|
+
apiVersion: '2024-12-01-preview',
|
|
118
|
+
};
|
|
119
|
+
const config = await collector.collectConfig(options);
|
|
120
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
121
|
+
expect.objectContaining({
|
|
122
|
+
type: 'password',
|
|
123
|
+
name: 'apiKey',
|
|
124
|
+
message: 'API key:',
|
|
125
|
+
mask: '*',
|
|
126
|
+
validate: expect.any(Function),
|
|
127
|
+
}),
|
|
128
|
+
]);
|
|
129
|
+
expect(config.apiKey).toBe('azure-secret-key');
|
|
130
|
+
});
|
|
131
|
+
it('validates apiKey is required', async () => {
|
|
132
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiKey: '' });
|
|
133
|
+
const options = {
|
|
134
|
+
model: 'gpt-4',
|
|
135
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
136
|
+
apiVersion: '2024-12-01-preview',
|
|
137
|
+
};
|
|
138
|
+
await expect(collector.collectConfig(options)).rejects.toThrow('API key is required');
|
|
139
|
+
});
|
|
140
|
+
it('tests apiKey validation function', async () => {
|
|
141
|
+
const options = {
|
|
142
|
+
model: 'gpt-4',
|
|
143
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
144
|
+
apiVersion: '2024-12-01-preview',
|
|
145
|
+
};
|
|
146
|
+
// Get the validate function from the prompt call
|
|
147
|
+
mockInquirer.prompt.mockImplementationOnce(async (questions) => {
|
|
148
|
+
const validate = questions[0].validate;
|
|
149
|
+
// Test empty string
|
|
150
|
+
expect(validate('')).toBe('API key is required');
|
|
151
|
+
// Test valid key
|
|
152
|
+
expect(validate('valid-azure-key')).toBe(true);
|
|
153
|
+
return { apiKey: 'valid-azure-key' };
|
|
154
|
+
});
|
|
155
|
+
await collector.collectConfig(options);
|
|
156
|
+
});
|
|
157
|
+
it('collects full configuration through interactive prompts', async () => {
|
|
158
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
159
|
+
baseUrl: 'https://eastus.openai.azure.com/',
|
|
160
|
+
});
|
|
161
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
162
|
+
apiVersion: '2024-08-01-preview',
|
|
163
|
+
});
|
|
164
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
165
|
+
apiKey: 'abc123def456',
|
|
166
|
+
});
|
|
167
|
+
const options = {
|
|
168
|
+
model: 'gpt-4o',
|
|
169
|
+
};
|
|
170
|
+
const config = await collector.collectConfig(options);
|
|
171
|
+
expect(config).toEqual({
|
|
172
|
+
type: 'azure',
|
|
173
|
+
modelValue: 'gpt-4o',
|
|
174
|
+
secretName: '',
|
|
175
|
+
baseUrl: 'https://eastus.openai.azure.com',
|
|
176
|
+
apiKey: 'abc123def456',
|
|
177
|
+
apiVersion: '2024-08-01-preview',
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
it('mixes CLI options and interactive prompts', async () => {
|
|
181
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
182
|
+
apiKey: 'prompted-key',
|
|
183
|
+
});
|
|
184
|
+
const options = {
|
|
185
|
+
model: 'gpt-35-turbo',
|
|
186
|
+
baseUrl: 'https://westeurope.openai.azure.com',
|
|
187
|
+
apiVersion: '2024-06-01',
|
|
188
|
+
};
|
|
189
|
+
const config = await collector.collectConfig(options);
|
|
190
|
+
expect(config).toEqual({
|
|
191
|
+
type: 'azure',
|
|
192
|
+
modelValue: 'gpt-35-turbo',
|
|
193
|
+
secretName: '',
|
|
194
|
+
baseUrl: 'https://westeurope.openai.azure.com',
|
|
195
|
+
apiKey: 'prompted-key',
|
|
196
|
+
apiVersion: '2024-06-01',
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
it('accepts custom apiVersion', async () => {
|
|
200
|
+
const options = {
|
|
201
|
+
model: 'gpt-4',
|
|
202
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
203
|
+
apiKey: 'azure-key',
|
|
204
|
+
apiVersion: '2023-05-15',
|
|
205
|
+
};
|
|
206
|
+
const config = await collector.collectConfig(options);
|
|
207
|
+
expect(config.apiVersion).toBe('2023-05-15');
|
|
208
|
+
});
|
|
209
|
+
it('prompts for apiVersion when only baseUrl is provided', async () => {
|
|
210
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
211
|
+
apiVersion: '2024-12-01-preview',
|
|
212
|
+
});
|
|
213
|
+
mockInquirer.prompt.mockResolvedValueOnce({ apiKey: 'azure-key' });
|
|
214
|
+
const options = {
|
|
215
|
+
model: 'gpt-4',
|
|
216
|
+
baseUrl: 'https://my-resource.openai.azure.com',
|
|
217
|
+
};
|
|
218
|
+
const config = await collector.collectConfig(options);
|
|
219
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
220
|
+
expect.objectContaining({
|
|
221
|
+
type: 'input',
|
|
222
|
+
name: 'apiVersion',
|
|
223
|
+
message: 'Azure API version:',
|
|
224
|
+
default: '2024-12-01-preview',
|
|
225
|
+
}),
|
|
226
|
+
]);
|
|
227
|
+
expect(config.apiVersion).toBe('2024-12-01-preview');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseProviderConfig, BaseCollectorOptions, ProviderConfigCollector } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for AWS Bedrock models.
|
|
4
|
+
*/
|
|
5
|
+
export interface BedrockConfig extends BaseProviderConfig {
|
|
6
|
+
type: 'bedrock';
|
|
7
|
+
region: string;
|
|
8
|
+
accessKeyId: string;
|
|
9
|
+
secretAccessKey: string;
|
|
10
|
+
sessionToken?: string;
|
|
11
|
+
modelArn?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Options specific to Bedrock collector.
|
|
15
|
+
*/
|
|
16
|
+
export interface BedrockCollectorOptions extends BaseCollectorOptions {
|
|
17
|
+
region?: string;
|
|
18
|
+
accessKeyId?: string;
|
|
19
|
+
secretAccessKey?: string;
|
|
20
|
+
sessionToken?: string;
|
|
21
|
+
modelArn?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Configuration collector for AWS Bedrock models.
|
|
25
|
+
*
|
|
26
|
+
* Collects the necessary configuration to connect to AWS Bedrock:
|
|
27
|
+
* - region: The AWS region where Bedrock is deployed (e.g., us-east-1)
|
|
28
|
+
* - accessKeyId: AWS access key ID for authentication
|
|
29
|
+
* - secretAccessKey: AWS secret access key for authentication
|
|
30
|
+
* - sessionToken: (Optional) AWS session token for temporary credentials
|
|
31
|
+
* - modelArn: (Optional) Specific ARN for the model to use
|
|
32
|
+
*
|
|
33
|
+
* Values can be provided via command-line options or will be prompted interactively.
|
|
34
|
+
*/
|
|
35
|
+
export declare class BedrockConfigCollector implements ProviderConfigCollector {
|
|
36
|
+
collectConfig(options: BaseCollectorOptions): Promise<BedrockConfig>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration collector for AWS Bedrock models.
|
|
4
|
+
*
|
|
5
|
+
* Collects the necessary configuration to connect to AWS Bedrock:
|
|
6
|
+
* - region: The AWS region where Bedrock is deployed (e.g., us-east-1)
|
|
7
|
+
* - accessKeyId: AWS access key ID for authentication
|
|
8
|
+
* - secretAccessKey: AWS secret access key for authentication
|
|
9
|
+
* - sessionToken: (Optional) AWS session token for temporary credentials
|
|
10
|
+
* - modelArn: (Optional) Specific ARN for the model to use
|
|
11
|
+
*
|
|
12
|
+
* Values can be provided via command-line options or will be prompted interactively.
|
|
13
|
+
*/
|
|
14
|
+
export class BedrockConfigCollector {
|
|
15
|
+
async collectConfig(options) {
|
|
16
|
+
const bedrockOptions = options;
|
|
17
|
+
let region = bedrockOptions.region;
|
|
18
|
+
if (!region) {
|
|
19
|
+
const answer = await inquirer.prompt([
|
|
20
|
+
{
|
|
21
|
+
type: 'input',
|
|
22
|
+
name: 'region',
|
|
23
|
+
message: 'AWS region:',
|
|
24
|
+
default: 'us-east-1',
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
region = answer.region;
|
|
28
|
+
}
|
|
29
|
+
if (!region) {
|
|
30
|
+
throw new Error('region is required');
|
|
31
|
+
}
|
|
32
|
+
let accessKeyId = bedrockOptions.accessKeyId;
|
|
33
|
+
if (!accessKeyId) {
|
|
34
|
+
const answer = await inquirer.prompt([
|
|
35
|
+
{
|
|
36
|
+
type: 'input',
|
|
37
|
+
name: 'accessKeyId',
|
|
38
|
+
message: 'AWS access key ID:',
|
|
39
|
+
validate: (input) => {
|
|
40
|
+
if (!input)
|
|
41
|
+
return 'access key ID is required';
|
|
42
|
+
return true;
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
accessKeyId = answer.accessKeyId;
|
|
47
|
+
}
|
|
48
|
+
if (!accessKeyId) {
|
|
49
|
+
throw new Error('access key ID is required');
|
|
50
|
+
}
|
|
51
|
+
let secretAccessKey = bedrockOptions.secretAccessKey;
|
|
52
|
+
if (!secretAccessKey) {
|
|
53
|
+
const answer = await inquirer.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: 'password',
|
|
56
|
+
name: 'secretAccessKey',
|
|
57
|
+
message: 'AWS secret access key:',
|
|
58
|
+
mask: '*',
|
|
59
|
+
validate: (input) => {
|
|
60
|
+
if (!input)
|
|
61
|
+
return 'secret access key is required';
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
secretAccessKey = answer.secretAccessKey;
|
|
67
|
+
}
|
|
68
|
+
if (!secretAccessKey) {
|
|
69
|
+
throw new Error('secret access key is required');
|
|
70
|
+
}
|
|
71
|
+
let sessionToken = bedrockOptions.sessionToken;
|
|
72
|
+
if (!sessionToken) {
|
|
73
|
+
const answer = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'password',
|
|
76
|
+
name: 'sessionToken',
|
|
77
|
+
message: 'AWS session token (optional, press enter to skip):',
|
|
78
|
+
mask: '*',
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
sessionToken = answer.sessionToken;
|
|
82
|
+
}
|
|
83
|
+
let modelArn = bedrockOptions.modelArn;
|
|
84
|
+
if (!modelArn) {
|
|
85
|
+
const answer = await inquirer.prompt([
|
|
86
|
+
{
|
|
87
|
+
type: 'input',
|
|
88
|
+
name: 'modelArn',
|
|
89
|
+
message: 'Model ARN (optional, press enter to skip):',
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
modelArn = answer.modelArn;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
type: 'bedrock',
|
|
96
|
+
modelValue: options.model,
|
|
97
|
+
secretName: '',
|
|
98
|
+
region,
|
|
99
|
+
accessKeyId,
|
|
100
|
+
secretAccessKey,
|
|
101
|
+
sessionToken: sessionToken || undefined,
|
|
102
|
+
modelArn: modelArn || undefined,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
const mockInquirer = {
|
|
3
|
+
prompt: jest.fn(),
|
|
4
|
+
};
|
|
5
|
+
jest.unstable_mockModule('inquirer', () => ({
|
|
6
|
+
default: mockInquirer,
|
|
7
|
+
}));
|
|
8
|
+
const { BedrockConfigCollector } = await import('./bedrock.js');
|
|
9
|
+
describe('BedrockConfigCollector', () => {
|
|
10
|
+
let collector;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
collector = new BedrockConfigCollector();
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
describe('collectConfig', () => {
|
|
16
|
+
it('uses provided options without prompting', async () => {
|
|
17
|
+
const options = {
|
|
18
|
+
model: 'anthropic.claude-3-sonnet-20240229-v1:0',
|
|
19
|
+
region: 'us-west-2',
|
|
20
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
21
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
22
|
+
sessionToken: 'session-token-123',
|
|
23
|
+
modelArn: 'arn:aws:bedrock:us-west-2:123456789012:model/test',
|
|
24
|
+
};
|
|
25
|
+
const config = await collector.collectConfig(options);
|
|
26
|
+
expect(mockInquirer.prompt).not.toHaveBeenCalled();
|
|
27
|
+
expect(config).toEqual({
|
|
28
|
+
type: 'bedrock',
|
|
29
|
+
modelValue: 'anthropic.claude-3-sonnet-20240229-v1:0',
|
|
30
|
+
secretName: '',
|
|
31
|
+
region: 'us-west-2',
|
|
32
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
33
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
34
|
+
sessionToken: 'session-token-123',
|
|
35
|
+
modelArn: 'arn:aws:bedrock:us-west-2:123456789012:model/test',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('prompts for missing region with default', async () => {
|
|
39
|
+
mockInquirer.prompt.mockResolvedValueOnce({ region: 'us-east-1' });
|
|
40
|
+
mockInquirer.prompt.mockResolvedValueOnce({ accessKeyId: 'AKIATEST' });
|
|
41
|
+
mockInquirer.prompt.mockResolvedValueOnce({ secretAccessKey: 'secret' });
|
|
42
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
43
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
44
|
+
const options = {
|
|
45
|
+
model: 'test-model',
|
|
46
|
+
};
|
|
47
|
+
const config = await collector.collectConfig(options);
|
|
48
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
49
|
+
expect.objectContaining({
|
|
50
|
+
type: 'input',
|
|
51
|
+
name: 'region',
|
|
52
|
+
message: 'AWS region:',
|
|
53
|
+
default: 'us-east-1',
|
|
54
|
+
}),
|
|
55
|
+
]);
|
|
56
|
+
expect(config.region).toBe('us-east-1');
|
|
57
|
+
});
|
|
58
|
+
it('throws error if region is missing after prompt', async () => {
|
|
59
|
+
mockInquirer.prompt.mockResolvedValueOnce({ region: '' });
|
|
60
|
+
const options = {
|
|
61
|
+
model: 'test-model',
|
|
62
|
+
};
|
|
63
|
+
await expect(collector.collectConfig(options)).rejects.toThrow('region is required');
|
|
64
|
+
});
|
|
65
|
+
it('prompts for missing accessKeyId', async () => {
|
|
66
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
67
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
68
|
+
});
|
|
69
|
+
mockInquirer.prompt.mockResolvedValueOnce({ secretAccessKey: 'secret' });
|
|
70
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
71
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
72
|
+
const options = {
|
|
73
|
+
model: 'test-model',
|
|
74
|
+
region: 'us-west-2',
|
|
75
|
+
};
|
|
76
|
+
const config = await collector.collectConfig(options);
|
|
77
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
type: 'input',
|
|
80
|
+
name: 'accessKeyId',
|
|
81
|
+
message: 'AWS access key ID:',
|
|
82
|
+
validate: expect.any(Function),
|
|
83
|
+
}),
|
|
84
|
+
]);
|
|
85
|
+
expect(config.accessKeyId).toBe('AKIAIOSFODNN7EXAMPLE');
|
|
86
|
+
});
|
|
87
|
+
it('validates accessKeyId is required', async () => {
|
|
88
|
+
mockInquirer.prompt.mockResolvedValueOnce({ accessKeyId: '' });
|
|
89
|
+
const options = {
|
|
90
|
+
model: 'test-model',
|
|
91
|
+
region: 'us-west-2',
|
|
92
|
+
};
|
|
93
|
+
await expect(collector.collectConfig(options)).rejects.toThrow('access key ID is required');
|
|
94
|
+
});
|
|
95
|
+
it('prompts for missing secretAccessKey as password field', async () => {
|
|
96
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
97
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
98
|
+
});
|
|
99
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
100
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
101
|
+
const options = {
|
|
102
|
+
model: 'test-model',
|
|
103
|
+
region: 'us-west-2',
|
|
104
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
105
|
+
};
|
|
106
|
+
const config = await collector.collectConfig(options);
|
|
107
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
108
|
+
expect.objectContaining({
|
|
109
|
+
type: 'password',
|
|
110
|
+
name: 'secretAccessKey',
|
|
111
|
+
message: 'AWS secret access key:',
|
|
112
|
+
mask: '*',
|
|
113
|
+
validate: expect.any(Function),
|
|
114
|
+
}),
|
|
115
|
+
]);
|
|
116
|
+
expect(config.secretAccessKey).toBe('wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
|
|
117
|
+
});
|
|
118
|
+
it('validates secretAccessKey is required', async () => {
|
|
119
|
+
mockInquirer.prompt.mockResolvedValueOnce({ secretAccessKey: '' });
|
|
120
|
+
const options = {
|
|
121
|
+
model: 'test-model',
|
|
122
|
+
region: 'us-west-2',
|
|
123
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
124
|
+
};
|
|
125
|
+
await expect(collector.collectConfig(options)).rejects.toThrow('secret access key is required');
|
|
126
|
+
});
|
|
127
|
+
it('prompts for optional sessionToken', async () => {
|
|
128
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
129
|
+
sessionToken: 'optional-token',
|
|
130
|
+
});
|
|
131
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
132
|
+
const options = {
|
|
133
|
+
model: 'test-model',
|
|
134
|
+
region: 'us-west-2',
|
|
135
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
136
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
137
|
+
};
|
|
138
|
+
const config = await collector.collectConfig(options);
|
|
139
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
140
|
+
expect.objectContaining({
|
|
141
|
+
type: 'password',
|
|
142
|
+
name: 'sessionToken',
|
|
143
|
+
message: 'AWS session token (optional, press enter to skip):',
|
|
144
|
+
mask: '*',
|
|
145
|
+
}),
|
|
146
|
+
]);
|
|
147
|
+
expect(config.sessionToken).toBe('optional-token');
|
|
148
|
+
});
|
|
149
|
+
it('sets sessionToken to undefined when empty', async () => {
|
|
150
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
151
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
152
|
+
const options = {
|
|
153
|
+
model: 'test-model',
|
|
154
|
+
region: 'us-west-2',
|
|
155
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
156
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
157
|
+
};
|
|
158
|
+
const config = await collector.collectConfig(options);
|
|
159
|
+
expect(config.sessionToken).toBeUndefined();
|
|
160
|
+
});
|
|
161
|
+
it('prompts for optional modelArn', async () => {
|
|
162
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
163
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
164
|
+
modelArn: 'arn:aws:bedrock:us-west-2:123456789012:model/test',
|
|
165
|
+
});
|
|
166
|
+
const options = {
|
|
167
|
+
model: 'test-model',
|
|
168
|
+
region: 'us-west-2',
|
|
169
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
170
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
171
|
+
};
|
|
172
|
+
const config = await collector.collectConfig(options);
|
|
173
|
+
expect(mockInquirer.prompt).toHaveBeenCalledWith([
|
|
174
|
+
expect.objectContaining({
|
|
175
|
+
type: 'input',
|
|
176
|
+
name: 'modelArn',
|
|
177
|
+
message: 'Model ARN (optional, press enter to skip):',
|
|
178
|
+
}),
|
|
179
|
+
]);
|
|
180
|
+
expect(config.modelArn).toBe('arn:aws:bedrock:us-west-2:123456789012:model/test');
|
|
181
|
+
});
|
|
182
|
+
it('sets modelArn to undefined when empty', async () => {
|
|
183
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
184
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
185
|
+
const options = {
|
|
186
|
+
model: 'test-model',
|
|
187
|
+
region: 'us-west-2',
|
|
188
|
+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
|
189
|
+
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
|
190
|
+
};
|
|
191
|
+
const config = await collector.collectConfig(options);
|
|
192
|
+
expect(config.modelArn).toBeUndefined();
|
|
193
|
+
});
|
|
194
|
+
it('collects full configuration through interactive prompts', async () => {
|
|
195
|
+
mockInquirer.prompt.mockResolvedValueOnce({ region: 'eu-west-1' });
|
|
196
|
+
mockInquirer.prompt.mockResolvedValueOnce({ accessKeyId: 'AKIATEST' });
|
|
197
|
+
mockInquirer.prompt.mockResolvedValueOnce({ secretAccessKey: 'secret123' });
|
|
198
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: 'token456' });
|
|
199
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
200
|
+
modelArn: 'arn:aws:bedrock:eu-west-1:123:model/claude',
|
|
201
|
+
});
|
|
202
|
+
const options = {
|
|
203
|
+
model: 'anthropic.claude-v2',
|
|
204
|
+
};
|
|
205
|
+
const config = await collector.collectConfig(options);
|
|
206
|
+
expect(config).toEqual({
|
|
207
|
+
type: 'bedrock',
|
|
208
|
+
modelValue: 'anthropic.claude-v2',
|
|
209
|
+
secretName: '',
|
|
210
|
+
region: 'eu-west-1',
|
|
211
|
+
accessKeyId: 'AKIATEST',
|
|
212
|
+
secretAccessKey: 'secret123',
|
|
213
|
+
sessionToken: 'token456',
|
|
214
|
+
modelArn: 'arn:aws:bedrock:eu-west-1:123:model/claude',
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
it('mixes CLI options and interactive prompts', async () => {
|
|
218
|
+
mockInquirer.prompt.mockResolvedValueOnce({
|
|
219
|
+
accessKeyId: 'AKIAPROMPTED',
|
|
220
|
+
});
|
|
221
|
+
mockInquirer.prompt.mockResolvedValueOnce({ sessionToken: '' });
|
|
222
|
+
mockInquirer.prompt.mockResolvedValueOnce({ modelArn: '' });
|
|
223
|
+
const options = {
|
|
224
|
+
model: 'test-model',
|
|
225
|
+
region: 'ap-south-1',
|
|
226
|
+
secretAccessKey: 'providedSecret',
|
|
227
|
+
};
|
|
228
|
+
const config = await collector.collectConfig(options);
|
|
229
|
+
expect(config).toEqual({
|
|
230
|
+
type: 'bedrock',
|
|
231
|
+
modelValue: 'test-model',
|
|
232
|
+
secretName: '',
|
|
233
|
+
region: 'ap-south-1',
|
|
234
|
+
accessKeyId: 'AKIAPROMPTED',
|
|
235
|
+
secretAccessKey: 'providedSecret',
|
|
236
|
+
sessionToken: undefined,
|
|
237
|
+
modelArn: undefined,
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProviderConfigCollector } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating provider-specific configuration collectors.
|
|
4
|
+
*
|
|
5
|
+
* This factory uses the provider type to instantiate the appropriate collector
|
|
6
|
+
* that knows how to gather configuration for that specific provider.
|
|
7
|
+
* This pattern makes it easy to add new providers without modifying existing code.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ProviderConfigCollectorFactory {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a configuration collector for the specified provider type.
|
|
12
|
+
*
|
|
13
|
+
* @param type - The provider type ('openai', 'azure', or 'bedrock')
|
|
14
|
+
* @returns A collector instance for the specified provider
|
|
15
|
+
* @throws Error if the provider type is not recognized
|
|
16
|
+
*/
|
|
17
|
+
static create(type: string): ProviderConfigCollector;
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { OpenAIConfigCollector } from './openai.js';
|
|
2
|
+
import { AzureConfigCollector } from './azure.js';
|
|
3
|
+
import { BedrockConfigCollector } from './bedrock.js';
|
|
4
|
+
/**
|
|
5
|
+
* Factory for creating provider-specific configuration collectors.
|
|
6
|
+
*
|
|
7
|
+
* This factory uses the provider type to instantiate the appropriate collector
|
|
8
|
+
* that knows how to gather configuration for that specific provider.
|
|
9
|
+
* This pattern makes it easy to add new providers without modifying existing code.
|
|
10
|
+
*/
|
|
11
|
+
export class ProviderConfigCollectorFactory {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a configuration collector for the specified provider type.
|
|
14
|
+
*
|
|
15
|
+
* @param type - The provider type ('openai', 'azure', or 'bedrock')
|
|
16
|
+
* @returns A collector instance for the specified provider
|
|
17
|
+
* @throws Error if the provider type is not recognized
|
|
18
|
+
*/
|
|
19
|
+
static create(type) {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case 'openai':
|
|
22
|
+
return new OpenAIConfigCollector();
|
|
23
|
+
case 'azure':
|
|
24
|
+
return new AzureConfigCollector();
|
|
25
|
+
case 'bedrock':
|
|
26
|
+
return new BedrockConfigCollector();
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unknown provider type: ${type}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|