@hasna/switcher 0.1.1 → 0.1.2
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/README.md +142 -6
- package/dist/aider-args.d.ts +7 -0
- package/dist/aider-config.d.ts +6 -0
- package/dist/auth.d.ts +3 -0
- package/dist/cli/index.js +6481 -3510
- package/dist/cline-backend.d.ts +7 -0
- package/dist/credentials.d.ts +5 -5
- package/dist/direct-launch.d.ts +1 -1
- package/dist/domain.d.ts +62 -49
- package/dist/dsh-args.d.ts +5 -0
- package/dist/gemini-bridge.d.ts +6 -0
- package/dist/gemini-config.d.ts +12 -0
- package/dist/generated/api.d.ts +28 -21
- package/dist/harness-arguments.d.ts +1 -0
- package/dist/harness-installation.d.ts +19 -0
- package/dist/harness-types.d.ts +1 -0
- package/dist/harnesses.d.ts +5 -2
- package/dist/hermes-backend.d.ts +19 -0
- package/dist/index.js +53 -11
- package/dist/kilo-config.d.ts +13 -0
- package/dist/kilo.d.ts +5 -0
- package/dist/launcher.d.ts +4 -3
- package/dist/mcp/index.js +43 -11
- package/dist/omp-backend.d.ts +7 -0
- package/dist/opencode2-config.d.ts +3 -3
- package/dist/ori-backend.d.ts +2 -2
- package/dist/presets.d.ts +11 -10
- package/dist/sdk.d.ts +41 -33
- package/dist/sdk.js +53 -11
- package/dist/serve/index.js +274 -49
- package/openapi.json +138 -14
- package/package.json +12 -2
package/dist/sdk.d.ts
CHANGED
|
@@ -57,12 +57,12 @@ export declare class SwitcherClient {
|
|
|
57
57
|
credentialEnv?: string;
|
|
58
58
|
credentialAliases: string[];
|
|
59
59
|
protocols: {
|
|
60
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
60
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
61
61
|
baseUrl?: string;
|
|
62
|
-
authStyle: "bearer" | "x-api-key";
|
|
62
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
63
63
|
catalogBaseUrl?: string;
|
|
64
|
-
catalogFormat: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
65
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
64
|
+
catalogFormat: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
65
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
66
66
|
modelsPath: string;
|
|
67
67
|
notes: string[];
|
|
68
68
|
}[];
|
|
@@ -73,12 +73,12 @@ export declare class SwitcherClient {
|
|
|
73
73
|
id: string;
|
|
74
74
|
name: string;
|
|
75
75
|
baseUrl: string;
|
|
76
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
76
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
77
77
|
credentialEnv?: string;
|
|
78
|
-
authStyle: "bearer" | "x-api-key";
|
|
78
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
79
79
|
catalogBaseUrl?: string;
|
|
80
|
-
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
81
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
80
|
+
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
81
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
82
82
|
catalogCredentialEnv?: string;
|
|
83
83
|
catalogAccountId?: string;
|
|
84
84
|
modelsPath: string;
|
|
@@ -92,6 +92,7 @@ export declare class SwitcherClient {
|
|
|
92
92
|
inputModalities?: string[];
|
|
93
93
|
outputModalities?: string[];
|
|
94
94
|
supportedParameters?: string[];
|
|
95
|
+
supportedGenerationMethods?: string[];
|
|
95
96
|
}[];
|
|
96
97
|
version: number;
|
|
97
98
|
updatedAt: string;
|
|
@@ -100,12 +101,12 @@ export declare class SwitcherClient {
|
|
|
100
101
|
id: string;
|
|
101
102
|
name: string;
|
|
102
103
|
baseUrl: string;
|
|
103
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
104
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
104
105
|
credentialEnv?: string;
|
|
105
|
-
authStyle: "bearer" | "x-api-key";
|
|
106
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
106
107
|
catalogBaseUrl?: string;
|
|
107
|
-
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
108
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
108
|
+
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
109
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
109
110
|
catalogCredentialEnv?: string;
|
|
110
111
|
catalogAccountId?: string;
|
|
111
112
|
modelsPath: string;
|
|
@@ -119,6 +120,7 @@ export declare class SwitcherClient {
|
|
|
119
120
|
inputModalities?: string[];
|
|
120
121
|
outputModalities?: string[];
|
|
121
122
|
supportedParameters?: string[];
|
|
123
|
+
supportedGenerationMethods?: string[];
|
|
122
124
|
}[];
|
|
123
125
|
version: number;
|
|
124
126
|
updatedAt: string;
|
|
@@ -127,12 +129,12 @@ export declare class SwitcherClient {
|
|
|
127
129
|
id: string;
|
|
128
130
|
name: string;
|
|
129
131
|
baseUrl: string;
|
|
130
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
132
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
131
133
|
credentialEnv?: string;
|
|
132
|
-
authStyle: "bearer" | "x-api-key";
|
|
134
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
133
135
|
catalogBaseUrl?: string;
|
|
134
|
-
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
135
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
136
|
+
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
137
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
136
138
|
catalogCredentialEnv?: string;
|
|
137
139
|
catalogAccountId?: string;
|
|
138
140
|
modelsPath: string;
|
|
@@ -146,6 +148,7 @@ export declare class SwitcherClient {
|
|
|
146
148
|
inputModalities?: string[];
|
|
147
149
|
outputModalities?: string[];
|
|
148
150
|
supportedParameters?: string[];
|
|
151
|
+
supportedGenerationMethods?: string[];
|
|
149
152
|
}[];
|
|
150
153
|
version: number;
|
|
151
154
|
updatedAt: string;
|
|
@@ -154,12 +157,12 @@ export declare class SwitcherClient {
|
|
|
154
157
|
id: string;
|
|
155
158
|
name: string;
|
|
156
159
|
baseUrl: string;
|
|
157
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
160
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
158
161
|
credentialEnv?: string;
|
|
159
|
-
authStyle: "bearer" | "x-api-key";
|
|
162
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
160
163
|
catalogBaseUrl?: string;
|
|
161
|
-
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
162
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
164
|
+
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
165
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
163
166
|
catalogCredentialEnv?: string;
|
|
164
167
|
catalogAccountId?: string;
|
|
165
168
|
modelsPath: string;
|
|
@@ -173,6 +176,7 @@ export declare class SwitcherClient {
|
|
|
173
176
|
inputModalities?: string[];
|
|
174
177
|
outputModalities?: string[];
|
|
175
178
|
supportedParameters?: string[];
|
|
179
|
+
supportedGenerationMethods?: string[];
|
|
176
180
|
}[];
|
|
177
181
|
version: number;
|
|
178
182
|
updatedAt: string;
|
|
@@ -191,6 +195,7 @@ export declare class SwitcherClient {
|
|
|
191
195
|
inputModalities?: string[];
|
|
192
196
|
outputModalities?: string[];
|
|
193
197
|
supportedParameters?: string[];
|
|
198
|
+
supportedGenerationMethods?: string[];
|
|
194
199
|
}[];
|
|
195
200
|
refreshedAt: string;
|
|
196
201
|
source: "remote" | "manual";
|
|
@@ -206,6 +211,7 @@ export declare class SwitcherClient {
|
|
|
206
211
|
inputModalities?: string[];
|
|
207
212
|
outputModalities?: string[];
|
|
208
213
|
supportedParameters?: string[];
|
|
214
|
+
supportedGenerationMethods?: string[];
|
|
209
215
|
codingEligible: boolean;
|
|
210
216
|
}[];
|
|
211
217
|
total: number;
|
|
@@ -218,7 +224,7 @@ export declare class SwitcherClient {
|
|
|
218
224
|
id: string;
|
|
219
225
|
name: string;
|
|
220
226
|
providerId: string;
|
|
221
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
227
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
222
228
|
model: string;
|
|
223
229
|
version: number;
|
|
224
230
|
updatedAt: string;
|
|
@@ -227,7 +233,7 @@ export declare class SwitcherClient {
|
|
|
227
233
|
id: string;
|
|
228
234
|
name: string;
|
|
229
235
|
providerId: string;
|
|
230
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
236
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
231
237
|
model: string;
|
|
232
238
|
version: number;
|
|
233
239
|
updatedAt: string;
|
|
@@ -236,7 +242,7 @@ export declare class SwitcherClient {
|
|
|
236
242
|
id: string;
|
|
237
243
|
name: string;
|
|
238
244
|
providerId: string;
|
|
239
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
245
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
240
246
|
model: string;
|
|
241
247
|
version: number;
|
|
242
248
|
updatedAt: string;
|
|
@@ -245,7 +251,7 @@ export declare class SwitcherClient {
|
|
|
245
251
|
id: string;
|
|
246
252
|
name: string;
|
|
247
253
|
providerId: string;
|
|
248
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
254
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
249
255
|
model: string;
|
|
250
256
|
version: number;
|
|
251
257
|
updatedAt: string;
|
|
@@ -258,12 +264,12 @@ export declare class SwitcherClient {
|
|
|
258
264
|
id: string;
|
|
259
265
|
name: string;
|
|
260
266
|
baseUrl: string;
|
|
261
|
-
protocol: "anthropic-messages" | "openai-responses" | "openai-chat";
|
|
267
|
+
protocol: "anthropic-messages" | "openai-responses" | "openai-chat" | "gemini-generate-content";
|
|
262
268
|
credentialEnv?: string;
|
|
263
|
-
authStyle: "bearer" | "x-api-key";
|
|
269
|
+
authStyle: "bearer" | "x-api-key" | "api-key";
|
|
264
270
|
catalogBaseUrl?: string;
|
|
265
|
-
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "none";
|
|
266
|
-
catalogAuthStyle?: "bearer" | "x-api-key" | "none";
|
|
271
|
+
catalogFormat?: "openai" | "ollama" | "mistral" | "together" | "fireworks" | "dashscope" | "gemini" | "none";
|
|
272
|
+
catalogAuthStyle?: "bearer" | "x-api-key" | "api-key" | "none";
|
|
267
273
|
catalogCredentialEnv?: string;
|
|
268
274
|
catalogAccountId?: string;
|
|
269
275
|
modelsPath: string;
|
|
@@ -277,6 +283,7 @@ export declare class SwitcherClient {
|
|
|
277
283
|
inputModalities?: string[];
|
|
278
284
|
outputModalities?: string[];
|
|
279
285
|
supportedParameters?: string[];
|
|
286
|
+
supportedGenerationMethods?: string[];
|
|
280
287
|
}[];
|
|
281
288
|
version: number;
|
|
282
289
|
updatedAt: string;
|
|
@@ -285,7 +292,7 @@ export declare class SwitcherClient {
|
|
|
285
292
|
id: string;
|
|
286
293
|
name: string;
|
|
287
294
|
providerId: string;
|
|
288
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
295
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
289
296
|
model: string;
|
|
290
297
|
version: number;
|
|
291
298
|
updatedAt: string;
|
|
@@ -301,6 +308,7 @@ export declare class SwitcherClient {
|
|
|
301
308
|
inputModalities?: string[];
|
|
302
309
|
outputModalities?: string[];
|
|
303
310
|
supportedParameters?: string[];
|
|
311
|
+
supportedGenerationMethods?: string[];
|
|
304
312
|
}[];
|
|
305
313
|
refreshedAt: string;
|
|
306
314
|
source: "remote" | "manual";
|
|
@@ -310,7 +318,7 @@ export declare class SwitcherClient {
|
|
|
310
318
|
}>;
|
|
311
319
|
listRuns(options?: {}): Promise<Page<{
|
|
312
320
|
profileId: string;
|
|
313
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
321
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
314
322
|
model: string;
|
|
315
323
|
planToken: string;
|
|
316
324
|
version: number;
|
|
@@ -326,7 +334,7 @@ export declare class SwitcherClient {
|
|
|
326
334
|
}>>;
|
|
327
335
|
getRun(id: string): Promise<{
|
|
328
336
|
profileId: string;
|
|
329
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
337
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
330
338
|
model: string;
|
|
331
339
|
planToken: string;
|
|
332
340
|
version: number;
|
|
@@ -342,7 +350,7 @@ export declare class SwitcherClient {
|
|
|
342
350
|
}>;
|
|
343
351
|
createRun(input: components["schemas"]["RunInput"], idempotencyKey?: string): Promise<{
|
|
344
352
|
profileId: string;
|
|
345
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
353
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
346
354
|
model: string;
|
|
347
355
|
planToken: string;
|
|
348
356
|
version: number;
|
|
@@ -361,7 +369,7 @@ export declare class SwitcherClient {
|
|
|
361
369
|
exitCode: number;
|
|
362
370
|
}, idempotencyKey?: string): Promise<{
|
|
363
371
|
profileId: string;
|
|
364
|
-
harness: "claude" | "codex" | "grok" | "opencode2" | "pi";
|
|
372
|
+
harness: "claude" | "codex" | "grok" | "opencode" | "opencode2" | "pi" | "omp" | "dsh" | "cline" | "hermes" | "prime-agent" | "gemini" | "aider" | "kilo";
|
|
365
373
|
model: string;
|
|
366
374
|
planToken: string;
|
|
367
375
|
version: number;
|
package/dist/sdk.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/domain.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
var harnessSchema = z.enum(["claude", "codex", "grok", "opencode2", "pi"]);
|
|
4
|
-
var protocolSchema = z.enum(["anthropic-messages", "openai-responses", "openai-chat"]);
|
|
3
|
+
var harnessSchema = z.enum(["claude", "codex", "grok", "opencode", "opencode2", "pi", "omp", "dsh", "cline", "hermes", "prime-agent", "gemini", "aider", "kilo"]);
|
|
4
|
+
var protocolSchema = z.enum(["anthropic-messages", "openai-responses", "openai-chat", "gemini-generate-content"]);
|
|
5
5
|
var idSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/);
|
|
6
6
|
var label = z.string().min(1).max(200);
|
|
7
7
|
var envRef = z.string().regex(/^SWITCHER_PROVIDER_[A-Z0-9_]+$/);
|
|
@@ -33,7 +33,8 @@ var modelSchema = z.object({
|
|
|
33
33
|
maxOutputTokens: z.number().int().positive().optional(),
|
|
34
34
|
inputModalities: z.array(z.string().max(50)).max(20).optional(),
|
|
35
35
|
outputModalities: z.array(z.string().max(50)).max(20).optional(),
|
|
36
|
-
supportedParameters: z.array(z.string().max(100)).max(100).optional()
|
|
36
|
+
supportedParameters: z.array(z.string().max(100)).max(100).optional(),
|
|
37
|
+
supportedGenerationMethods: z.array(z.string().min(1).max(100)).max(100).optional()
|
|
37
38
|
}).strict();
|
|
38
39
|
var providerInputSchema = z.object({
|
|
39
40
|
id: idSchema,
|
|
@@ -41,10 +42,10 @@ var providerInputSchema = z.object({
|
|
|
41
42
|
baseUrl: urlSchema,
|
|
42
43
|
protocol: protocolSchema,
|
|
43
44
|
credentialEnv: envRef.optional(),
|
|
44
|
-
authStyle: z.enum(["bearer", "x-api-key"]).default("bearer"),
|
|
45
|
+
authStyle: z.enum(["bearer", "x-api-key", "api-key"]).default("bearer"),
|
|
45
46
|
catalogBaseUrl: urlSchema.optional(),
|
|
46
|
-
catalogFormat: z.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "none"]).optional(),
|
|
47
|
-
catalogAuthStyle: z.enum(["bearer", "x-api-key", "none"]).optional(),
|
|
47
|
+
catalogFormat: z.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "gemini", "none"]).optional(),
|
|
48
|
+
catalogAuthStyle: z.enum(["bearer", "x-api-key", "api-key", "none"]).optional(),
|
|
48
49
|
catalogCredentialEnv: envRef.optional(),
|
|
49
50
|
catalogAccountId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/).optional(),
|
|
50
51
|
modelsPath: z.string().regex(/^[a-zA-Z0-9_/-]+$/).max(200).default("models"),
|
|
@@ -58,10 +59,10 @@ var providerPresetSchema = z.object({
|
|
|
58
59
|
protocols: z.array(z.object({
|
|
59
60
|
protocol: protocolSchema,
|
|
60
61
|
baseUrl: urlSchema.optional(),
|
|
61
|
-
authStyle: z.enum(["bearer", "x-api-key"]),
|
|
62
|
+
authStyle: z.enum(["bearer", "x-api-key", "api-key"]),
|
|
62
63
|
catalogBaseUrl: urlSchema.optional(),
|
|
63
|
-
catalogFormat: z.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "none"]),
|
|
64
|
-
catalogAuthStyle: z.enum(["bearer", "x-api-key", "none"]).optional(),
|
|
64
|
+
catalogFormat: z.enum(["openai", "ollama", "mistral", "together", "fireworks", "dashscope", "gemini", "none"]),
|
|
65
|
+
catalogAuthStyle: z.enum(["bearer", "x-api-key", "api-key", "none"]).optional(),
|
|
65
66
|
modelsPath: z.string(),
|
|
66
67
|
notes: z.array(z.string())
|
|
67
68
|
}).strict()).min(1),
|
|
@@ -102,7 +103,15 @@ function parse(schema, value) {
|
|
|
102
103
|
return result.data;
|
|
103
104
|
}
|
|
104
105
|
function compatible(harness, protocol) {
|
|
105
|
-
|
|
106
|
+
if (harness === "claude")
|
|
107
|
+
return protocol === "anthropic-messages";
|
|
108
|
+
if (harness === "codex")
|
|
109
|
+
return protocol === "openai-responses";
|
|
110
|
+
if (harness === "gemini")
|
|
111
|
+
return protocol === "gemini-generate-content";
|
|
112
|
+
if (protocol === "gemini-generate-content")
|
|
113
|
+
return false;
|
|
114
|
+
return true;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
// src/http.ts
|
|
@@ -168,7 +177,18 @@ var providerPresets = [
|
|
|
168
177
|
], ["https://api-docs.deepseek.com/guides/anthropic_api", "https://api-docs.deepseek.com/api/list-models"], "DEEPSEEK_API_KEY"),
|
|
169
178
|
preset("openrouter", "OpenRouter", ["openai-chat", "openai-responses", "anthropic-messages"].map((protocol) => route(protocol, "https://openrouter.ai/api/v1", { catalogAuthStyle: "none" })), ["https://openrouter.ai/docs/api/api-reference/models/list-all-models-and-their-properties", "https://openrouter.ai/docs/guides/overview"], "OPENROUTER_API_KEY"),
|
|
170
179
|
preset("anthropic", "Anthropic", [route("anthropic-messages", "https://api.anthropic.com/v1", { authStyle: "x-api-key" })], ["https://platform.claude.com/docs/en/api/overview", "https://platform.claude.com/docs/en/api/models/list"], "ANTHROPIC_API_KEY"),
|
|
180
|
+
preset("gemini", "Google Gemini", [route("gemini-generate-content", "https://generativelanguage.googleapis.com/v1beta", {
|
|
181
|
+
authStyle: "x-api-key",
|
|
182
|
+
catalogBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
183
|
+
catalogFormat: "gemini",
|
|
184
|
+
catalogAuthStyle: "x-api-key",
|
|
185
|
+
notes: ["Gemini CLI uses the native generateContent wire with x-goog-api-key authentication; model IDs are returned as models/{id}."]
|
|
186
|
+
}), route("openai-chat", "https://generativelanguage.googleapis.com/v1beta/openai")], ["https://ai.google.dev/api", "https://ai.google.dev/api/models", "https://github.com/google-gemini/gemini-cli", "https://ai.google.dev/gemini-api/docs/openai"], "GEMINI_API_KEY"),
|
|
171
187
|
preset("openai", "OpenAI", [route("openai-responses", "https://api.openai.com/v1"), route("openai-chat", "https://api.openai.com/v1")], ["https://platform.openai.com/docs/api-reference/introduction", "https://platform.openai.com/docs/api-reference/models/list"], "OPENAI_API_KEY"),
|
|
188
|
+
preset("azure-openai", "Azure OpenAI (v1)", [
|
|
189
|
+
route("openai-responses", undefined, { authStyle: "api-key", catalogFormat: "none", notes: ["Pass the Azure OpenAI v1 resource endpoint ending in /openai/v1. The request model is your deployment name. Azure's model-definition list is not a deployment catalog, so configure manual deployment models or an explicit deployment catalog parser; Switcher does not synthesize deployment paths or api-version query parameters."] }),
|
|
190
|
+
route("openai-chat", undefined, { authStyle: "api-key", catalogFormat: "none", notes: ["Pass the Azure OpenAI v1 resource endpoint ending in /openai/v1. Chat Completions is POST /chat/completions and accepts the literal api-key header. The request model is your deployment name; configure manual deployment models or an explicit deployment catalog parser because GET /models does not establish deployment names."] })
|
|
191
|
+
], ["https://learn.microsoft.com/en-us/rest/api/aifoundry/azureopenai/models", "https://learn.microsoft.com/en-us/rest/api/microsoft-foundry/azureopenai/chat", "https://learn.microsoft.com/en-us/rest/api/aifoundry/azureopenai/responses"], "AZURE_OPENAI_API_KEY"),
|
|
172
192
|
preset("xai", "xAI", ["openai-chat", "openai-responses", "anthropic-messages"].map((protocol) => route(protocol, "https://api.x.ai/v1")), ["https://api.x.ai/docs/", "https://docs.x.ai/developers/model-capabilities/text/generate-text"], "XAI_API_KEY"),
|
|
173
193
|
preset("ollama", "Ollama", ["openai-chat", "openai-responses"].map((protocol) => route(protocol, "http://127.0.0.1:11434/v1", {
|
|
174
194
|
catalogBaseUrl: "http://127.0.0.1:11434",
|
|
@@ -229,6 +249,8 @@ function providerFromPreset(presetId, options = {}) {
|
|
|
229
249
|
const baseUrl = options.baseUrl ?? selected.baseUrl;
|
|
230
250
|
if (!baseUrl)
|
|
231
251
|
throw new Fault(400, "endpoint_required", "This preset requires an explicit --url for its inference endpoint.");
|
|
252
|
+
if (presetId === "azure-openai" && !/\/openai\/v1$/.test(endpoint(baseUrl)))
|
|
253
|
+
throw new Fault(400, "invalid_url", "Azure OpenAI v1 requires an explicit endpoint ending in /openai/v1; deployment and api-version URLs are unsupported.");
|
|
232
254
|
if (options.baseUrl && selected.baseUrl && new URL(endpoint(options.baseUrl)).origin !== new URL(selected.baseUrl).origin && preset2.credentialEnv && !options.credentialEnv)
|
|
233
255
|
throw new Fault(422, "credential_authority", "An endpoint on another origin requires an explicit --credential-env reference.");
|
|
234
256
|
const suffix = selected.protocol === "anthropic-messages" ? "messages" : selected.protocol === "openai-responses" ? "responses" : "chat";
|
|
@@ -265,6 +287,26 @@ class SwitcherError extends Error {
|
|
|
265
287
|
this.requestId = requestId;
|
|
266
288
|
}
|
|
267
289
|
}
|
|
290
|
+
function apiError(status, data, apiKey) {
|
|
291
|
+
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
292
|
+
const error = object(data) && object(data.error) ? data.error : {};
|
|
293
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
294
|
+
const base64 = Buffer.from(apiKey, "utf8").toString("base64");
|
|
295
|
+
const literal = [apiKey, JSON.stringify(apiKey).slice(1, -1), base64, base64.replace(/=+$/, ""), Buffer.from(apiKey, "utf8").toString("base64url")];
|
|
296
|
+
const encoded = [encodeURIComponent(apiKey), new URLSearchParams({ key: apiKey }).toString().slice(4)];
|
|
297
|
+
const patterns = [...new Set(literal)].sort((a, b) => b.length - a.length).map(escape);
|
|
298
|
+
for (const value of encoded) {
|
|
299
|
+
patterns.unshift(escape(value).replace(/%[0-9A-F]{2}/g, (part) => part.replace(/[A-F]/g, (letter) => `[${letter}${letter.toLowerCase()}]`)));
|
|
300
|
+
}
|
|
301
|
+
const reflected = new RegExp(patterns.join("|"), "g");
|
|
302
|
+
const redact = (value) => value.replace(reflected, "[REDACTED]");
|
|
303
|
+
const identifier = (value, pattern) => typeof value === "string" && pattern.test(value) && redact(value) === value ? value : undefined;
|
|
304
|
+
const code = identifier(error.code, /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/) ?? "api_error";
|
|
305
|
+
const requestId = identifier(error.requestId, /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/);
|
|
306
|
+
const fallback = `Switcher API returned HTTP ${status}.`;
|
|
307
|
+
const message = typeof error.message === "string" && error.message.length <= 4096 ? redact(error.message).replace(/[\x00-\x1f\x7f-\x9f]/g, " ").trim().slice(0, 2048) || fallback : fallback;
|
|
308
|
+
return new SwitcherError(status, code, message, requestId);
|
|
309
|
+
}
|
|
268
310
|
|
|
269
311
|
class SwitcherClient {
|
|
270
312
|
options;
|
|
@@ -301,7 +343,7 @@ class SwitcherClient {
|
|
|
301
343
|
throw new SwitcherError(response.status, "invalid_response", "Switcher API returned invalid JSON.");
|
|
302
344
|
}
|
|
303
345
|
if (!response.ok)
|
|
304
|
-
throw
|
|
346
|
+
throw apiError(response.status, data, apiKey);
|
|
305
347
|
return data;
|
|
306
348
|
}
|
|
307
349
|
query(options = {}) {
|