@lightharu/krouter 1.8.0

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.
Files changed (61) hide show
  1. package/LICENSE +679 -0
  2. package/README.md +238 -0
  3. package/dist-web/assets/index-CM4-0adf.css +1 -0
  4. package/dist-web/assets/index-DCslvfUR.js +139 -0
  5. package/dist-web/favicon.svg +9 -0
  6. package/dist-web/icon.svg +9 -0
  7. package/dist-web/index.html +19 -0
  8. package/out-server/main/kiroAuthSync.js +249 -0
  9. package/out-server/main/kproxy/certManager.js +262 -0
  10. package/out-server/main/kproxy/index.js +254 -0
  11. package/out-server/main/kproxy/mitmProxy.js +475 -0
  12. package/out-server/main/kproxy/types.js +23 -0
  13. package/out-server/main/proxy/accountPool.js +543 -0
  14. package/out-server/main/proxy/clientConfig.js +596 -0
  15. package/out-server/main/proxy/index.js +25 -0
  16. package/out-server/main/proxy/kiroApi.js +1996 -0
  17. package/out-server/main/proxy/logger.js +407 -0
  18. package/out-server/main/proxy/modelCatalog.js +75 -0
  19. package/out-server/main/proxy/promptCacheTracker.js +301 -0
  20. package/out-server/main/proxy/proxyServer.js +3543 -0
  21. package/out-server/main/proxy/selfSignedCert.js +179 -0
  22. package/out-server/main/proxy/systemProxy.js +250 -0
  23. package/out-server/main/proxy/tokenCounter.js +164 -0
  24. package/out-server/main/proxy/toolNameRegistry.js +57 -0
  25. package/out-server/main/proxy/translator.js +1084 -0
  26. package/out-server/main/proxy/types.js +3 -0
  27. package/out-server/main/registration/browser-identity.js +184 -0
  28. package/out-server/main/registration/chainProxy.js +349 -0
  29. package/out-server/main/registration/config.js +58 -0
  30. package/out-server/main/registration/email-service.js +801 -0
  31. package/out-server/main/registration/fingerprint.js +352 -0
  32. package/out-server/main/registration/http-utils.js +148 -0
  33. package/out-server/main/registration/jwe.js +74 -0
  34. package/out-server/main/registration/names.js +142 -0
  35. package/out-server/main/registration/proton-mail-window.js +339 -0
  36. package/out-server/main/registration/registrar.js +1715 -0
  37. package/out-server/main/registration/tlsClientPool.js +70 -0
  38. package/out-server/main/registration/xxtea.js +161 -0
  39. package/out-server/main/runtimePaths.js +19 -0
  40. package/out-server/main/utils/redact.js +95 -0
  41. package/out-server/server/index.js +1272 -0
  42. package/out-server/server/services/accountExtras.js +105 -0
  43. package/out-server/server/services/accountProfileHydration.js +95 -0
  44. package/out-server/server/services/authFlows.js +509 -0
  45. package/out-server/server/services/dashboardTunnel.js +315 -0
  46. package/out-server/server/services/diagnostics.js +326 -0
  47. package/out-server/server/services/kiroAccounts.js +431 -0
  48. package/out-server/server/services/kiroSettings.js +260 -0
  49. package/out-server/server/services/kproxyRuntime.js +264 -0
  50. package/out-server/server/services/localKiroCredentials.js +320 -0
  51. package/out-server/server/services/machineIdRuntime.js +327 -0
  52. package/out-server/server/services/protonBrowserRuntime.js +724 -0
  53. package/out-server/server/services/proxyRuntime.js +523 -0
  54. package/out-server/server/services/registrationRuntime.js +106 -0
  55. package/out-server/server/store.js +266 -0
  56. package/package.json +113 -0
  57. package/resources/tls-client-xgo-1.14.0-windows-amd64.dll +0 -0
  58. package/scripts/kiro-manager-cli.cjs +3 -0
  59. package/scripts/krouter-cli.cjs +509 -0
  60. package/src/renderer/src/assets/krouter-logo.svg +11 -0
  61. package/src/renderer/src/assets/krouter-mark.svg +9 -0
@@ -0,0 +1,596 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configureProxyClients = configureProxyClients;
4
+ const fs_1 = require("fs");
5
+ const promises_1 = require("fs/promises");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const modelCatalog_1 = require("./modelCatalog");
9
+ function dedupeClientModels(models) {
10
+ const modelMap = new Map();
11
+ for (const model of models) {
12
+ const id = model.id?.trim();
13
+ if (!id)
14
+ continue;
15
+ const key = (0, modelCatalog_1.normalizeKiroModelIdForCompare)(id);
16
+ if (!modelMap.has(key)) {
17
+ modelMap.set(key, { ...model, id });
18
+ continue;
19
+ }
20
+ const existing = modelMap.get(key);
21
+ modelMap.set(key, {
22
+ ...model,
23
+ ...existing,
24
+ id: existing.id,
25
+ name: existing.name || model.name,
26
+ inputTypes: existing.inputTypes?.length ? existing.inputTypes : model.inputTypes,
27
+ maxInputTokens: existing.maxInputTokens || model.maxInputTokens,
28
+ maxOutputTokens: existing.maxOutputTokens || model.maxOutputTokens
29
+ });
30
+ }
31
+ return Array.from(modelMap.values());
32
+ }
33
+ function buildOpenClawModels(context) {
34
+ return dedupeClientModels(context.models).filter(model => !(0, modelCatalog_1.isAutoKiroModelId)(model.id));
35
+ }
36
+ function openClawModelsToClientModels(value) {
37
+ if (!Array.isArray(value))
38
+ return [];
39
+ return value
40
+ .map((item) => {
41
+ if (!isRecord(item))
42
+ return null;
43
+ const id = typeof item.id === 'string' ? item.id.trim() : '';
44
+ if (!id)
45
+ return null;
46
+ const input = Array.isArray(item.input)
47
+ ? item.input.filter((entry) => typeof entry === 'string').map(entry => entry.toUpperCase())
48
+ : undefined;
49
+ return {
50
+ id,
51
+ name: typeof item.name === 'string' ? item.name : id,
52
+ inputTypes: input,
53
+ maxInputTokens: typeof item.contextWindow === 'number'
54
+ ? item.contextWindow
55
+ : typeof item.contextTokens === 'number' ? item.contextTokens : undefined,
56
+ maxOutputTokens: typeof item.maxTokens === 'number' ? item.maxTokens : undefined
57
+ };
58
+ })
59
+ .filter((item) => Boolean(item));
60
+ }
61
+ function pickOpenClawPrimaryModel(context, models) {
62
+ const selected = context.modelId.trim();
63
+ if (selected && !(0, modelCatalog_1.isAutoKiroModelId)(selected)) {
64
+ const match = models.find(model => (0, modelCatalog_1.normalizeKiroModelIdForCompare)(model.id) === (0, modelCatalog_1.normalizeKiroModelIdForCompare)(selected));
65
+ if (match)
66
+ return match.id;
67
+ }
68
+ for (const preferredId of modelCatalog_1.KIRO_PROXY_PREFERRED_MODEL_IDS) {
69
+ const match = models.find(model => (0, modelCatalog_1.normalizeKiroModelIdForCompare)(model.id) === (0, modelCatalog_1.normalizeKiroModelIdForCompare)(preferredId));
70
+ if (match)
71
+ return match.id;
72
+ }
73
+ return models[0]?.id || (selected && !(0, modelCatalog_1.isAutoKiroModelId)(selected) ? selected : 'claude-sonnet-4.5');
74
+ }
75
+ function buildOpenClawFallbackRefs(providerId, primaryModelId, models) {
76
+ const byNormalizedId = new Map();
77
+ for (const model of models) {
78
+ const id = model.id?.trim();
79
+ if (!id || (0, modelCatalog_1.isAutoKiroModelId)(id))
80
+ continue;
81
+ byNormalizedId.set((0, modelCatalog_1.normalizeKiroModelIdForCompare)(id), id);
82
+ }
83
+ const preferredFallbackOrder = [
84
+ 'claude-opus-4.8',
85
+ 'claude-opus-4.7',
86
+ 'claude-opus-4.5',
87
+ 'claude-sonnet-4.5',
88
+ 'claude-sonnet-4',
89
+ 'claude-haiku-4.5',
90
+ ...modelCatalog_1.KIRO_PROXY_PREFERRED_MODEL_IDS
91
+ ];
92
+ const primaryKey = (0, modelCatalog_1.normalizeKiroModelIdForCompare)(primaryModelId);
93
+ const refs = [];
94
+ const seen = new Set();
95
+ for (const modelId of preferredFallbackOrder) {
96
+ const match = byNormalizedId.get((0, modelCatalog_1.normalizeKiroModelIdForCompare)(modelId));
97
+ if (!match)
98
+ continue;
99
+ const key = (0, modelCatalog_1.normalizeKiroModelIdForCompare)(match);
100
+ if (key === primaryKey || seen.has(key))
101
+ continue;
102
+ seen.add(key);
103
+ refs.push(`${providerId}/${match}`);
104
+ }
105
+ return refs;
106
+ }
107
+ function isRecord(value) {
108
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
109
+ }
110
+ function parseJsonObject(content, path) {
111
+ const parsed = JSON.parse(path.endsWith('.jsonc') ? stripJsonc(content) : content);
112
+ if (!isRecord(parsed)) {
113
+ throw new Error(`${path} root must be a JSON object`);
114
+ }
115
+ return parsed;
116
+ }
117
+ function stripJsonc(content) {
118
+ let output = '';
119
+ let inString = false;
120
+ let quote = '';
121
+ let escaped = false;
122
+ for (let index = 0; index < content.length; index++) {
123
+ const current = content[index];
124
+ const next = content[index + 1];
125
+ if (inString) {
126
+ output += current;
127
+ if (escaped) {
128
+ escaped = false;
129
+ continue;
130
+ }
131
+ if (current === '\\') {
132
+ escaped = true;
133
+ continue;
134
+ }
135
+ if (current === quote) {
136
+ inString = false;
137
+ quote = '';
138
+ }
139
+ continue;
140
+ }
141
+ if (current === '"' || current === "'") {
142
+ inString = true;
143
+ quote = current;
144
+ output += current;
145
+ continue;
146
+ }
147
+ if (current === '/' && next === '/') {
148
+ while (index < content.length && content[index] !== '\n')
149
+ index++;
150
+ output += '\n';
151
+ continue;
152
+ }
153
+ if (current === '/' && next === '*') {
154
+ index += 2;
155
+ while (index < content.length && !(content[index] === '*' && content[index + 1] === '/'))
156
+ index++;
157
+ index++;
158
+ continue;
159
+ }
160
+ output += current;
161
+ }
162
+ return removeTrailingJsonCommas(output);
163
+ }
164
+ function removeTrailingJsonCommas(content) {
165
+ let output = '';
166
+ let inString = false;
167
+ let escaped = false;
168
+ for (let index = 0; index < content.length; index++) {
169
+ const current = content[index];
170
+ if (inString) {
171
+ output += current;
172
+ if (escaped) {
173
+ escaped = false;
174
+ continue;
175
+ }
176
+ if (current === '\\') {
177
+ escaped = true;
178
+ continue;
179
+ }
180
+ if (current === '"')
181
+ inString = false;
182
+ continue;
183
+ }
184
+ if (current === '"') {
185
+ inString = true;
186
+ output += current;
187
+ continue;
188
+ }
189
+ if (current === ',') {
190
+ let nextIndex = index + 1;
191
+ while (/\s/.test(content[nextIndex] || ''))
192
+ nextIndex++;
193
+ if (content[nextIndex] === '}' || content[nextIndex] === ']')
194
+ continue;
195
+ }
196
+ output += current;
197
+ }
198
+ return output;
199
+ }
200
+ function escapeTomlString(value) {
201
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
202
+ }
203
+ function outputLimit(model) {
204
+ if (typeof model.maxOutputTokens === 'number' && model.maxOutputTokens > 0)
205
+ return model.maxOutputTokens;
206
+ if (model.id.toLowerCase().includes('haiku'))
207
+ return 8192;
208
+ return 32000;
209
+ }
210
+ function contextLimit(model) {
211
+ if (typeof model.maxInputTokens === 'number' && model.maxInputTokens > 0)
212
+ return model.maxInputTokens;
213
+ return 200000;
214
+ }
215
+ function inputModalities(model) {
216
+ const values = new Set(['text']);
217
+ for (const item of model.inputTypes ?? []) {
218
+ const lower = item.toLowerCase();
219
+ if (lower.includes('image'))
220
+ values.add('image');
221
+ if (lower.includes('pdf') || lower.includes('document') || lower.includes('file'))
222
+ values.add('pdf');
223
+ }
224
+ return Array.from(values);
225
+ }
226
+ function buildProxyOrigin(input) {
227
+ const host = input.host === '0.0.0.0' ? '127.0.0.1' : input.host === '::' ? '::1' : input.host;
228
+ const urlHost = host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
229
+ return `${input.tlsEnabled ? 'https' : 'http'}://${urlHost}:${input.port}`;
230
+ }
231
+ async function exists(path) {
232
+ return (0, promises_1.access)(path, fs_1.constants.F_OK).then(() => true, () => false);
233
+ }
234
+ async function backupIfExists(path) {
235
+ if (!(await exists(path)))
236
+ return [];
237
+ const backupPath = `${path}.kiro-backup-${Date.now()}`;
238
+ await (0, promises_1.copyFile)(path, backupPath);
239
+ return [backupPath];
240
+ }
241
+ async function readJsonObject(path) {
242
+ if (!(await exists(path)))
243
+ return {};
244
+ return parseJsonObject(await (0, promises_1.readFile)(path, 'utf-8'), path);
245
+ }
246
+ async function writeJsonObject(path, value) {
247
+ await (0, promises_1.mkdir)((0, path_1.dirname)(path), { recursive: true });
248
+ const backupPaths = await backupIfExists(path);
249
+ await (0, promises_1.writeFile)(path, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
250
+ return backupPaths;
251
+ }
252
+ async function writeText(path, value) {
253
+ await (0, promises_1.mkdir)((0, path_1.dirname)(path), { recursive: true });
254
+ const backupPaths = await backupIfExists(path);
255
+ await (0, promises_1.writeFile)(path, value.endsWith('\n') ? value : `${value}\n`, 'utf-8');
256
+ return backupPaths;
257
+ }
258
+ function getClaudeSettingsPath() {
259
+ const settingsPath = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
260
+ const legacyPath = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'claude.json');
261
+ return (0, fs_1.existsSync)(settingsPath) || !(0, fs_1.existsSync)(legacyPath) ? settingsPath : legacyPath;
262
+ }
263
+ function getOpenCodeConfigPath() {
264
+ const dir = (0, path_1.join)((0, os_1.homedir)(), '.config', 'opencode');
265
+ const candidates = [(0, path_1.join)(dir, 'opencode.jsonc'), (0, path_1.join)(dir, 'opencode.json'), (0, path_1.join)(dir, 'config.json')];
266
+ return candidates.find(path => (0, fs_1.existsSync)(path)) || candidates[1];
267
+ }
268
+ function getCodexAuthPath() {
269
+ return (0, path_1.join)((0, os_1.homedir)(), '.codex', 'auth.json');
270
+ }
271
+ function getCodexConfigPath() {
272
+ return (0, path_1.join)((0, os_1.homedir)(), '.codex', 'config.toml');
273
+ }
274
+ function ensureObjectField(target, key) {
275
+ if (!isRecord(target[key]))
276
+ target[key] = {};
277
+ return target[key];
278
+ }
279
+ async function configureClaudeCode(context) {
280
+ const path = getClaudeSettingsPath();
281
+ const config = await readJsonObject(path);
282
+ const env = ensureObjectField(config, 'env');
283
+ env.ANTHROPIC_BASE_URL = context.proxyOrigin;
284
+ env.ANTHROPIC_AUTH_TOKEN = context.apiKey;
285
+ env.ANTHROPIC_API_KEY = context.apiKey;
286
+ env.ANTHROPIC_MODEL = context.modelId;
287
+ // 默认模型映射:让 Claude Code 的 haiku/opus/sonnet 快捷调用都走代理支持的模型
288
+ const haikuModel = context.models.find(m => m.id.toLowerCase().includes('haiku'))?.id || 'claude-haiku-4.5';
289
+ const opusModel = context.models.find(m => m.id.toLowerCase().includes('opus'))?.id || context.modelId;
290
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL = haikuModel;
291
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL = opusModel;
292
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL = context.modelId;
293
+ return { paths: [path], backupPaths: await writeJsonObject(path, config) };
294
+ }
295
+ function openCodeModelConfig(model) {
296
+ const modalities = inputModalities(model);
297
+ return {
298
+ name: model.name || model.id,
299
+ attachment: modalities.some(item => item !== 'text'),
300
+ reasoning: false,
301
+ temperature: true,
302
+ tool_call: true,
303
+ limit: {
304
+ context: contextLimit(model),
305
+ output: outputLimit(model)
306
+ },
307
+ modalities: {
308
+ input: modalities,
309
+ output: ['text']
310
+ }
311
+ };
312
+ }
313
+ function openClawModelConfig(model) {
314
+ const input = inputModalities(model);
315
+ return {
316
+ id: model.id,
317
+ name: model.name || model.id,
318
+ reasoning: false,
319
+ input,
320
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
321
+ contextWindow: contextLimit(model),
322
+ contextTokens: contextLimit(model),
323
+ maxTokens: outputLimit(model)
324
+ };
325
+ }
326
+ async function configureOpenCode(context) {
327
+ const path = getOpenCodeConfigPath();
328
+ const config = await readJsonObject(path);
329
+ const provider = ensureObjectField(config, 'provider');
330
+ provider.kiro = {
331
+ npm: '@ai-sdk/openai-compatible',
332
+ name: 'Kiro Proxy',
333
+ options: {
334
+ baseURL: context.openaiBaseUrl,
335
+ apiKey: context.apiKey
336
+ },
337
+ models: Object.fromEntries(context.models.map(model => [model.id, openCodeModelConfig(model)]))
338
+ };
339
+ config.$schema = typeof config.$schema === 'string' ? config.$schema : 'https://opencode.ai/config.json';
340
+ config.model = `kiro/${context.modelId}`;
341
+ if (typeof config.small_model !== 'string' || config.small_model.startsWith('kiro/')) {
342
+ config.small_model = `kiro/${context.modelId}`;
343
+ }
344
+ if (Array.isArray(config.enabled_providers) && !config.enabled_providers.includes('kiro')) {
345
+ config.enabled_providers = [...config.enabled_providers, 'kiro'];
346
+ }
347
+ return { paths: [path], backupPaths: await writeJsonObject(path, config) };
348
+ }
349
+ function upsertRootTomlString(content, key, value) {
350
+ const newline = content.includes('\r\n') ? '\r\n' : '\n';
351
+ const lines = content.length === 0 ? [] : content.split(/\r?\n/);
352
+ const sectionIndex = lines.findIndex(line => /^\s*\[/.test(line));
353
+ const rootEnd = sectionIndex === -1 ? lines.length : sectionIndex;
354
+ const nextLines = [];
355
+ let written = false;
356
+ for (let index = 0; index < lines.length; index++) {
357
+ if (index < rootEnd && new RegExp(`^\\s*${key}\\s*=`).test(lines[index])) {
358
+ if (!written) {
359
+ nextLines.push(`${key} = "${escapeTomlString(value)}"`);
360
+ written = true;
361
+ }
362
+ continue;
363
+ }
364
+ if (!written && index === rootEnd) {
365
+ nextLines.push(`${key} = "${escapeTomlString(value)}"`);
366
+ written = true;
367
+ }
368
+ nextLines.push(lines[index]);
369
+ }
370
+ if (!written)
371
+ nextLines.push(`${key} = "${escapeTomlString(value)}"`);
372
+ return nextLines.join(newline);
373
+ }
374
+ function removeTomlSection(content, section) {
375
+ const newline = content.includes('\r\n') ? '\r\n' : '\n';
376
+ const lines = content.length === 0 ? [] : content.split(/\r?\n/);
377
+ const nextLines = [];
378
+ let skipping = false;
379
+ const escapedSection = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
380
+ for (const line of lines) {
381
+ if (new RegExp(`^\\s*\\[${escapedSection}\\]\\s*$`).test(line)) {
382
+ skipping = true;
383
+ continue;
384
+ }
385
+ if (skipping && /^\s*\[/.test(line))
386
+ skipping = false;
387
+ if (!skipping)
388
+ nextLines.push(line);
389
+ }
390
+ return nextLines.join(newline).trimEnd();
391
+ }
392
+ function upsertCodexConfig(content, context) {
393
+ const newline = content.includes('\r\n') ? '\r\n' : '\n';
394
+ const withProvider = upsertRootTomlString(upsertRootTomlString(content, 'model_provider', 'kiro'), 'model', context.modelId);
395
+ const withoutKiro = removeTomlSection(removeTomlSection(withProvider, 'model_providers.kiro'), 'model_providers."kiro"');
396
+ const separator = withoutKiro.trim() ? `${newline}${newline}` : '';
397
+ return `${withoutKiro.trimEnd()}${separator}[model_providers.kiro]${newline}name = "Kiro Proxy"${newline}base_url = "${escapeTomlString(context.openaiBaseUrl)}"${newline}wire_api = "responses"${newline}`;
398
+ }
399
+ async function configureCodex(context) {
400
+ const authPath = getCodexAuthPath();
401
+ const configPath = getCodexConfigPath();
402
+ const auth = await readJsonObject(authPath);
403
+ auth.OPENAI_API_KEY = context.apiKey;
404
+ const authBackups = await writeJsonObject(authPath, auth);
405
+ const config = (await exists(configPath)) ? await (0, promises_1.readFile)(configPath, 'utf-8') : '';
406
+ const configBackups = await writeText(configPath, upsertCodexConfig(config, context));
407
+ return { paths: [authPath, configPath], backupPaths: [...authBackups, ...configBackups] };
408
+ }
409
+ // Gemini CLI: ~/.gemini/.env + ~/.gemini/settings.json
410
+ function getGeminiEnvPath() {
411
+ return (0, path_1.join)((0, os_1.homedir)(), '.gemini', '.env');
412
+ }
413
+ function getGeminiSettingsPath() {
414
+ return (0, path_1.join)((0, os_1.homedir)(), '.gemini', 'settings.json');
415
+ }
416
+ function buildEnvContent(entries) {
417
+ return Object.entries(entries).map(([k, v]) => `${k}=${v}`).join('\n') + '\n';
418
+ }
419
+ function parseEnvFile(content) {
420
+ const result = {};
421
+ for (const line of content.split(/\r?\n/)) {
422
+ const trimmed = line.trim();
423
+ if (!trimmed || trimmed.startsWith('#'))
424
+ continue;
425
+ const idx = trimmed.indexOf('=');
426
+ if (idx > 0)
427
+ result[trimmed.substring(0, idx).trim()] = trimmed.substring(idx + 1).trim();
428
+ }
429
+ return result;
430
+ }
431
+ async function configureGemini(context) {
432
+ const envPath = getGeminiEnvPath();
433
+ const settingsPath = getGeminiSettingsPath();
434
+ const allPaths = [envPath, settingsPath];
435
+ const allBackups = [];
436
+ // .env
437
+ const existingEnv = (await exists(envPath)) ? parseEnvFile(await (0, promises_1.readFile)(envPath, 'utf-8')) : {};
438
+ existingEnv.GEMINI_API_KEY = context.apiKey;
439
+ existingEnv.GOOGLE_GEMINI_BASE_URL = `${context.proxyOrigin}/v1beta`;
440
+ existingEnv.GEMINI_MODEL = context.modelId;
441
+ allBackups.push(...await writeText(envPath, buildEnvContent(existingEnv)));
442
+ // settings.json
443
+ const settings = await readJsonObject(settingsPath);
444
+ const security = ensureObjectField(settings, 'security');
445
+ const auth = ensureObjectField(security, 'auth');
446
+ auth.selectedType = 'gemini-api-key';
447
+ allBackups.push(...await writeJsonObject(settingsPath, settings));
448
+ return { paths: allPaths, backupPaths: allBackups };
449
+ }
450
+ // Hermes: ~/.hermes/config.yaml
451
+ function getHermesConfigPath() {
452
+ return (0, path_1.join)((0, os_1.homedir)(), '.hermes', 'config.yaml');
453
+ }
454
+ async function configureHermes(context) {
455
+ const configPath = getHermesConfigPath();
456
+ const existing = (await exists(configPath)) ? await (0, promises_1.readFile)(configPath, 'utf-8') : '';
457
+ const newline = existing.includes('\r\n') ? '\r\n' : '\n';
458
+ // 构建 models dict
459
+ const modelsYaml = context.models.map(m => {
460
+ const ctx = typeof m.maxInputTokens === 'number' && m.maxInputTokens > 0 ? m.maxInputTokens : 200000;
461
+ return ` ${m.id}:${newline} context_length: ${ctx}`;
462
+ }).join(newline);
463
+ const providerBlock = [
464
+ ` - name: kiro`,
465
+ ` base_url: ${context.openaiBaseUrl}`,
466
+ ` api_key: ${context.apiKey}`,
467
+ ` model: ${context.modelId}`,
468
+ ` models:`,
469
+ modelsYaml
470
+ ].join(newline);
471
+ // 简单追加/替换 custom_providers 中的 kiro 条目
472
+ let content = existing;
473
+ const kiroProviderRegex = /^\s*- name:\s*kiro\b[\s\S]*?(?=^\s*- name:|^[a-z]|$)/gm;
474
+ if (kiroProviderRegex.test(content)) {
475
+ content = content.replace(kiroProviderRegex, providerBlock + newline);
476
+ }
477
+ else if (content.includes('custom_providers:')) {
478
+ content = content.replace(/(custom_providers:\s*)/, `$1${newline}${providerBlock}${newline}`);
479
+ }
480
+ else {
481
+ content = `${content.trimEnd()}${newline}${newline}custom_providers:${newline}${providerBlock}${newline}`;
482
+ }
483
+ // 更新 model section
484
+ const modelSection = `model:${newline} default: "kiro/${context.modelId}"${newline} provider: "kiro"${newline}`;
485
+ if (/^model:/m.test(content)) {
486
+ content = content.replace(/^model:.*(?:\n(?=\s).*)*$/m, modelSection.trimEnd());
487
+ }
488
+ else {
489
+ content = `${content.trimEnd()}${newline}${newline}${modelSection}`;
490
+ }
491
+ const backups = await writeText(configPath, content);
492
+ return { paths: [configPath], backupPaths: backups };
493
+ }
494
+ // OpenClaw: ~/.openclaw/openclaw.json
495
+ function getOpenClawConfigPath() {
496
+ return (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'openclaw.json');
497
+ }
498
+ async function configureOpenClaw(context) {
499
+ const configPath = getOpenClawConfigPath();
500
+ const config = await readJsonObject(configPath);
501
+ const providerId = 'krouter';
502
+ const legacyProviderId = ['kiro', 'manager'].join('-');
503
+ // models.providers.kiro
504
+ const models = ensureObjectField(config, 'models');
505
+ if (typeof models.mode !== 'string')
506
+ models.mode = 'merge';
507
+ const providers = ensureObjectField(models, 'providers');
508
+ if (isRecord(providers.kiro) && (providers.kiro.base_url || providers.kiro.api_key || providers.kiro.api === 'openai-chat')) {
509
+ delete providers.kiro;
510
+ }
511
+ const existingProvider = isRecord(providers[providerId])
512
+ ? providers[providerId]
513
+ : isRecord(providers[legacyProviderId])
514
+ ? providers[legacyProviderId]
515
+ : {};
516
+ const existingModels = openClawModelsToClientModels(existingProvider.models);
517
+ const openClawModels = dedupeClientModels([
518
+ ...existingModels,
519
+ ...buildOpenClawModels(context)
520
+ ]).filter(model => !(0, modelCatalog_1.isAutoKiroModelId)(model.id));
521
+ const primaryModelId = pickOpenClawPrimaryModel(context, openClawModels);
522
+ providers[providerId] = {
523
+ baseUrl: context.openaiBaseUrl,
524
+ apiKey: context.apiKey,
525
+ auth: 'api-key',
526
+ api: 'openai-completions',
527
+ models: openClawModels.map(openClawModelConfig)
528
+ };
529
+ delete providers[legacyProviderId];
530
+ // agents.defaults.model
531
+ const agents = ensureObjectField(config, 'agents');
532
+ const defaults = ensureObjectField(agents, 'defaults');
533
+ defaults.model = {
534
+ primary: `${providerId}/${primaryModelId}`,
535
+ fallbacks: buildOpenClawFallbackRefs(providerId, primaryModelId, openClawModels)
536
+ };
537
+ const defaultsModels = ensureObjectField(defaults, 'models');
538
+ for (const model of openClawModels) {
539
+ const ref = `${providerId}/${model.id}`;
540
+ defaultsModels[ref] = isRecord(defaultsModels[ref]) ? defaultsModels[ref] : {};
541
+ }
542
+ const backups = await writeJsonObject(configPath, config);
543
+ return { paths: [configPath], backupPaths: backups };
544
+ }
545
+ const ALL_CLIENT_TARGETS = ['claudeCode', 'opencode', 'codex', 'gemini', 'hermes', 'openclaw'];
546
+ async function configureClient(client, context) {
547
+ try {
548
+ const result = client === 'claudeCode' ? await configureClaudeCode(context)
549
+ : client === 'opencode' ? await configureOpenCode(context)
550
+ : client === 'codex' ? await configureCodex(context)
551
+ : client === 'gemini' ? await configureGemini(context)
552
+ : client === 'hermes' ? await configureHermes(context)
553
+ : await configureOpenClaw(context);
554
+ return { client, success: true, ...result };
555
+ }
556
+ catch (error) {
557
+ return { client, success: false, paths: [], backupPaths: [], error: error instanceof Error ? error.message : 'Unknown error' };
558
+ }
559
+ }
560
+ async function configureProxyClients(input) {
561
+ const modelId = input.modelId.trim();
562
+ const apiKey = input.apiKey?.trim();
563
+ if (!Array.isArray(input.clients))
564
+ throw new Error('Client targets are required');
565
+ const clients = Array.from(new Set(input.clients));
566
+ if (!modelId)
567
+ throw new Error('Model is required');
568
+ if (!apiKey)
569
+ throw new Error('API Key is required');
570
+ if (clients.length === 0)
571
+ throw new Error('At least one client is required');
572
+ if (clients.some(client => !ALL_CLIENT_TARGETS.includes(client)))
573
+ throw new Error('Unsupported client target');
574
+ const proxyOrigin = buildProxyOrigin(input);
575
+ const modelMap = new Map((input.models?.length ? input.models : [{ id: modelId, name: input.modelName || modelId }]).map(model => [model.id, model]));
576
+ if (!modelMap.has(modelId))
577
+ modelMap.set(modelId, { id: modelId, name: input.modelName || modelId });
578
+ const context = {
579
+ proxyOrigin,
580
+ openaiBaseUrl: `${proxyOrigin.replace(/\/$/, '')}/v1`,
581
+ apiKey,
582
+ modelId,
583
+ models: Array.from(modelMap.values())
584
+ };
585
+ const results = [];
586
+ for (const client of clients) {
587
+ results.push(await configureClient(client, context));
588
+ }
589
+ return {
590
+ success: results.every(result => result.success),
591
+ proxyOrigin,
592
+ openaiBaseUrl: context.openaiBaseUrl,
593
+ apiKey: { key: apiKey },
594
+ results
595
+ };
596
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ProxyServer = void 0;
18
+ // Kiro Proxy 模块导出
19
+ __exportStar(require("./types"), exports);
20
+ __exportStar(require("./accountPool"), exports);
21
+ __exportStar(require("./kiroApi"), exports);
22
+ __exportStar(require("./translator"), exports);
23
+ __exportStar(require("./clientConfig"), exports);
24
+ var proxyServer_1 = require("./proxyServer");
25
+ Object.defineProperty(exports, "ProxyServer", { enumerable: true, get: function () { return proxyServer_1.ProxyServer; } });