@clinebot/shared 0.0.13 → 0.0.14
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/db/index.js +2 -2
- package/dist/remote-config/constants.d.ts +5 -0
- package/dist/remote-config/schema.d.ts +426 -0
- package/package.json +10 -3
- package/src/db/sqlite-db.ts +11 -4
- package/src/remote-config/constants.ts +5 -0
- package/src/remote-config/schema.test.ts +1004 -0
- package/src/remote-config/schema.ts +263 -0
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
AwsBedrockSettingsSchema,
|
|
4
|
+
ClineSettingsSchema,
|
|
5
|
+
EnterpriseTelemetrySchema,
|
|
6
|
+
OpenAiCompatibleSchema,
|
|
7
|
+
PromptUploadingSchema,
|
|
8
|
+
type RemoteConfig,
|
|
9
|
+
RemoteConfigSchema,
|
|
10
|
+
S3AccessKeySettingsSchema,
|
|
11
|
+
} from "./schema";
|
|
12
|
+
|
|
13
|
+
describe("Remote Config Schema", () => {
|
|
14
|
+
describe("EnterpriseTelemetry", () => {
|
|
15
|
+
it("accepts an empty object", () => {
|
|
16
|
+
const result = EnterpriseTelemetrySchema.parse({});
|
|
17
|
+
|
|
18
|
+
expect(result).to.deep.equal({});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("accepts an empty prompt uploading object", () => {
|
|
22
|
+
const result = EnterpriseTelemetrySchema.parse({
|
|
23
|
+
promptUploading: {},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
expect(result.promptUploading).to.deep.equal({});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("S3AccessKeySettingsSchema", () => {
|
|
31
|
+
it("should accept valid S3 access key settings with required fields", () => {
|
|
32
|
+
const validSettings = {
|
|
33
|
+
bucket: "my-bucket",
|
|
34
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
35
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
36
|
+
};
|
|
37
|
+
const result = S3AccessKeySettingsSchema.parse(validSettings);
|
|
38
|
+
expect(result).to.deep.equal(validSettings);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should accept S3 settings with all optional fields", () => {
|
|
42
|
+
const fullSettings = {
|
|
43
|
+
bucket: "my-bucket",
|
|
44
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
45
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
46
|
+
region: "us-east-1",
|
|
47
|
+
endpoint: "https://s3.us-east-1.amazonaws.com",
|
|
48
|
+
accountId: "123456789012",
|
|
49
|
+
intervalMs: 30000,
|
|
50
|
+
maxRetries: 3,
|
|
51
|
+
batchSize: 100,
|
|
52
|
+
maxQueueSize: 1000,
|
|
53
|
+
maxFailedAgeMs: 86400000,
|
|
54
|
+
backfillEnabled: true,
|
|
55
|
+
};
|
|
56
|
+
const result = S3AccessKeySettingsSchema.parse(fullSettings);
|
|
57
|
+
expect(result).to.deep.equal(fullSettings);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should accept S3 settings with queue configuration fields", () => {
|
|
61
|
+
const settings = {
|
|
62
|
+
bucket: "my-bucket",
|
|
63
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
64
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
65
|
+
intervalMs: 60000,
|
|
66
|
+
maxRetries: 5,
|
|
67
|
+
batchSize: 50,
|
|
68
|
+
maxQueueSize: 500,
|
|
69
|
+
maxFailedAgeMs: 172800000,
|
|
70
|
+
backfillEnabled: false,
|
|
71
|
+
};
|
|
72
|
+
const result = S3AccessKeySettingsSchema.parse(settings);
|
|
73
|
+
expect(result.intervalMs).to.equal(60000);
|
|
74
|
+
expect(result.maxRetries).to.equal(5);
|
|
75
|
+
expect(result.batchSize).to.equal(50);
|
|
76
|
+
expect(result.maxQueueSize).to.equal(500);
|
|
77
|
+
expect(result.maxFailedAgeMs).to.equal(172800000);
|
|
78
|
+
expect(result.backfillEnabled).to.equal(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should reject S3 settings with invalid intervalMs type", () => {
|
|
82
|
+
expect(() =>
|
|
83
|
+
S3AccessKeySettingsSchema.parse({
|
|
84
|
+
bucket: "my-bucket",
|
|
85
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
86
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
87
|
+
intervalMs: "30000",
|
|
88
|
+
}),
|
|
89
|
+
).to.throw();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should reject S3 settings with invalid maxRetries type", () => {
|
|
93
|
+
expect(() =>
|
|
94
|
+
S3AccessKeySettingsSchema.parse({
|
|
95
|
+
bucket: "my-bucket",
|
|
96
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
97
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
98
|
+
maxRetries: "3",
|
|
99
|
+
}),
|
|
100
|
+
).to.throw();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should reject S3 settings with invalid backfillEnabled type", () => {
|
|
104
|
+
expect(() =>
|
|
105
|
+
S3AccessKeySettingsSchema.parse({
|
|
106
|
+
bucket: "my-bucket",
|
|
107
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
108
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
109
|
+
backfillEnabled: "true",
|
|
110
|
+
}),
|
|
111
|
+
).to.throw();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should reject S3 settings with missing bucket", () => {
|
|
115
|
+
expect(() =>
|
|
116
|
+
S3AccessKeySettingsSchema.parse({
|
|
117
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
118
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
119
|
+
}),
|
|
120
|
+
).to.throw();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should reject S3 settings with missing accessKeyId", () => {
|
|
124
|
+
expect(() =>
|
|
125
|
+
S3AccessKeySettingsSchema.parse({
|
|
126
|
+
bucket: "my-bucket",
|
|
127
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
128
|
+
}),
|
|
129
|
+
).to.throw();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should reject S3 settings with missing secretAccessKey", () => {
|
|
133
|
+
expect(() =>
|
|
134
|
+
S3AccessKeySettingsSchema.parse({
|
|
135
|
+
bucket: "my-bucket",
|
|
136
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
137
|
+
}),
|
|
138
|
+
).to.throw();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("PromptUploadingSchema", () => {
|
|
143
|
+
it("should accept an empty object", () => {
|
|
144
|
+
const result = PromptUploadingSchema.parse({});
|
|
145
|
+
expect(result).to.deep.equal({});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should accept enabled field only", () => {
|
|
149
|
+
const result = PromptUploadingSchema.parse({ enabled: true });
|
|
150
|
+
expect(result.enabled).to.equal(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should accept enabled as false", () => {
|
|
154
|
+
const result = PromptUploadingSchema.parse({ enabled: false });
|
|
155
|
+
expect(result.enabled).to.equal(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should accept type field with s3_access_keys", () => {
|
|
159
|
+
const result = PromptUploadingSchema.parse({ type: "s3_access_keys" });
|
|
160
|
+
expect(result.type).to.equal("s3_access_keys");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("should reject invalid type values", () => {
|
|
164
|
+
expect(() =>
|
|
165
|
+
PromptUploadingSchema.parse({ type: "invalid_type" }),
|
|
166
|
+
).to.throw();
|
|
167
|
+
expect(() => PromptUploadingSchema.parse({ type: "s3" })).to.throw();
|
|
168
|
+
expect(() => PromptUploadingSchema.parse({ type: "" })).to.throw();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should accept complete prompt uploading configuration", () => {
|
|
172
|
+
const fullConfig = {
|
|
173
|
+
enabled: true,
|
|
174
|
+
type: "s3_access_keys" as const,
|
|
175
|
+
s3AccessSettings: {
|
|
176
|
+
bucket: "prompt-uploads-bucket",
|
|
177
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
178
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
179
|
+
region: "us-west-2",
|
|
180
|
+
endpoint: "https://s3.us-west-2.amazonaws.com",
|
|
181
|
+
accountId: "123456789012",
|
|
182
|
+
intervalMs: 30000,
|
|
183
|
+
maxRetries: 3,
|
|
184
|
+
batchSize: 100,
|
|
185
|
+
maxQueueSize: 1000,
|
|
186
|
+
maxFailedAgeMs: 86400000,
|
|
187
|
+
backfillEnabled: true,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
const result = PromptUploadingSchema.parse(fullConfig);
|
|
191
|
+
expect(result).to.deep.equal(fullConfig);
|
|
192
|
+
expect(result.enabled).to.equal(true);
|
|
193
|
+
expect(result.type).to.equal("s3_access_keys");
|
|
194
|
+
expect(result.s3AccessSettings?.bucket).to.equal("prompt-uploads-bucket");
|
|
195
|
+
expect(result.s3AccessSettings?.region).to.equal("us-west-2");
|
|
196
|
+
expect(result.s3AccessSettings?.intervalMs).to.equal(30000);
|
|
197
|
+
expect(result.s3AccessSettings?.maxRetries).to.equal(3);
|
|
198
|
+
expect(result.s3AccessSettings?.batchSize).to.equal(100);
|
|
199
|
+
expect(result.s3AccessSettings?.maxQueueSize).to.equal(1000);
|
|
200
|
+
expect(result.s3AccessSettings?.maxFailedAgeMs).to.equal(86400000);
|
|
201
|
+
expect(result.s3AccessSettings?.backfillEnabled).to.equal(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should accept s3AccessSettings without optional fields", () => {
|
|
205
|
+
const config = {
|
|
206
|
+
enabled: true,
|
|
207
|
+
type: "s3_access_keys" as const,
|
|
208
|
+
s3AccessSettings: {
|
|
209
|
+
bucket: "my-bucket",
|
|
210
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
211
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
const result = PromptUploadingSchema.parse(config);
|
|
215
|
+
expect(result.s3AccessSettings?.bucket).to.equal("my-bucket");
|
|
216
|
+
expect(result.s3AccessSettings?.region).to.be.undefined;
|
|
217
|
+
expect(result.s3AccessSettings?.endpoint).to.be.undefined;
|
|
218
|
+
expect(result.s3AccessSettings?.accountId).to.be.undefined;
|
|
219
|
+
expect(result.s3AccessSettings?.intervalMs).to.be.undefined;
|
|
220
|
+
expect(result.s3AccessSettings?.maxRetries).to.be.undefined;
|
|
221
|
+
expect(result.s3AccessSettings?.batchSize).to.be.undefined;
|
|
222
|
+
expect(result.s3AccessSettings?.maxQueueSize).to.be.undefined;
|
|
223
|
+
expect(result.s3AccessSettings?.maxFailedAgeMs).to.be.undefined;
|
|
224
|
+
expect(result.s3AccessSettings?.backfillEnabled).to.be.undefined;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("should reject s3AccessSettings with missing required fields", () => {
|
|
228
|
+
expect(() =>
|
|
229
|
+
PromptUploadingSchema.parse({
|
|
230
|
+
enabled: true,
|
|
231
|
+
s3AccessSettings: {
|
|
232
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
233
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
).to.throw();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should reject invalid enabled type", () => {
|
|
240
|
+
expect(() => PromptUploadingSchema.parse({ enabled: "yes" })).to.throw();
|
|
241
|
+
expect(() => PromptUploadingSchema.parse({ enabled: 1 })).to.throw();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("OpenAiCompatibleSchema", () => {
|
|
246
|
+
it("should accept valid OpenAI compatible settings", () => {
|
|
247
|
+
const validSettings = {
|
|
248
|
+
models: [
|
|
249
|
+
{
|
|
250
|
+
id: "gpt-4",
|
|
251
|
+
temperature: 0.7,
|
|
252
|
+
isR1FormatRequired: true,
|
|
253
|
+
maxTokens: 4096,
|
|
254
|
+
contextWindow: 128000,
|
|
255
|
+
inputPrice: 0.03,
|
|
256
|
+
outputPrice: 0.06,
|
|
257
|
+
supportsImages: true,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: "gpt-3.5-turbo",
|
|
261
|
+
temperature: 0.7,
|
|
262
|
+
maxTokens: 4096,
|
|
263
|
+
contextWindow: 16000,
|
|
264
|
+
inputPrice: 0.001,
|
|
265
|
+
outputPrice: 0.002,
|
|
266
|
+
supportsImages: false,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
openAiBaseUrl: "https://api.openai.com/v1",
|
|
270
|
+
openAiHeaders: { "X-Custom-Header": "value" },
|
|
271
|
+
azureApiVersion: "2024-02-15-preview",
|
|
272
|
+
};
|
|
273
|
+
const result = OpenAiCompatibleSchema.parse(validSettings);
|
|
274
|
+
expect(result).to.deep.equal(validSettings);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("should have undefined for models and openAiHeaders by default", () => {
|
|
278
|
+
const result = OpenAiCompatibleSchema.parse({});
|
|
279
|
+
expect(result.models).to.be.undefined;
|
|
280
|
+
expect(result.openAiHeaders).to.be.undefined;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("should reject invalid field types", () => {
|
|
284
|
+
expect(() =>
|
|
285
|
+
OpenAiCompatibleSchema.parse({ models: "not-an-array" }),
|
|
286
|
+
).to.throw();
|
|
287
|
+
expect(() =>
|
|
288
|
+
OpenAiCompatibleSchema.parse({ openAiHeaders: "not-an-object" }),
|
|
289
|
+
).to.throw();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("should reject models with missing id field", () => {
|
|
293
|
+
expect(() =>
|
|
294
|
+
OpenAiCompatibleSchema.parse({
|
|
295
|
+
models: [{ temperature: 0.7 }],
|
|
296
|
+
}),
|
|
297
|
+
).to.throw();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should accept models with only id field", () => {
|
|
301
|
+
const settings = {
|
|
302
|
+
models: [{ id: "gpt-4" }],
|
|
303
|
+
};
|
|
304
|
+
expect(() => OpenAiCompatibleSchema.parse(settings)).to.not.throw();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should accept headers as record of strings", () => {
|
|
308
|
+
const settings = {
|
|
309
|
+
openAiHeaders: {
|
|
310
|
+
Authorization: "Bearer token",
|
|
311
|
+
"Content-Type": "application/json",
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
const result = OpenAiCompatibleSchema.parse(settings);
|
|
315
|
+
expect(result.openAiHeaders).to.deep.equal(settings.openAiHeaders);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe("ClineSettingsSchema", () => {
|
|
320
|
+
it("should accept valid Cline provider settings", () => {
|
|
321
|
+
const validSettings = {
|
|
322
|
+
models: [
|
|
323
|
+
{ id: "claude-3-5-sonnet-20241022" },
|
|
324
|
+
{ id: "claude-3-5-haiku-20241022" },
|
|
325
|
+
],
|
|
326
|
+
};
|
|
327
|
+
const result = ClineSettingsSchema.parse(validSettings);
|
|
328
|
+
expect(result).to.deep.equal(validSettings);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should accept empty settings object", () => {
|
|
332
|
+
const result = ClineSettingsSchema.parse({});
|
|
333
|
+
expect(result.models).to.be.undefined;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should accept models with only id field", () => {
|
|
337
|
+
const settings = {
|
|
338
|
+
models: [{ id: "claude-3-5-sonnet-20241022" }],
|
|
339
|
+
};
|
|
340
|
+
expect(() => ClineSettingsSchema.parse(settings)).to.not.throw();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should reject models with missing id field", () => {
|
|
344
|
+
expect(() =>
|
|
345
|
+
ClineSettingsSchema.parse({
|
|
346
|
+
models: [{}],
|
|
347
|
+
}),
|
|
348
|
+
).to.throw();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
describe("AwsBedrockSettingsSchema", () => {
|
|
353
|
+
it("should accept valid AWS Bedrock settings", () => {
|
|
354
|
+
const validSettings = {
|
|
355
|
+
models: [
|
|
356
|
+
{ id: "anthropic.claude-v2", thinkingBudgetTokens: 1600 },
|
|
357
|
+
{ id: "anthropic.claude-instant-v1", thinkingBudgetTokens: 800 },
|
|
358
|
+
],
|
|
359
|
+
customModels: [
|
|
360
|
+
{ name: "my-custom-model", baseModelId: "anthropic.claude-v2" },
|
|
361
|
+
{ name: "another-model", baseModelId: "anthropic.claude-instant-v1" },
|
|
362
|
+
],
|
|
363
|
+
awsRegion: "us-east-1",
|
|
364
|
+
awsUseCrossRegionInference: true,
|
|
365
|
+
awsBedrockUsePromptCache: true,
|
|
366
|
+
awsBedrockEndpoint: "https://bedrock.us-east-1.amazonaws.com",
|
|
367
|
+
};
|
|
368
|
+
const result = AwsBedrockSettingsSchema.parse(validSettings);
|
|
369
|
+
expect(result).to.deep.equal(validSettings);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should accept empty settings object", () => {
|
|
373
|
+
const result = AwsBedrockSettingsSchema.parse({});
|
|
374
|
+
expect(result.models).to.be.undefined;
|
|
375
|
+
expect(result.customModels).to.be.undefined;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("should accept models with only id field", () => {
|
|
379
|
+
const settings = {
|
|
380
|
+
models: [{ id: "anthropic.claude-v2" }],
|
|
381
|
+
};
|
|
382
|
+
expect(() => AwsBedrockSettingsSchema.parse(settings)).to.not.throw();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should accept models with thinkingBudgetTokens", () => {
|
|
386
|
+
const settings = {
|
|
387
|
+
models: [
|
|
388
|
+
{ id: "anthropic.claude-v2", thinkingBudgetTokens: 1600 },
|
|
389
|
+
{ id: "anthropic.claude-instant-v1", thinkingBudgetTokens: 800 },
|
|
390
|
+
],
|
|
391
|
+
};
|
|
392
|
+
const result = AwsBedrockSettingsSchema.parse(settings);
|
|
393
|
+
expect(result.models).to.have.lengthOf(2);
|
|
394
|
+
expect(result.models?.[0].thinkingBudgetTokens).to.equal(1600);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("should accept custom models array", () => {
|
|
398
|
+
const settings = {
|
|
399
|
+
customModels: [
|
|
400
|
+
{
|
|
401
|
+
name: "custom-1",
|
|
402
|
+
baseModelId: "base-model-1",
|
|
403
|
+
thinkingBudgetTokens: 1600,
|
|
404
|
+
},
|
|
405
|
+
{ name: "custom-2", baseModelId: "base-model-2" },
|
|
406
|
+
],
|
|
407
|
+
};
|
|
408
|
+
expect(() => AwsBedrockSettingsSchema.parse(settings)).to.not.throw();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("should reject invalid field types", () => {
|
|
412
|
+
expect(() =>
|
|
413
|
+
AwsBedrockSettingsSchema.parse({ models: "not-an-array" }),
|
|
414
|
+
).to.throw();
|
|
415
|
+
expect(() =>
|
|
416
|
+
AwsBedrockSettingsSchema.parse({ customModels: "not-an-array" }),
|
|
417
|
+
).to.throw();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("should reject models with missing id field", () => {
|
|
421
|
+
expect(() =>
|
|
422
|
+
AwsBedrockSettingsSchema.parse({
|
|
423
|
+
models: [{ thinkingBudgetTokens: 1600 }],
|
|
424
|
+
}),
|
|
425
|
+
).to.throw();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("should reject custom models with missing fields", () => {
|
|
429
|
+
expect(() =>
|
|
430
|
+
AwsBedrockSettingsSchema.parse({
|
|
431
|
+
customModels: [{ name: "missing-base-model" }],
|
|
432
|
+
}),
|
|
433
|
+
).to.throw();
|
|
434
|
+
expect(() =>
|
|
435
|
+
AwsBedrockSettingsSchema.parse({
|
|
436
|
+
customModels: [{ baseModelId: "missing-name" }],
|
|
437
|
+
}),
|
|
438
|
+
).to.throw();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("MCPSettingsSchema", () => {
|
|
443
|
+
it("should reject servers with a missing id", () => {
|
|
444
|
+
const config = {
|
|
445
|
+
version: "v1",
|
|
446
|
+
allowedMCPServers: [{}],
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
expect(() => RemoteConfigSchema.parse(config)).to.throw();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it("should accept valid MCP settings", () => {
|
|
453
|
+
const config = {
|
|
454
|
+
version: "v1",
|
|
455
|
+
mcpMarketplaceEnabled: true,
|
|
456
|
+
allowedMCPServers: [
|
|
457
|
+
{ id: "https://github.com/mcp/filesystem" },
|
|
458
|
+
{ id: "https://github.com/mcp/github" },
|
|
459
|
+
],
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const result = RemoteConfigSchema.parse(config);
|
|
463
|
+
expect(result.mcpMarketplaceEnabled).to.equal(true);
|
|
464
|
+
expect(result.allowedMCPServers).to.deep.equal(config.allowedMCPServers);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
describe("RemoteMCPServersSchema", () => {
|
|
469
|
+
it("should accept remoteMCPServers with alwaysEnabled true", () => {
|
|
470
|
+
const config = {
|
|
471
|
+
version: "v1",
|
|
472
|
+
remoteMCPServers: [
|
|
473
|
+
{
|
|
474
|
+
name: "always-on-server",
|
|
475
|
+
url: "https://example.com/mcp",
|
|
476
|
+
alwaysEnabled: true,
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const result = RemoteConfigSchema.parse(config);
|
|
482
|
+
expect(result.remoteMCPServers).to.have.lengthOf(1);
|
|
483
|
+
expect(result.remoteMCPServers?.[0].name).to.equal("always-on-server");
|
|
484
|
+
expect(result.remoteMCPServers?.[0].url).to.equal(
|
|
485
|
+
"https://example.com/mcp",
|
|
486
|
+
);
|
|
487
|
+
expect(result.remoteMCPServers?.[0].alwaysEnabled).to.equal(true);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("should accept remoteMCPServers with alwaysEnabled false", () => {
|
|
491
|
+
const config = {
|
|
492
|
+
version: "v1",
|
|
493
|
+
remoteMCPServers: [
|
|
494
|
+
{
|
|
495
|
+
name: "toggle-server",
|
|
496
|
+
url: "https://example.com/mcp",
|
|
497
|
+
alwaysEnabled: false,
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const result = RemoteConfigSchema.parse(config);
|
|
503
|
+
expect(result.remoteMCPServers).to.have.lengthOf(1);
|
|
504
|
+
expect(result.remoteMCPServers?.[0].alwaysEnabled).to.equal(false);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it("should accept remoteMCPServers without alwaysEnabled (defaults to undefined)", () => {
|
|
508
|
+
const config = {
|
|
509
|
+
version: "v1",
|
|
510
|
+
remoteMCPServers: [
|
|
511
|
+
{ name: "default-server", url: "https://example.com/mcp" },
|
|
512
|
+
],
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const result = RemoteConfigSchema.parse(config);
|
|
516
|
+
expect(result.remoteMCPServers).to.have.lengthOf(1);
|
|
517
|
+
expect(result.remoteMCPServers?.[0].name).to.equal("default-server");
|
|
518
|
+
expect(result.remoteMCPServers?.[0].url).to.equal(
|
|
519
|
+
"https://example.com/mcp",
|
|
520
|
+
);
|
|
521
|
+
expect(result.remoteMCPServers?.[0].alwaysEnabled).to.be.undefined;
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it("should accept multiple remoteMCPServers with mixed alwaysEnabled values", () => {
|
|
525
|
+
const config = {
|
|
526
|
+
version: "v1",
|
|
527
|
+
remoteMCPServers: [
|
|
528
|
+
{
|
|
529
|
+
name: "always-on",
|
|
530
|
+
url: "https://example1.com/mcp",
|
|
531
|
+
alwaysEnabled: true,
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "toggle",
|
|
535
|
+
url: "https://example2.com/mcp",
|
|
536
|
+
alwaysEnabled: false,
|
|
537
|
+
},
|
|
538
|
+
{ name: "default", url: "https://example3.com/mcp" },
|
|
539
|
+
],
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const result = RemoteConfigSchema.parse(config);
|
|
543
|
+
expect(result.remoteMCPServers).to.have.lengthOf(3);
|
|
544
|
+
expect(result.remoteMCPServers?.[0].alwaysEnabled).to.equal(true);
|
|
545
|
+
expect(result.remoteMCPServers?.[1].alwaysEnabled).to.equal(false);
|
|
546
|
+
expect(result.remoteMCPServers?.[2].alwaysEnabled).to.be.undefined;
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("should reject remoteMCPServers with missing name", () => {
|
|
550
|
+
const config = {
|
|
551
|
+
version: "v1",
|
|
552
|
+
remoteMCPServers: [
|
|
553
|
+
{ url: "https://example.com/mcp", alwaysEnabled: true },
|
|
554
|
+
],
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
expect(() => RemoteConfigSchema.parse(config)).to.throw();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it("should reject remoteMCPServers with missing url", () => {
|
|
561
|
+
const config = {
|
|
562
|
+
version: "v1",
|
|
563
|
+
remoteMCPServers: [{ name: "test-server", alwaysEnabled: true }],
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
expect(() => RemoteConfigSchema.parse(config)).to.throw();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it("should reject remoteMCPServers with invalid alwaysEnabled type", () => {
|
|
570
|
+
const config = {
|
|
571
|
+
version: "v1",
|
|
572
|
+
remoteMCPServers: [
|
|
573
|
+
{
|
|
574
|
+
name: "test-server",
|
|
575
|
+
url: "https://example.com/mcp",
|
|
576
|
+
alwaysEnabled: "yes",
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
expect(() => RemoteConfigSchema.parse(config)).to.throw();
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("should accept headers", () => {
|
|
585
|
+
const config = {
|
|
586
|
+
version: "v1",
|
|
587
|
+
remoteMCPServers: [
|
|
588
|
+
{
|
|
589
|
+
name: "test-server",
|
|
590
|
+
url: "https://example.com/mcp",
|
|
591
|
+
headers: { Authorization: "test" },
|
|
592
|
+
},
|
|
593
|
+
],
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const result = RemoteConfigSchema.parse(config);
|
|
597
|
+
expect(result.remoteMCPServers).to.have.lengthOf(1);
|
|
598
|
+
expect(result.remoteMCPServers?.[0].headers).to.deep.equal({
|
|
599
|
+
Authorization: "test",
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
describe("RemoteConfigSchema", () => {
|
|
605
|
+
it("should accept valid complete remote config", () => {
|
|
606
|
+
const validConfig: RemoteConfig = {
|
|
607
|
+
version: "v1",
|
|
608
|
+
telemetryEnabled: true,
|
|
609
|
+
kanbanEnabled: true,
|
|
610
|
+
mcpMarketplaceEnabled: true,
|
|
611
|
+
yoloModeAllowed: false,
|
|
612
|
+
providerSettings: {
|
|
613
|
+
OpenAiCompatible: {
|
|
614
|
+
models: [{ id: "gpt-4" }],
|
|
615
|
+
openAiBaseUrl: "https://api.openai.com/v1",
|
|
616
|
+
openAiHeaders: {},
|
|
617
|
+
},
|
|
618
|
+
AwsBedrock: {
|
|
619
|
+
models: [{ id: "anthropic.claude-v2" }],
|
|
620
|
+
awsRegion: "us-west-2",
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
};
|
|
624
|
+
const result = RemoteConfigSchema.parse(validConfig);
|
|
625
|
+
expect(result).to.deep.equal(validConfig);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it("should require version field", () => {
|
|
629
|
+
const configWithoutVersion = {};
|
|
630
|
+
expect(() => RemoteConfigSchema.parse(configWithoutVersion)).to.throw();
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("should accept minimal valid config", () => {
|
|
634
|
+
const minimalConfig = {
|
|
635
|
+
version: "v1",
|
|
636
|
+
};
|
|
637
|
+
expect(() => RemoteConfigSchema.parse(minimalConfig)).to.not.throw();
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it("should accept config with general settings only", () => {
|
|
641
|
+
const configWithGeneralSettings = {
|
|
642
|
+
version: "v1",
|
|
643
|
+
telemetryEnabled: false,
|
|
644
|
+
mcpMarketplaceEnabled: false,
|
|
645
|
+
yoloModeAllowed: true,
|
|
646
|
+
};
|
|
647
|
+
const result = RemoteConfigSchema.parse(configWithGeneralSettings);
|
|
648
|
+
expect(result.telemetryEnabled).to.equal(false);
|
|
649
|
+
expect(result.mcpMarketplaceEnabled).to.equal(false);
|
|
650
|
+
expect(result.yoloModeAllowed).to.equal(true);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it("should accept config with OpenAI compatible provider only", () => {
|
|
654
|
+
const config = {
|
|
655
|
+
version: "v1",
|
|
656
|
+
providerSettings: {
|
|
657
|
+
OpenAiCompatible: {
|
|
658
|
+
models: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }],
|
|
659
|
+
openAiBaseUrl: "https://api.openai.com/v1",
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
expect(() => RemoteConfigSchema.parse(config)).to.not.throw();
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("should accept config with AWS Bedrock provider only", () => {
|
|
667
|
+
const config = {
|
|
668
|
+
version: "v1",
|
|
669
|
+
providerSettings: {
|
|
670
|
+
AwsBedrock: {
|
|
671
|
+
models: [{ id: "anthropic.claude-v2" }],
|
|
672
|
+
awsRegion: "us-east-1",
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
};
|
|
676
|
+
expect(() => RemoteConfigSchema.parse(config)).to.not.throw();
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it("should reject invalid version type", () => {
|
|
680
|
+
expect(() => RemoteConfigSchema.parse({ version: 123 })).to.throw();
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it("should reject invalid telemetry setting type", () => {
|
|
684
|
+
expect(() =>
|
|
685
|
+
RemoteConfigSchema.parse({
|
|
686
|
+
version: "v1",
|
|
687
|
+
telemetryEnabled: "yes",
|
|
688
|
+
}),
|
|
689
|
+
).to.throw();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it("should allow undefined optional provider settings", () => {
|
|
693
|
+
const config = {
|
|
694
|
+
version: "v1",
|
|
695
|
+
// providerSettings is undefined
|
|
696
|
+
};
|
|
697
|
+
expect(() => RemoteConfigSchema.parse(config)).to.not.throw();
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
it("should handle complete config with all fields", () => {
|
|
701
|
+
const config = {
|
|
702
|
+
version: "v1",
|
|
703
|
+
telemetryEnabled: true,
|
|
704
|
+
kanbanEnabled: true,
|
|
705
|
+
mcpMarketplaceEnabled: false,
|
|
706
|
+
blockPersonalRemoteMCPServers: true,
|
|
707
|
+
allowedMCPServers: [
|
|
708
|
+
{ id: "https://github.com/mcp/filesystem" },
|
|
709
|
+
{ id: "https://github.com/mcp/github" },
|
|
710
|
+
],
|
|
711
|
+
yoloModeAllowed: true,
|
|
712
|
+
openTelemetryEnabled: true,
|
|
713
|
+
openTelemetryMetricsExporter: "otlp",
|
|
714
|
+
openTelemetryLogsExporter: "otlp",
|
|
715
|
+
openTelemetryOtlpProtocol: "http/json",
|
|
716
|
+
openTelemetryOtlpEndpoint: "http://localhost:4318",
|
|
717
|
+
openTelemetryOtlpMetricsProtocol: "http/json",
|
|
718
|
+
openTelemetryOtlpMetricsEndpoint: "http://localhost:4318/v1/metrics",
|
|
719
|
+
openTelemetryOtlpLogsProtocol: "http/json",
|
|
720
|
+
openTelemetryOtlpLogsEndpoint: "http://localhost:4318/v1/logs",
|
|
721
|
+
openTelemetryMetricExportInterval: 60000,
|
|
722
|
+
openTelemetryOtlpInsecure: false,
|
|
723
|
+
openTelemetryLogBatchSize: 512,
|
|
724
|
+
openTelemetryLogBatchTimeout: 5000,
|
|
725
|
+
openTelemetryLogMaxQueueSize: 2048,
|
|
726
|
+
openTelemetryOtlpHeaders: { test: "string" },
|
|
727
|
+
globalRules: [
|
|
728
|
+
{
|
|
729
|
+
alwaysEnabled: true,
|
|
730
|
+
name: "company-standards.md",
|
|
731
|
+
contents:
|
|
732
|
+
"# Company Standards\n\nAll code must follow these standards...",
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
alwaysEnabled: false,
|
|
736
|
+
name: "optional-guidelines.md",
|
|
737
|
+
contents:
|
|
738
|
+
"# Optional Guidelines\n\nConsider these best practices...",
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
globalWorkflows: [
|
|
742
|
+
{
|
|
743
|
+
alwaysEnabled: true,
|
|
744
|
+
name: "deployment-workflow.md",
|
|
745
|
+
contents:
|
|
746
|
+
"# Deployment Workflow\n\n1. Run tests\n2. Build\n3. Deploy",
|
|
747
|
+
},
|
|
748
|
+
],
|
|
749
|
+
providerSettings: {
|
|
750
|
+
OpenAiCompatible: {
|
|
751
|
+
models: [
|
|
752
|
+
{
|
|
753
|
+
id: "gpt-4",
|
|
754
|
+
temperature: 0.7,
|
|
755
|
+
isR1FormatRequired: false,
|
|
756
|
+
maxTokens: 4096,
|
|
757
|
+
contextWindow: 128000,
|
|
758
|
+
inputPrice: 0.03,
|
|
759
|
+
outputPrice: 0.06,
|
|
760
|
+
supportsImages: true,
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
id: "gpt-3.5-turbo",
|
|
764
|
+
temperature: 0.8,
|
|
765
|
+
isR1FormatRequired: false,
|
|
766
|
+
maxTokens: 4096,
|
|
767
|
+
contextWindow: 16000,
|
|
768
|
+
inputPrice: 0.001,
|
|
769
|
+
outputPrice: 0.002,
|
|
770
|
+
supportsImages: false,
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
openAiBaseUrl: "https://custom.openai.api/v1",
|
|
774
|
+
openAiHeaders: {
|
|
775
|
+
"X-API-Key": "secret-key",
|
|
776
|
+
"X-Custom-Header": "custom-value",
|
|
777
|
+
},
|
|
778
|
+
azureApiVersion: "2024-02-15-preview",
|
|
779
|
+
},
|
|
780
|
+
AwsBedrock: {
|
|
781
|
+
models: [
|
|
782
|
+
{ id: "anthropic.claude-v2", thinkingBudgetTokens: 1600 },
|
|
783
|
+
{ id: "anthropic.claude-instant-v1", thinkingBudgetTokens: 800 },
|
|
784
|
+
],
|
|
785
|
+
customModels: [
|
|
786
|
+
{
|
|
787
|
+
name: "my-custom-model",
|
|
788
|
+
baseModelId: "anthropic.claude-v2",
|
|
789
|
+
thinkingBudgetTokens: 2000,
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
name: "another-custom",
|
|
793
|
+
baseModelId: "anthropic.claude-instant-v1",
|
|
794
|
+
thinkingBudgetTokens: 1000,
|
|
795
|
+
},
|
|
796
|
+
],
|
|
797
|
+
awsRegion: "eu-west-1",
|
|
798
|
+
awsUseCrossRegionInference: false,
|
|
799
|
+
awsUseGlobalInference: true,
|
|
800
|
+
awsBedrockUsePromptCache: true,
|
|
801
|
+
awsBedrockEndpoint: "https://custom-bedrock.endpoint",
|
|
802
|
+
},
|
|
803
|
+
Cline: {
|
|
804
|
+
models: [
|
|
805
|
+
{ id: "claude-3-5-sonnet-20241022" },
|
|
806
|
+
{ id: "claude-3-5-haiku-20241022" },
|
|
807
|
+
],
|
|
808
|
+
},
|
|
809
|
+
Vertex: {
|
|
810
|
+
models: [
|
|
811
|
+
{
|
|
812
|
+
id: "claude-3-5-sonnet-v2@20241022",
|
|
813
|
+
thinkingBudgetTokens: 1600,
|
|
814
|
+
},
|
|
815
|
+
{ id: "claude-3-5-haiku@20241022" },
|
|
816
|
+
],
|
|
817
|
+
vertexProjectId: "my-gcp-project",
|
|
818
|
+
vertexRegion: "us-central1",
|
|
819
|
+
},
|
|
820
|
+
Anthropic: {
|
|
821
|
+
models: [
|
|
822
|
+
{ id: "claude-3-5-sonnet-20241022" },
|
|
823
|
+
{ id: "claude-3-5-sonnet-20241024", thinkingBudgetTokens: 1600 },
|
|
824
|
+
],
|
|
825
|
+
baseUrl: "https://example.cline.bot",
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
enterpriseTelemetry: {
|
|
829
|
+
promptUploading: {
|
|
830
|
+
enabled: true,
|
|
831
|
+
type: "s3_access_keys",
|
|
832
|
+
s3AccessSettings: {
|
|
833
|
+
bucket: "enterprise-prompts",
|
|
834
|
+
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
|
835
|
+
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
836
|
+
region: "us-east-1",
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
};
|
|
841
|
+
const result = RemoteConfigSchema.parse(config);
|
|
842
|
+
|
|
843
|
+
// Verify all top-level fields
|
|
844
|
+
expect(result.version).to.equal("v1");
|
|
845
|
+
expect(result.telemetryEnabled).to.equal(true);
|
|
846
|
+
expect(result.yoloModeAllowed).to.equal(true);
|
|
847
|
+
expect(result.kanbanEnabled).to.equal(true);
|
|
848
|
+
|
|
849
|
+
expect(result.mcpMarketplaceEnabled).to.equal(false);
|
|
850
|
+
expect(result.allowedMCPServers).to.deep.equal(config.allowedMCPServers);
|
|
851
|
+
|
|
852
|
+
// Verify OpenAI Compatible settings
|
|
853
|
+
expect(
|
|
854
|
+
result.providerSettings?.OpenAiCompatible?.models,
|
|
855
|
+
).to.have.lengthOf(2);
|
|
856
|
+
expect(result.providerSettings?.OpenAiCompatible?.openAiBaseUrl).to.equal(
|
|
857
|
+
"https://custom.openai.api/v1",
|
|
858
|
+
);
|
|
859
|
+
expect(
|
|
860
|
+
result.providerSettings?.OpenAiCompatible?.azureApiVersion,
|
|
861
|
+
).to.equal("2024-02-15-preview");
|
|
862
|
+
|
|
863
|
+
// Verify AWS Bedrock settings
|
|
864
|
+
expect(result.providerSettings?.AwsBedrock?.models).to.have.lengthOf(2);
|
|
865
|
+
expect(
|
|
866
|
+
result.providerSettings?.AwsBedrock?.customModels,
|
|
867
|
+
).to.have.lengthOf(2);
|
|
868
|
+
expect(result.providerSettings?.AwsBedrock?.awsRegion).to.equal(
|
|
869
|
+
"eu-west-1",
|
|
870
|
+
);
|
|
871
|
+
expect(
|
|
872
|
+
result.providerSettings?.AwsBedrock?.awsUseCrossRegionInference,
|
|
873
|
+
).to.equal(false);
|
|
874
|
+
expect(
|
|
875
|
+
result.providerSettings?.AwsBedrock?.awsUseGlobalInference,
|
|
876
|
+
).to.equal(true);
|
|
877
|
+
expect(
|
|
878
|
+
result.providerSettings?.AwsBedrock?.awsBedrockUsePromptCache,
|
|
879
|
+
).to.equal(true);
|
|
880
|
+
expect(result.providerSettings?.AwsBedrock?.awsBedrockEndpoint).to.equal(
|
|
881
|
+
"https://custom-bedrock.endpoint",
|
|
882
|
+
);
|
|
883
|
+
|
|
884
|
+
// Verify Cline settings
|
|
885
|
+
expect(result.providerSettings?.Cline?.models).to.have.lengthOf(2);
|
|
886
|
+
expect(result.providerSettings?.Cline?.models?.[0].id).to.equal(
|
|
887
|
+
"claude-3-5-sonnet-20241022",
|
|
888
|
+
);
|
|
889
|
+
expect(result.providerSettings?.Cline?.models?.[1].id).to.equal(
|
|
890
|
+
"claude-3-5-haiku-20241022",
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
// Verify Vertex settings
|
|
894
|
+
expect(result.providerSettings?.Vertex?.models).to.have.lengthOf(2);
|
|
895
|
+
expect(result.providerSettings?.Vertex?.models?.[0].id).to.equal(
|
|
896
|
+
"claude-3-5-sonnet-v2@20241022",
|
|
897
|
+
);
|
|
898
|
+
expect(
|
|
899
|
+
result.providerSettings?.Vertex?.models?.[0].thinkingBudgetTokens,
|
|
900
|
+
).to.equal(1600);
|
|
901
|
+
expect(result.providerSettings?.Vertex?.models?.[1].id).to.equal(
|
|
902
|
+
"claude-3-5-haiku@20241022",
|
|
903
|
+
);
|
|
904
|
+
expect(result.providerSettings?.Vertex?.models?.[1].thinkingBudgetTokens)
|
|
905
|
+
.to.be.undefined;
|
|
906
|
+
expect(result.providerSettings?.Vertex?.vertexProjectId).to.equal(
|
|
907
|
+
"my-gcp-project",
|
|
908
|
+
);
|
|
909
|
+
expect(result.providerSettings?.Vertex?.vertexRegion).to.equal(
|
|
910
|
+
"us-central1",
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
expect(result.providerSettings?.Anthropic?.models).to.have.lengthOf(2);
|
|
914
|
+
expect(result.providerSettings?.Anthropic?.models?.[0].id).to.equal(
|
|
915
|
+
"claude-3-5-sonnet-20241022",
|
|
916
|
+
);
|
|
917
|
+
expect(
|
|
918
|
+
result.providerSettings?.Anthropic?.models?.[0].thinkingBudgetTokens,
|
|
919
|
+
).to.be.undefined;
|
|
920
|
+
expect(result.providerSettings?.Anthropic?.models?.[1].id).to.equal(
|
|
921
|
+
"claude-3-5-sonnet-20241024",
|
|
922
|
+
);
|
|
923
|
+
expect(
|
|
924
|
+
result.providerSettings?.Anthropic?.models?.[1].thinkingBudgetTokens,
|
|
925
|
+
).to.equal(1600);
|
|
926
|
+
expect(result.providerSettings?.Anthropic?.baseUrl).to.equal(
|
|
927
|
+
"https://example.cline.bot",
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
// Verify OpenTelemetry settings
|
|
931
|
+
expect(result.openTelemetryEnabled).to.equal(true);
|
|
932
|
+
expect(result.openTelemetryMetricsExporter).to.equal("otlp");
|
|
933
|
+
expect(result.openTelemetryLogsExporter).to.equal("otlp");
|
|
934
|
+
expect(result.openTelemetryOtlpProtocol).to.equal("http/json");
|
|
935
|
+
expect(result.openTelemetryOtlpEndpoint).to.equal(
|
|
936
|
+
"http://localhost:4318",
|
|
937
|
+
);
|
|
938
|
+
expect(result.openTelemetryOtlpMetricsProtocol).to.equal("http/json");
|
|
939
|
+
expect(result.openTelemetryOtlpMetricsEndpoint).to.equal(
|
|
940
|
+
"http://localhost:4318/v1/metrics",
|
|
941
|
+
);
|
|
942
|
+
expect(result.openTelemetryOtlpLogsProtocol).to.equal("http/json");
|
|
943
|
+
expect(result.openTelemetryOtlpLogsEndpoint).to.equal(
|
|
944
|
+
"http://localhost:4318/v1/logs",
|
|
945
|
+
);
|
|
946
|
+
expect(result.openTelemetryMetricExportInterval).to.equal(60000);
|
|
947
|
+
expect(result.openTelemetryOtlpInsecure).to.equal(false);
|
|
948
|
+
expect(result.openTelemetryLogBatchSize).to.equal(512);
|
|
949
|
+
expect(result.openTelemetryLogBatchTimeout).to.equal(5000);
|
|
950
|
+
expect(result.openTelemetryLogMaxQueueSize).to.equal(2048);
|
|
951
|
+
expect(result.openTelemetryOtlpHeaders).to.deep.equal({ test: "string" });
|
|
952
|
+
|
|
953
|
+
// Verify Global Instructions settings
|
|
954
|
+
expect(result.globalRules).to.have.lengthOf(2);
|
|
955
|
+
expect(result.globalRules?.[0].alwaysEnabled).to.equal(true);
|
|
956
|
+
expect(result.globalRules?.[0].name).to.equal("company-standards.md");
|
|
957
|
+
expect(result.globalRules?.[0].contents).to.include("Company Standards");
|
|
958
|
+
expect(result.globalRules?.[1].alwaysEnabled).to.equal(false);
|
|
959
|
+
expect(result.globalRules?.[1].name).to.equal("optional-guidelines.md");
|
|
960
|
+
|
|
961
|
+
expect(result.globalWorkflows).to.have.lengthOf(1);
|
|
962
|
+
expect(result.globalWorkflows?.[0].alwaysEnabled).to.equal(true);
|
|
963
|
+
expect(result.globalWorkflows?.[0].name).to.equal(
|
|
964
|
+
"deployment-workflow.md",
|
|
965
|
+
);
|
|
966
|
+
expect(result.globalWorkflows?.[0].contents).to.include(
|
|
967
|
+
"Deployment Workflow",
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
expect(result.enterpriseTelemetry?.promptUploading?.enabled).to.equal(
|
|
971
|
+
true,
|
|
972
|
+
);
|
|
973
|
+
expect(result.enterpriseTelemetry?.promptUploading?.type).to.equal(
|
|
974
|
+
"s3_access_keys",
|
|
975
|
+
);
|
|
976
|
+
expect(
|
|
977
|
+
result.enterpriseTelemetry?.promptUploading?.s3AccessSettings?.bucket,
|
|
978
|
+
).to.equal("enterprise-prompts");
|
|
979
|
+
expect(
|
|
980
|
+
result.enterpriseTelemetry?.promptUploading?.s3AccessSettings?.region,
|
|
981
|
+
).to.equal("us-east-1");
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
describe("Type Inference", () => {
|
|
986
|
+
it("should properly infer RemoteConfig type", () => {
|
|
987
|
+
const config: RemoteConfig = {
|
|
988
|
+
version: "v1",
|
|
989
|
+
telemetryEnabled: true,
|
|
990
|
+
};
|
|
991
|
+
// TypeScript compilation will fail if type inference is wrong
|
|
992
|
+
expect(config.version).to.be.a("string");
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
it("should allow optional fields to be undefined", () => {
|
|
996
|
+
const config: RemoteConfig = {
|
|
997
|
+
version: "v1",
|
|
998
|
+
// All other fields are optional and can be undefined
|
|
999
|
+
};
|
|
1000
|
+
expect(config.telemetryEnabled).to.be.undefined;
|
|
1001
|
+
expect(config.providerSettings).to.be.undefined;
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
});
|