@javargasm/opencode-kiro-auth 0.1.2 → 0.2.1
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/file-logger.d.ts +47 -0
- package/dist/file-logger.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1353 -490
- package/dist/kiro-cli-sync.d.ts +58 -4
- package/dist/kiro-cli-sync.d.ts.map +1 -1
- package/dist/models.d.ts +9 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/oauth.d.ts +32 -2
- package/dist/oauth.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/stream.d.ts +0 -16
- package/dist/stream.d.ts.map +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -117,13 +117,430 @@ var init_debug = __esm(() => {
|
|
|
117
117
|
};
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
// src/models.ts
|
|
121
|
+
var exports_models = {};
|
|
122
|
+
__export(exports_models, {
|
|
123
|
+
setCachedDynamicModels: () => setCachedDynamicModels,
|
|
124
|
+
seedProfileArn: () => seedProfileArn,
|
|
125
|
+
resolveProfileArn: () => resolveProfileArn,
|
|
126
|
+
resolveKiroModel: () => resolveKiroModel,
|
|
127
|
+
resolveApiRegion: () => resolveApiRegion,
|
|
128
|
+
resetProfileArnCache: () => resetProfileArnCache,
|
|
129
|
+
kiroModels: () => kiroModels,
|
|
130
|
+
getCachedDynamicModels: () => getCachedDynamicModels,
|
|
131
|
+
fetchAvailableModels: () => fetchAvailableModels,
|
|
132
|
+
dotToDash: () => dotToDash,
|
|
133
|
+
buildModelsFromApi: () => buildModelsFromApi,
|
|
134
|
+
KIRO_MODEL_IDS: () => KIRO_MODEL_IDS
|
|
135
|
+
});
|
|
136
|
+
function dotToDash(modelId) {
|
|
137
|
+
return modelId.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
138
|
+
}
|
|
139
|
+
function resolveKiroModel(modelId) {
|
|
140
|
+
const kiroId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
141
|
+
if (!KIRO_MODEL_IDS.has(kiroId)) {
|
|
142
|
+
throw new Error(`Unknown Kiro model ID: ${modelId}`);
|
|
143
|
+
}
|
|
144
|
+
return kiroId;
|
|
145
|
+
}
|
|
146
|
+
function resolveApiRegion(ssoRegion) {
|
|
147
|
+
if (!ssoRegion)
|
|
148
|
+
return "us-east-1";
|
|
149
|
+
return API_REGION_MAP[ssoRegion] ?? ssoRegion;
|
|
150
|
+
}
|
|
151
|
+
function resetProfileArnCache(skipResolution = false) {
|
|
152
|
+
cachedProfileArn = null;
|
|
153
|
+
profileArnSkipResolution = skipResolution;
|
|
154
|
+
}
|
|
155
|
+
function seedProfileArn(arn) {
|
|
156
|
+
cachedProfileArn = arn;
|
|
157
|
+
}
|
|
158
|
+
async function resolveProfileArn(accessToken, apiRegion) {
|
|
159
|
+
if (profileArnSkipResolution)
|
|
160
|
+
return null;
|
|
161
|
+
if (cachedProfileArn !== null)
|
|
162
|
+
return cachedProfileArn;
|
|
163
|
+
const endpoint = `https://management.${apiRegion}.kiro.dev/`;
|
|
164
|
+
const resp = await fetch(endpoint, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
Authorization: `Bearer ${accessToken}`,
|
|
168
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
169
|
+
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableProfiles",
|
|
170
|
+
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.7.1 app/AmazonQ-For-CLI"
|
|
171
|
+
},
|
|
172
|
+
body: "{}"
|
|
173
|
+
});
|
|
174
|
+
if (!resp || !resp.ok)
|
|
175
|
+
return null;
|
|
176
|
+
const data = await resp.json();
|
|
177
|
+
const profiles = data.profiles ?? [];
|
|
178
|
+
const kiroProfile = profiles.find((p) => p.profileType === "KIRO" && p.status === "ACTIVE");
|
|
179
|
+
const arn = kiroProfile?.arn ?? profiles[0]?.arn ?? null;
|
|
180
|
+
if (arn) {
|
|
181
|
+
cachedProfileArn = arn;
|
|
182
|
+
}
|
|
183
|
+
return arn;
|
|
184
|
+
}
|
|
185
|
+
async function fetchAvailableModels(accessToken, apiRegion, profileArn) {
|
|
186
|
+
const url = `https://management.${apiRegion}.kiro.dev/?origin=KIRO_CLI&profileArn=${encodeURIComponent(profileArn)}`;
|
|
187
|
+
const resp = await fetch(url, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
Authorization: `Bearer ${accessToken}`,
|
|
191
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
192
|
+
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableModels",
|
|
193
|
+
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.7.1 app/AmazonQ-For-CLI"
|
|
194
|
+
},
|
|
195
|
+
body: "{}"
|
|
196
|
+
});
|
|
197
|
+
if (!resp.ok) {
|
|
198
|
+
throw new Error(`ListAvailableModels failed: HTTP ${resp.status}`);
|
|
199
|
+
}
|
|
200
|
+
const data = await resp.json();
|
|
201
|
+
return (data.models ?? []).filter((m) => m.modelId !== "auto");
|
|
202
|
+
}
|
|
203
|
+
function isReasoningModel(dotId) {
|
|
204
|
+
for (const family of REASONING_FAMILIES) {
|
|
205
|
+
if (dotId.startsWith(family))
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function firstTokenTimeout(dotId) {
|
|
211
|
+
if (dotId.startsWith("claude-fable") || dotId.startsWith("claude-opus"))
|
|
212
|
+
return 180000;
|
|
213
|
+
return 90000;
|
|
214
|
+
}
|
|
215
|
+
function buildModelsFromApi(apiModels) {
|
|
216
|
+
return apiModels.map((m) => {
|
|
217
|
+
KIRO_MODEL_IDS.add(m.modelId);
|
|
218
|
+
const dashId = dotToDash(m.modelId);
|
|
219
|
+
const supportedTypes = m.supportedInputTypes ?? ["TEXT"];
|
|
220
|
+
const input = supportedTypes.includes("IMAGE") ? ["text", "image"] : ["text"];
|
|
221
|
+
const effortEnum = m.additionalModelRequestFieldsSchema?.properties?.output_config?.properties?.effort?.enum;
|
|
222
|
+
const supportedEfforts = Array.isArray(effortEnum) && effortEnum.length > 0 ? effortEnum : undefined;
|
|
223
|
+
const supportsThinkingConfig = !!m.additionalModelRequestFieldsSchema?.properties?.thinking;
|
|
224
|
+
return {
|
|
225
|
+
...KIRO_DEFAULTS,
|
|
226
|
+
id: dashId,
|
|
227
|
+
name: m.modelName,
|
|
228
|
+
reasoning: isReasoningModel(m.modelId),
|
|
229
|
+
input,
|
|
230
|
+
contextWindow: m.tokenLimits?.maxInputTokens ?? 200000,
|
|
231
|
+
maxTokens: m.tokenLimits?.maxOutputTokens ?? 8192,
|
|
232
|
+
firstTokenTimeout: firstTokenTimeout(m.modelId),
|
|
233
|
+
...supportedEfforts ? { supportedEfforts } : {},
|
|
234
|
+
...supportsThinkingConfig ? { supportsThinkingConfig } : {}
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function getCachedDynamicModels() {
|
|
239
|
+
return cachedDynamicModels;
|
|
240
|
+
}
|
|
241
|
+
function setCachedDynamicModels(models) {
|
|
242
|
+
cachedDynamicModels = models;
|
|
243
|
+
}
|
|
244
|
+
var KIRO_MODEL_IDS, API_REGION_MAP, BASE_URL = "https://runtime.us-east-1.kiro.dev", ZERO_COST, KIRO_DEFAULTS, MULTIMODAL, TEXT_ONLY, kiroModels, cachedProfileArn = null, profileArnSkipResolution = false, REASONING_FAMILIES, cachedDynamicModels = null;
|
|
245
|
+
var init_models = __esm(() => {
|
|
246
|
+
KIRO_MODEL_IDS = new Set([
|
|
247
|
+
"claude-fable-5",
|
|
248
|
+
"claude-opus-4.8",
|
|
249
|
+
"claude-opus-4.7",
|
|
250
|
+
"claude-opus-4.6",
|
|
251
|
+
"claude-opus-4.6-1m",
|
|
252
|
+
"claude-sonnet-4.6",
|
|
253
|
+
"claude-sonnet-4.6-1m",
|
|
254
|
+
"claude-opus-4.5",
|
|
255
|
+
"claude-sonnet-4.5",
|
|
256
|
+
"claude-sonnet-4.5-1m",
|
|
257
|
+
"claude-sonnet-4",
|
|
258
|
+
"claude-haiku-4.5",
|
|
259
|
+
"deepseek-3.2",
|
|
260
|
+
"kimi-k2.5",
|
|
261
|
+
"minimax-m2.1",
|
|
262
|
+
"minimax-m2.5",
|
|
263
|
+
"glm-4.7",
|
|
264
|
+
"glm-4.7-flash",
|
|
265
|
+
"qwen3-coder-next",
|
|
266
|
+
"agi-nova-beta-1m",
|
|
267
|
+
"qwen3-coder-480b",
|
|
268
|
+
"auto"
|
|
269
|
+
]);
|
|
270
|
+
API_REGION_MAP = {
|
|
271
|
+
"us-west-1": "us-east-1",
|
|
272
|
+
"us-west-2": "us-east-1",
|
|
273
|
+
"us-east-2": "us-east-1",
|
|
274
|
+
"eu-west-1": "eu-central-1",
|
|
275
|
+
"eu-west-2": "eu-central-1",
|
|
276
|
+
"eu-west-3": "eu-central-1",
|
|
277
|
+
"eu-north-1": "eu-central-1",
|
|
278
|
+
"eu-south-1": "eu-central-1",
|
|
279
|
+
"eu-south-2": "eu-central-1",
|
|
280
|
+
"eu-central-2": "eu-central-1",
|
|
281
|
+
"ap-northeast-1": "us-east-1",
|
|
282
|
+
"ap-northeast-2": "us-east-1",
|
|
283
|
+
"ap-northeast-3": "us-east-1",
|
|
284
|
+
"ap-southeast-1": "us-east-1",
|
|
285
|
+
"ap-southeast-2": "us-east-1",
|
|
286
|
+
"ap-south-1": "us-east-1",
|
|
287
|
+
"ap-east-1": "us-east-1",
|
|
288
|
+
"ap-south-2": "us-east-1",
|
|
289
|
+
"ap-southeast-3": "us-east-1",
|
|
290
|
+
"ap-southeast-4": "us-east-1"
|
|
291
|
+
};
|
|
292
|
+
ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
293
|
+
KIRO_DEFAULTS = {
|
|
294
|
+
api: "kiro-api",
|
|
295
|
+
provider: "kiro",
|
|
296
|
+
baseUrl: BASE_URL,
|
|
297
|
+
cost: ZERO_COST
|
|
298
|
+
};
|
|
299
|
+
MULTIMODAL = ["text", "image"];
|
|
300
|
+
TEXT_ONLY = ["text"];
|
|
301
|
+
kiroModels = [
|
|
302
|
+
{
|
|
303
|
+
...KIRO_DEFAULTS,
|
|
304
|
+
id: "claude-fable-5",
|
|
305
|
+
name: "Claude Fable 5",
|
|
306
|
+
reasoning: true,
|
|
307
|
+
input: MULTIMODAL,
|
|
308
|
+
contextWindow: 1e6,
|
|
309
|
+
maxTokens: 128000,
|
|
310
|
+
firstTokenTimeout: 180000,
|
|
311
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
312
|
+
supportsThinkingConfig: true
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
...KIRO_DEFAULTS,
|
|
316
|
+
id: "claude-opus-4-8",
|
|
317
|
+
name: "Claude Opus 4.8",
|
|
318
|
+
reasoning: true,
|
|
319
|
+
input: MULTIMODAL,
|
|
320
|
+
contextWindow: 1e6,
|
|
321
|
+
maxTokens: 128000,
|
|
322
|
+
firstTokenTimeout: 180000,
|
|
323
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
324
|
+
supportsThinkingConfig: true
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
...KIRO_DEFAULTS,
|
|
328
|
+
id: "claude-opus-4-7",
|
|
329
|
+
name: "Claude Opus 4.7",
|
|
330
|
+
reasoning: true,
|
|
331
|
+
input: MULTIMODAL,
|
|
332
|
+
contextWindow: 1e6,
|
|
333
|
+
maxTokens: 128000,
|
|
334
|
+
firstTokenTimeout: 180000,
|
|
335
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
336
|
+
supportsThinkingConfig: true
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
...KIRO_DEFAULTS,
|
|
340
|
+
id: "claude-opus-4-6",
|
|
341
|
+
name: "Claude Opus 4.6",
|
|
342
|
+
reasoning: true,
|
|
343
|
+
input: MULTIMODAL,
|
|
344
|
+
contextWindow: 1e6,
|
|
345
|
+
maxTokens: 64000,
|
|
346
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
347
|
+
supportsThinkingConfig: true
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
...KIRO_DEFAULTS,
|
|
351
|
+
id: "claude-opus-4-6-1m",
|
|
352
|
+
name: "Claude Opus 4.6 (1M)",
|
|
353
|
+
reasoning: true,
|
|
354
|
+
input: MULTIMODAL,
|
|
355
|
+
contextWindow: 1e6,
|
|
356
|
+
maxTokens: 64000,
|
|
357
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
358
|
+
supportsThinkingConfig: true
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
...KIRO_DEFAULTS,
|
|
362
|
+
id: "claude-sonnet-4-6",
|
|
363
|
+
name: "Claude Sonnet 4.6",
|
|
364
|
+
reasoning: true,
|
|
365
|
+
input: MULTIMODAL,
|
|
366
|
+
contextWindow: 1e6,
|
|
367
|
+
maxTokens: 64000,
|
|
368
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
369
|
+
supportsThinkingConfig: true
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
...KIRO_DEFAULTS,
|
|
373
|
+
id: "claude-sonnet-4-6-1m",
|
|
374
|
+
name: "Claude Sonnet 4.6 (1M)",
|
|
375
|
+
reasoning: true,
|
|
376
|
+
input: MULTIMODAL,
|
|
377
|
+
contextWindow: 1e6,
|
|
378
|
+
maxTokens: 64000,
|
|
379
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
380
|
+
supportsThinkingConfig: true
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
...KIRO_DEFAULTS,
|
|
384
|
+
id: "claude-opus-4-5",
|
|
385
|
+
name: "Claude Opus 4.5",
|
|
386
|
+
reasoning: true,
|
|
387
|
+
input: MULTIMODAL,
|
|
388
|
+
contextWindow: 200000,
|
|
389
|
+
maxTokens: 64000
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
...KIRO_DEFAULTS,
|
|
393
|
+
id: "claude-sonnet-4-5",
|
|
394
|
+
name: "Claude Sonnet 4.5",
|
|
395
|
+
reasoning: true,
|
|
396
|
+
input: MULTIMODAL,
|
|
397
|
+
contextWindow: 200000,
|
|
398
|
+
maxTokens: 65536
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
...KIRO_DEFAULTS,
|
|
402
|
+
id: "claude-sonnet-4-5-1m",
|
|
403
|
+
name: "Claude Sonnet 4.5 (1M)",
|
|
404
|
+
reasoning: true,
|
|
405
|
+
input: MULTIMODAL,
|
|
406
|
+
contextWindow: 1e6,
|
|
407
|
+
maxTokens: 65536
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
...KIRO_DEFAULTS,
|
|
411
|
+
id: "claude-sonnet-4",
|
|
412
|
+
name: "Claude Sonnet 4",
|
|
413
|
+
reasoning: true,
|
|
414
|
+
input: MULTIMODAL,
|
|
415
|
+
contextWindow: 200000,
|
|
416
|
+
maxTokens: 65536
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
...KIRO_DEFAULTS,
|
|
420
|
+
id: "claude-haiku-4-5",
|
|
421
|
+
name: "Claude Haiku 4.5",
|
|
422
|
+
reasoning: false,
|
|
423
|
+
input: MULTIMODAL,
|
|
424
|
+
contextWindow: 200000,
|
|
425
|
+
maxTokens: 65536
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
...KIRO_DEFAULTS,
|
|
429
|
+
id: "deepseek-3-2",
|
|
430
|
+
name: "DeepSeek 3.2",
|
|
431
|
+
reasoning: true,
|
|
432
|
+
input: TEXT_ONLY,
|
|
433
|
+
contextWindow: 128000,
|
|
434
|
+
maxTokens: 8192
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
...KIRO_DEFAULTS,
|
|
438
|
+
id: "kimi-k2-5",
|
|
439
|
+
name: "Kimi K2.5",
|
|
440
|
+
reasoning: true,
|
|
441
|
+
input: TEXT_ONLY,
|
|
442
|
+
contextWindow: 200000,
|
|
443
|
+
maxTokens: 8192
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
...KIRO_DEFAULTS,
|
|
447
|
+
id: "minimax-m2-5",
|
|
448
|
+
name: "MiniMax M2.5",
|
|
449
|
+
reasoning: false,
|
|
450
|
+
input: TEXT_ONLY,
|
|
451
|
+
contextWindow: 196000,
|
|
452
|
+
maxTokens: 64000
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
...KIRO_DEFAULTS,
|
|
456
|
+
id: "minimax-m2-1",
|
|
457
|
+
name: "MiniMax M2.1",
|
|
458
|
+
reasoning: false,
|
|
459
|
+
input: MULTIMODAL,
|
|
460
|
+
contextWindow: 196000,
|
|
461
|
+
maxTokens: 64000
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
...KIRO_DEFAULTS,
|
|
465
|
+
id: "glm-4-7",
|
|
466
|
+
name: "GLM 4.7",
|
|
467
|
+
reasoning: true,
|
|
468
|
+
input: TEXT_ONLY,
|
|
469
|
+
contextWindow: 128000,
|
|
470
|
+
maxTokens: 8192
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
...KIRO_DEFAULTS,
|
|
474
|
+
id: "glm-4-7-flash",
|
|
475
|
+
name: "GLM 4.7 Flash",
|
|
476
|
+
reasoning: false,
|
|
477
|
+
input: TEXT_ONLY,
|
|
478
|
+
contextWindow: 128000,
|
|
479
|
+
maxTokens: 8192
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
...KIRO_DEFAULTS,
|
|
483
|
+
id: "qwen3-coder-next",
|
|
484
|
+
name: "Qwen3 Coder Next",
|
|
485
|
+
reasoning: true,
|
|
486
|
+
input: MULTIMODAL,
|
|
487
|
+
contextWindow: 256000,
|
|
488
|
+
maxTokens: 64000
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
...KIRO_DEFAULTS,
|
|
492
|
+
id: "qwen3-coder-480b",
|
|
493
|
+
name: "Qwen3 Coder 480B",
|
|
494
|
+
reasoning: true,
|
|
495
|
+
input: TEXT_ONLY,
|
|
496
|
+
contextWindow: 128000,
|
|
497
|
+
maxTokens: 8192
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
...KIRO_DEFAULTS,
|
|
501
|
+
id: "agi-nova-beta-1m",
|
|
502
|
+
name: "AGI Nova Beta (1M)",
|
|
503
|
+
reasoning: true,
|
|
504
|
+
input: MULTIMODAL,
|
|
505
|
+
contextWindow: 1e6,
|
|
506
|
+
maxTokens: 65536
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
...KIRO_DEFAULTS,
|
|
510
|
+
id: "auto",
|
|
511
|
+
name: "Auto",
|
|
512
|
+
reasoning: true,
|
|
513
|
+
input: MULTIMODAL,
|
|
514
|
+
contextWindow: 200000,
|
|
515
|
+
maxTokens: 65536
|
|
516
|
+
}
|
|
517
|
+
];
|
|
518
|
+
REASONING_FAMILIES = new Set([
|
|
519
|
+
"claude-fable",
|
|
520
|
+
"claude-sonnet",
|
|
521
|
+
"claude-opus",
|
|
522
|
+
"deepseek",
|
|
523
|
+
"kimi",
|
|
524
|
+
"glm",
|
|
525
|
+
"qwen",
|
|
526
|
+
"agi-nova",
|
|
527
|
+
"minimax"
|
|
528
|
+
]);
|
|
529
|
+
});
|
|
530
|
+
|
|
120
531
|
// src/kiro-cli-sync.ts
|
|
121
532
|
var exports_kiro_cli_sync = {};
|
|
122
533
|
__export(exports_kiro_cli_sync, {
|
|
123
|
-
|
|
534
|
+
selectKiroTokenRowForWrite: () => selectKiroTokenRowForWrite,
|
|
535
|
+
saveKiroCliCredentials: () => saveKiroCliCredentials,
|
|
536
|
+
sameKiroCliCredential: () => sameKiroCliCredential,
|
|
537
|
+
importFromKiroSsoCache: () => importFromKiroSsoCache,
|
|
538
|
+
importFromKiroCli: () => importFromKiroCli,
|
|
539
|
+
getKiroCliCredentialsAllowExpired: () => getKiroCliCredentialsAllowExpired
|
|
124
540
|
});
|
|
125
541
|
import { homedir } from "node:os";
|
|
126
542
|
import { join } from "node:path";
|
|
543
|
+
import { spawn } from "node:child_process";
|
|
127
544
|
import { existsSync, readFileSync } from "node:fs";
|
|
128
545
|
function getKiroDbPath() {
|
|
129
546
|
const home = homedir();
|
|
@@ -155,6 +572,105 @@ function safeJsonParse(value) {
|
|
|
155
572
|
return null;
|
|
156
573
|
}
|
|
157
574
|
}
|
|
575
|
+
function sqliteQuote(value) {
|
|
576
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
577
|
+
}
|
|
578
|
+
function runSqliteCli(dbPath, sql, options) {
|
|
579
|
+
return new Promise((resolve2) => {
|
|
580
|
+
const args = [
|
|
581
|
+
...options.readonly ? ["-readonly"] : [],
|
|
582
|
+
"-cmd",
|
|
583
|
+
`.timeout ${SQLITE_CLI_TIMEOUT_MS}`,
|
|
584
|
+
...options.json ? ["-json"] : [],
|
|
585
|
+
dbPath
|
|
586
|
+
];
|
|
587
|
+
const child = spawn("sqlite3", args, { stdio: "pipe" });
|
|
588
|
+
let stdout = "";
|
|
589
|
+
let settled = false;
|
|
590
|
+
const finish = (result) => {
|
|
591
|
+
if (settled)
|
|
592
|
+
return;
|
|
593
|
+
settled = true;
|
|
594
|
+
clearTimeout(timeout);
|
|
595
|
+
resolve2(result);
|
|
596
|
+
};
|
|
597
|
+
const timeout = setTimeout(() => {
|
|
598
|
+
child.kill();
|
|
599
|
+
log.debug("sqlite3 CLI timed out while reading Kiro DB");
|
|
600
|
+
finish(null);
|
|
601
|
+
}, SQLITE_CLI_TIMEOUT_MS);
|
|
602
|
+
child.stdout?.setEncoding("utf8");
|
|
603
|
+
child.stdout?.on("data", (chunk) => {
|
|
604
|
+
stdout += chunk;
|
|
605
|
+
});
|
|
606
|
+
child.on("error", () => {
|
|
607
|
+
log.debug("sqlite3 CLI unavailable for Kiro DB access");
|
|
608
|
+
finish(null);
|
|
609
|
+
});
|
|
610
|
+
child.on("close", (code) => {
|
|
611
|
+
if (code === 0)
|
|
612
|
+
finish(stdout);
|
|
613
|
+
else {
|
|
614
|
+
log.debug("sqlite3 CLI failed while accessing Kiro DB");
|
|
615
|
+
finish(null);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
child.stdin?.end(`${sql}
|
|
619
|
+
`);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function parseAuthKvRows(value) {
|
|
623
|
+
if (!Array.isArray(value))
|
|
624
|
+
return null;
|
|
625
|
+
const rows = [];
|
|
626
|
+
for (const item of value) {
|
|
627
|
+
if (!item || typeof item !== "object")
|
|
628
|
+
continue;
|
|
629
|
+
const record = item;
|
|
630
|
+
if (typeof record.key === "string" && typeof record.value === "string") {
|
|
631
|
+
rows.push({ key: record.key, value: record.value });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return rows;
|
|
635
|
+
}
|
|
636
|
+
function extractActiveProfileArnFromStateRows(value) {
|
|
637
|
+
if (!Array.isArray(value))
|
|
638
|
+
return;
|
|
639
|
+
const first = value[0];
|
|
640
|
+
if (!first || typeof first !== "object")
|
|
641
|
+
return;
|
|
642
|
+
const record = first;
|
|
643
|
+
const parsed = safeJsonParse(record.value);
|
|
644
|
+
const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
|
|
645
|
+
return typeof arn === "string" && arn.trim() ? arn.trim() : undefined;
|
|
646
|
+
}
|
|
647
|
+
function parseSqliteChanges(value) {
|
|
648
|
+
if (!Array.isArray(value))
|
|
649
|
+
return 0;
|
|
650
|
+
const first = value[0];
|
|
651
|
+
if (!first || typeof first !== "object")
|
|
652
|
+
return 0;
|
|
653
|
+
const changes = first.changes;
|
|
654
|
+
return typeof changes === "number" ? changes : 0;
|
|
655
|
+
}
|
|
656
|
+
async function readKiroAuthKvRowsWithSqliteCli(dbPath) {
|
|
657
|
+
const rowsRaw = await runSqliteCli(dbPath, "SELECT key, value FROM auth_kv", { readonly: true, json: true });
|
|
658
|
+
if (!rowsRaw)
|
|
659
|
+
return null;
|
|
660
|
+
return parseAuthKvRows(safeJsonParse(rowsRaw));
|
|
661
|
+
}
|
|
662
|
+
async function readKiroDbWithSqliteCli(dbPath) {
|
|
663
|
+
const rows = await readKiroAuthKvRowsWithSqliteCli(dbPath);
|
|
664
|
+
if (!rows)
|
|
665
|
+
return null;
|
|
666
|
+
const stateRaw = await runSqliteCli(dbPath, "SELECT value FROM state WHERE key = 'api.codewhisperer.profile'", { readonly: true, json: true });
|
|
667
|
+
const activeProfileArn = stateRaw ? extractActiveProfileArnFromStateRows(safeJsonParse(stateRaw)) : undefined;
|
|
668
|
+
return { rows, activeProfileArn };
|
|
669
|
+
}
|
|
670
|
+
async function writeKiroDbWithSqliteCli(dbPath, tokenKey, updatedValue) {
|
|
671
|
+
const resultRaw = await runSqliteCli(dbPath, `UPDATE auth_kv SET value = ${sqliteQuote(updatedValue)} WHERE key = ${sqliteQuote(tokenKey)}; ` + "SELECT changes() AS changes", { readonly: false, json: true });
|
|
672
|
+
return parseSqliteChanges(safeJsonParse(resultRaw)) > 0;
|
|
673
|
+
}
|
|
158
674
|
function findClientCreds(obj) {
|
|
159
675
|
if (!obj || typeof obj !== "object")
|
|
160
676
|
return {};
|
|
@@ -173,6 +689,23 @@ function findClientCreds(obj) {
|
|
|
173
689
|
function isIdcTokenKey(key) {
|
|
174
690
|
return key.includes("odic") || key.includes("oidc") || key.includes("idc");
|
|
175
691
|
}
|
|
692
|
+
function isTokenRow(row) {
|
|
693
|
+
return row.key.includes(":token");
|
|
694
|
+
}
|
|
695
|
+
function tokenReadRank(row) {
|
|
696
|
+
return isIdcTokenKey(row.key) ? 0 : 1;
|
|
697
|
+
}
|
|
698
|
+
function selectKiroTokenRowForWrite(rows, creds) {
|
|
699
|
+
const tokenRows = rows.filter(isTokenRow);
|
|
700
|
+
if (!creds.tokenKey)
|
|
701
|
+
return;
|
|
702
|
+
return tokenRows.find((row) => row.key === creds.tokenKey);
|
|
703
|
+
}
|
|
704
|
+
function sameKiroCliCredential(left, right) {
|
|
705
|
+
if (!left || !right)
|
|
706
|
+
return false;
|
|
707
|
+
return left.source === right.source && left.tokenKey === right.tokenKey && left.accessToken === right.accessToken && left.refreshToken === right.refreshToken && left.region === right.region && left.authMethod === right.authMethod;
|
|
708
|
+
}
|
|
176
709
|
function extractRegionFromArn(arn) {
|
|
177
710
|
if (!arn)
|
|
178
711
|
return;
|
|
@@ -189,37 +722,56 @@ async function importFromKiroDb() {
|
|
|
189
722
|
return null;
|
|
190
723
|
}
|
|
191
724
|
try {
|
|
192
|
-
const { Database } = await import("bun:sqlite");
|
|
193
|
-
const db = new Database(dbPath, { readonly: true });
|
|
194
|
-
try {
|
|
195
|
-
db.exec("PRAGMA busy_timeout = 5000");
|
|
196
|
-
} catch {}
|
|
197
725
|
let rows;
|
|
726
|
+
let activeProfileArn;
|
|
727
|
+
let Database = null;
|
|
198
728
|
try {
|
|
199
|
-
|
|
729
|
+
Database = (await import("bun:sqlite")).Database;
|
|
200
730
|
} catch {
|
|
201
|
-
|
|
731
|
+
try {
|
|
732
|
+
Database = (await import("better-sqlite3")).default;
|
|
733
|
+
} catch {
|
|
734
|
+
log.debug("No SQLite driver available (need bun:sqlite or better-sqlite3); trying sqlite3 CLI");
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (Database) {
|
|
738
|
+
const db = new Database(dbPath, { readonly: true });
|
|
739
|
+
try {
|
|
740
|
+
db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
|
|
741
|
+
} catch {}
|
|
742
|
+
try {
|
|
743
|
+
const stmt = db.prepare("SELECT key, value FROM auth_kv");
|
|
744
|
+
rows = stmt.all();
|
|
745
|
+
} catch {
|
|
746
|
+
log.debug("Failed to read auth_kv table from Kiro DB");
|
|
747
|
+
try {
|
|
748
|
+
db.close();
|
|
749
|
+
} catch {}
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
const stateStmt = db.prepare("SELECT value FROM state WHERE key = ?");
|
|
754
|
+
const stateRow = stateStmt.get("api.codewhisperer.profile");
|
|
755
|
+
const parsed = safeJsonParse(stateRow?.value);
|
|
756
|
+
const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
|
|
757
|
+
if (typeof arn === "string" && arn.trim()) {
|
|
758
|
+
activeProfileArn = arn.trim();
|
|
759
|
+
}
|
|
760
|
+
} catch {}
|
|
202
761
|
try {
|
|
203
762
|
db.close();
|
|
204
763
|
} catch {}
|
|
205
|
-
|
|
764
|
+
} else {
|
|
765
|
+
const snapshot = await readKiroDbWithSqliteCli(dbPath);
|
|
766
|
+
if (!snapshot)
|
|
767
|
+
return null;
|
|
768
|
+
rows = snapshot.rows;
|
|
769
|
+
activeProfileArn = snapshot.activeProfileArn;
|
|
206
770
|
}
|
|
207
|
-
let activeProfileArn;
|
|
208
|
-
try {
|
|
209
|
-
const stateRow = db.prepare("SELECT value FROM state WHERE key = ?").get("api.codewhisperer.profile");
|
|
210
|
-
const parsed = safeJsonParse(stateRow?.value);
|
|
211
|
-
const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
|
|
212
|
-
if (typeof arn === "string" && arn.trim()) {
|
|
213
|
-
activeProfileArn = arn.trim();
|
|
214
|
-
}
|
|
215
|
-
} catch {}
|
|
216
|
-
try {
|
|
217
|
-
db.close();
|
|
218
|
-
} catch {}
|
|
219
771
|
const deviceRegRow = rows.find((r) => typeof r?.key === "string" && r.key.includes("device-registration"));
|
|
220
772
|
const deviceReg = safeJsonParse(deviceRegRow?.value);
|
|
221
773
|
const regCreds = deviceReg ? findClientCreds(deviceReg) : {};
|
|
222
|
-
const tokenRows = rows.filter(
|
|
774
|
+
const tokenRows = rows.filter(isTokenRow).sort((a, b) => tokenReadRank(a) - tokenReadRank(b));
|
|
223
775
|
for (const row of tokenRows) {
|
|
224
776
|
const data = safeJsonParse(row.value);
|
|
225
777
|
if (!data)
|
|
@@ -232,16 +784,19 @@ async function importFromKiroDb() {
|
|
|
232
784
|
const authMethod = isIdc ? "idc" : "desktop";
|
|
233
785
|
const oidcRegion = data.region || "us-east-1";
|
|
234
786
|
let profileArn = data.profile_arn || data.profileArn;
|
|
235
|
-
if (!profileArn)
|
|
787
|
+
if (!profileArn && isIdc) {
|
|
236
788
|
profileArn = activeProfileArn;
|
|
789
|
+
}
|
|
237
790
|
const serviceRegion = extractRegionFromArn(profileArn) || oidcRegion;
|
|
238
791
|
const result = {
|
|
239
792
|
accessToken: accessToken || "",
|
|
240
793
|
refreshToken: refreshToken || "",
|
|
241
794
|
region: serviceRegion,
|
|
242
795
|
authMethod,
|
|
796
|
+
profileArn,
|
|
243
797
|
email: data.email || data.emailAddress,
|
|
244
|
-
|
|
798
|
+
source: "kiro-cli-db",
|
|
799
|
+
tokenKey: row.key
|
|
245
800
|
};
|
|
246
801
|
if (isIdc && regCreds.clientId) {
|
|
247
802
|
result.clientId = regCreds.clientId;
|
|
@@ -257,30 +812,48 @@ async function importFromKiroDb() {
|
|
|
257
812
|
return null;
|
|
258
813
|
}
|
|
259
814
|
}
|
|
815
|
+
function mapSsoCacheAuthMethod(value) {
|
|
816
|
+
if (typeof value !== "string")
|
|
817
|
+
return "idc";
|
|
818
|
+
const v = value.toLowerCase();
|
|
819
|
+
if (v === "builderid" || v === "builder-id")
|
|
820
|
+
return "desktop";
|
|
821
|
+
return "idc";
|
|
822
|
+
}
|
|
260
823
|
async function importFromKiroSsoCache() {
|
|
261
|
-
const
|
|
262
|
-
if (!existsSync(
|
|
263
|
-
log.debug(`Kiro SSO cache not found at ${
|
|
824
|
+
const path = getKiroSsoCachePath();
|
|
825
|
+
if (!existsSync(path)) {
|
|
826
|
+
log.debug(`Kiro SSO cache not found at ${path}`);
|
|
264
827
|
return null;
|
|
265
828
|
}
|
|
266
829
|
let raw;
|
|
267
830
|
try {
|
|
268
|
-
raw = readFileSync(
|
|
831
|
+
raw = readFileSync(path, "utf8");
|
|
269
832
|
} catch (err) {
|
|
270
|
-
log.warn(`Failed to read Kiro SSO cache at ${
|
|
833
|
+
log.warn(`Failed to read Kiro SSO cache at ${path}: ${err}`);
|
|
271
834
|
return null;
|
|
272
835
|
}
|
|
273
836
|
const token = safeJsonParse(raw);
|
|
274
|
-
if (!token || typeof token !== "object")
|
|
837
|
+
if (!token || typeof token !== "object") {
|
|
838
|
+
log.debug(`Kiro SSO cache at ${path} is not valid JSON`);
|
|
275
839
|
return null;
|
|
840
|
+
}
|
|
276
841
|
const accessToken = typeof token.accessToken === "string" ? token.accessToken : "";
|
|
277
842
|
const refreshToken = typeof token.refreshToken === "string" ? token.refreshToken : "";
|
|
278
|
-
if (!accessToken && !refreshToken)
|
|
843
|
+
if (!accessToken && !refreshToken) {
|
|
844
|
+
log.debug(`Kiro SSO cache at ${path} has no tokens`);
|
|
279
845
|
return null;
|
|
846
|
+
}
|
|
280
847
|
const region = typeof token.region === "string" && token.region.length > 0 ? token.region : "us-east-1";
|
|
281
|
-
const authMethod =
|
|
282
|
-
log.info(`Imported Kiro SSO cache credentials (region=${region})`);
|
|
283
|
-
return {
|
|
848
|
+
const authMethod = mapSsoCacheAuthMethod(token.authMethod);
|
|
849
|
+
log.info(`Imported Kiro SSO cache credentials (method=${authMethod}, region=${region})`);
|
|
850
|
+
return {
|
|
851
|
+
accessToken,
|
|
852
|
+
refreshToken,
|
|
853
|
+
region,
|
|
854
|
+
authMethod,
|
|
855
|
+
source: "kiro-sso-cache"
|
|
856
|
+
};
|
|
284
857
|
}
|
|
285
858
|
async function importFromKiroCli() {
|
|
286
859
|
const dbResult = await importFromKiroDb();
|
|
@@ -288,6 +861,98 @@ async function importFromKiroCli() {
|
|
|
288
861
|
return dbResult;
|
|
289
862
|
return importFromKiroSsoCache();
|
|
290
863
|
}
|
|
864
|
+
async function getKiroCliCredentialsAllowExpired(exclude) {
|
|
865
|
+
const imported = await importFromKiroCli();
|
|
866
|
+
return sameKiroCliCredential(imported, exclude ?? null) ? null : imported;
|
|
867
|
+
}
|
|
868
|
+
async function saveKiroCliCredentials(creds) {
|
|
869
|
+
if (creds.source !== "kiro-cli-db" || !creds.tokenKey) {
|
|
870
|
+
log.debug("Credential write-back skipped: credential did not originate from kiro-cli DB");
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
const dbPath = getKiroDbPath();
|
|
874
|
+
if (!existsSync(dbPath)) {
|
|
875
|
+
log.debug(`Kiro CLI DB not found at ${dbPath} — cannot save credentials`);
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
try {
|
|
879
|
+
let Database = null;
|
|
880
|
+
try {
|
|
881
|
+
Database = (await import("bun:sqlite")).Database;
|
|
882
|
+
} catch {
|
|
883
|
+
try {
|
|
884
|
+
Database = (await import("better-sqlite3")).default;
|
|
885
|
+
} catch {
|
|
886
|
+
log.debug("No SQLite driver available for credential write-back; trying sqlite3 CLI");
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
let rows;
|
|
890
|
+
let db = null;
|
|
891
|
+
if (Database) {
|
|
892
|
+
db = new Database(dbPath);
|
|
893
|
+
try {
|
|
894
|
+
db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
|
|
895
|
+
} catch {}
|
|
896
|
+
try {
|
|
897
|
+
const stmt = db.prepare("SELECT key, value FROM auth_kv");
|
|
898
|
+
rows = stmt.all();
|
|
899
|
+
} catch {
|
|
900
|
+
log.debug("Failed to read auth_kv table for credential write-back");
|
|
901
|
+
try {
|
|
902
|
+
db.close();
|
|
903
|
+
} catch {}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
} else {
|
|
907
|
+
const cliRows = await readKiroAuthKvRowsWithSqliteCli(dbPath);
|
|
908
|
+
if (!cliRows)
|
|
909
|
+
return false;
|
|
910
|
+
rows = cliRows;
|
|
911
|
+
}
|
|
912
|
+
const tokenRow = selectKiroTokenRowForWrite(rows, creds);
|
|
913
|
+
if (!tokenRow) {
|
|
914
|
+
log.debug("No matching token entry found in auth_kv — cannot write back");
|
|
915
|
+
try {
|
|
916
|
+
db?.close();
|
|
917
|
+
} catch {}
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
const existing = safeJsonParse(tokenRow.value) ?? {};
|
|
921
|
+
const updated = {
|
|
922
|
+
...existing,
|
|
923
|
+
accessToken: creds.accessToken,
|
|
924
|
+
access_token: creds.accessToken,
|
|
925
|
+
refreshToken: creds.refreshToken,
|
|
926
|
+
refresh_token: creds.refreshToken
|
|
927
|
+
};
|
|
928
|
+
const updatedValue = JSON.stringify(updated);
|
|
929
|
+
if (db) {
|
|
930
|
+
try {
|
|
931
|
+
const updateStmt = db.prepare("UPDATE auth_kv SET value = ? WHERE key = ?");
|
|
932
|
+
updateStmt.run(updatedValue, tokenRow.key);
|
|
933
|
+
} catch (err) {
|
|
934
|
+
log.warn(`Failed to write credentials back to Kiro CLI DB: ${err}`);
|
|
935
|
+
try {
|
|
936
|
+
db.close();
|
|
937
|
+
} catch {}
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
db.close();
|
|
942
|
+
} catch {}
|
|
943
|
+
} else {
|
|
944
|
+
const wrote = await writeKiroDbWithSqliteCli(dbPath, tokenRow.key, updatedValue);
|
|
945
|
+
if (!wrote)
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
log.info("Wrote refreshed credentials back to Kiro CLI DB");
|
|
949
|
+
return true;
|
|
950
|
+
} catch (err) {
|
|
951
|
+
log.warn(`Failed to save credentials to Kiro CLI: ${err}`);
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
var SQLITE_CLI_TIMEOUT_MS = 5000;
|
|
291
956
|
var init_kiro_cli_sync = __esm(() => {
|
|
292
957
|
init_debug();
|
|
293
958
|
});
|
|
@@ -616,372 +1281,8 @@ function isPermanentError(reason) {
|
|
|
616
1281
|
return PERMANENT_PATTERNS.some((p) => reason.includes(p));
|
|
617
1282
|
}
|
|
618
1283
|
|
|
619
|
-
// src/
|
|
620
|
-
|
|
621
|
-
"claude-fable-5",
|
|
622
|
-
"claude-opus-4.8",
|
|
623
|
-
"claude-opus-4.7",
|
|
624
|
-
"claude-opus-4.6",
|
|
625
|
-
"claude-opus-4.6-1m",
|
|
626
|
-
"claude-sonnet-4.6",
|
|
627
|
-
"claude-sonnet-4.6-1m",
|
|
628
|
-
"claude-opus-4.5",
|
|
629
|
-
"claude-sonnet-4.5",
|
|
630
|
-
"claude-sonnet-4.5-1m",
|
|
631
|
-
"claude-sonnet-4",
|
|
632
|
-
"claude-haiku-4.5",
|
|
633
|
-
"deepseek-3.2",
|
|
634
|
-
"kimi-k2.5",
|
|
635
|
-
"minimax-m2.1",
|
|
636
|
-
"minimax-m2.5",
|
|
637
|
-
"glm-4.7",
|
|
638
|
-
"glm-4.7-flash",
|
|
639
|
-
"qwen3-coder-next",
|
|
640
|
-
"agi-nova-beta-1m",
|
|
641
|
-
"qwen3-coder-480b",
|
|
642
|
-
"auto"
|
|
643
|
-
]);
|
|
644
|
-
function dotToDash(modelId) {
|
|
645
|
-
return modelId.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
646
|
-
}
|
|
647
|
-
function resolveKiroModel(modelId) {
|
|
648
|
-
const kiroId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
649
|
-
if (!KIRO_MODEL_IDS.has(kiroId)) {
|
|
650
|
-
throw new Error(`Unknown Kiro model ID: ${modelId}`);
|
|
651
|
-
}
|
|
652
|
-
return kiroId;
|
|
653
|
-
}
|
|
654
|
-
var API_REGION_MAP = {
|
|
655
|
-
"us-west-1": "us-east-1",
|
|
656
|
-
"us-west-2": "us-east-1",
|
|
657
|
-
"us-east-2": "us-east-1",
|
|
658
|
-
"eu-west-1": "eu-central-1",
|
|
659
|
-
"eu-west-2": "eu-central-1",
|
|
660
|
-
"eu-west-3": "eu-central-1",
|
|
661
|
-
"eu-north-1": "eu-central-1",
|
|
662
|
-
"eu-south-1": "eu-central-1",
|
|
663
|
-
"eu-south-2": "eu-central-1",
|
|
664
|
-
"eu-central-2": "eu-central-1",
|
|
665
|
-
"ap-northeast-1": "us-east-1",
|
|
666
|
-
"ap-northeast-2": "us-east-1",
|
|
667
|
-
"ap-northeast-3": "us-east-1",
|
|
668
|
-
"ap-southeast-1": "us-east-1",
|
|
669
|
-
"ap-southeast-2": "us-east-1",
|
|
670
|
-
"ap-south-1": "us-east-1",
|
|
671
|
-
"ap-east-1": "us-east-1",
|
|
672
|
-
"ap-south-2": "us-east-1",
|
|
673
|
-
"ap-southeast-3": "us-east-1",
|
|
674
|
-
"ap-southeast-4": "us-east-1"
|
|
675
|
-
};
|
|
676
|
-
function resolveApiRegion(ssoRegion) {
|
|
677
|
-
if (!ssoRegion)
|
|
678
|
-
return "us-east-1";
|
|
679
|
-
return API_REGION_MAP[ssoRegion] ?? ssoRegion;
|
|
680
|
-
}
|
|
681
|
-
var BASE_URL = "https://runtime.us-east-1.kiro.dev";
|
|
682
|
-
var ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
683
|
-
var KIRO_DEFAULTS = {
|
|
684
|
-
api: "kiro-api",
|
|
685
|
-
provider: "kiro",
|
|
686
|
-
baseUrl: BASE_URL,
|
|
687
|
-
cost: ZERO_COST
|
|
688
|
-
};
|
|
689
|
-
var MULTIMODAL = ["text", "image"];
|
|
690
|
-
var TEXT_ONLY = ["text"];
|
|
691
|
-
var kiroModels = [
|
|
692
|
-
{
|
|
693
|
-
...KIRO_DEFAULTS,
|
|
694
|
-
id: "claude-fable-5",
|
|
695
|
-
name: "Claude Fable 5",
|
|
696
|
-
reasoning: true,
|
|
697
|
-
input: MULTIMODAL,
|
|
698
|
-
contextWindow: 1e6,
|
|
699
|
-
maxTokens: 128000,
|
|
700
|
-
firstTokenTimeout: 180000,
|
|
701
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
702
|
-
supportsThinkingConfig: true
|
|
703
|
-
},
|
|
704
|
-
{
|
|
705
|
-
...KIRO_DEFAULTS,
|
|
706
|
-
id: "claude-opus-4-8",
|
|
707
|
-
name: "Claude Opus 4.8",
|
|
708
|
-
reasoning: true,
|
|
709
|
-
input: MULTIMODAL,
|
|
710
|
-
contextWindow: 1e6,
|
|
711
|
-
maxTokens: 128000,
|
|
712
|
-
firstTokenTimeout: 180000,
|
|
713
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
714
|
-
supportsThinkingConfig: true
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
...KIRO_DEFAULTS,
|
|
718
|
-
id: "claude-opus-4-7",
|
|
719
|
-
name: "Claude Opus 4.7",
|
|
720
|
-
reasoning: true,
|
|
721
|
-
input: MULTIMODAL,
|
|
722
|
-
contextWindow: 1e6,
|
|
723
|
-
maxTokens: 128000,
|
|
724
|
-
firstTokenTimeout: 180000,
|
|
725
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
726
|
-
supportsThinkingConfig: true
|
|
727
|
-
},
|
|
728
|
-
{
|
|
729
|
-
...KIRO_DEFAULTS,
|
|
730
|
-
id: "claude-opus-4-6",
|
|
731
|
-
name: "Claude Opus 4.6",
|
|
732
|
-
reasoning: true,
|
|
733
|
-
input: MULTIMODAL,
|
|
734
|
-
contextWindow: 1e6,
|
|
735
|
-
maxTokens: 64000,
|
|
736
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
737
|
-
supportsThinkingConfig: true
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
...KIRO_DEFAULTS,
|
|
741
|
-
id: "claude-opus-4-6-1m",
|
|
742
|
-
name: "Claude Opus 4.6 (1M)",
|
|
743
|
-
reasoning: true,
|
|
744
|
-
input: MULTIMODAL,
|
|
745
|
-
contextWindow: 1e6,
|
|
746
|
-
maxTokens: 64000,
|
|
747
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
748
|
-
supportsThinkingConfig: true
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
...KIRO_DEFAULTS,
|
|
752
|
-
id: "claude-sonnet-4-6",
|
|
753
|
-
name: "Claude Sonnet 4.6",
|
|
754
|
-
reasoning: true,
|
|
755
|
-
input: MULTIMODAL,
|
|
756
|
-
contextWindow: 1e6,
|
|
757
|
-
maxTokens: 64000,
|
|
758
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
759
|
-
supportsThinkingConfig: true
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
...KIRO_DEFAULTS,
|
|
763
|
-
id: "claude-sonnet-4-6-1m",
|
|
764
|
-
name: "Claude Sonnet 4.6 (1M)",
|
|
765
|
-
reasoning: true,
|
|
766
|
-
input: MULTIMODAL,
|
|
767
|
-
contextWindow: 1e6,
|
|
768
|
-
maxTokens: 64000,
|
|
769
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
770
|
-
supportsThinkingConfig: true
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
...KIRO_DEFAULTS,
|
|
774
|
-
id: "claude-opus-4-5",
|
|
775
|
-
name: "Claude Opus 4.5",
|
|
776
|
-
reasoning: true,
|
|
777
|
-
input: MULTIMODAL,
|
|
778
|
-
contextWindow: 200000,
|
|
779
|
-
maxTokens: 64000
|
|
780
|
-
},
|
|
781
|
-
{
|
|
782
|
-
...KIRO_DEFAULTS,
|
|
783
|
-
id: "claude-sonnet-4-5",
|
|
784
|
-
name: "Claude Sonnet 4.5",
|
|
785
|
-
reasoning: true,
|
|
786
|
-
input: MULTIMODAL,
|
|
787
|
-
contextWindow: 200000,
|
|
788
|
-
maxTokens: 65536
|
|
789
|
-
},
|
|
790
|
-
{
|
|
791
|
-
...KIRO_DEFAULTS,
|
|
792
|
-
id: "claude-sonnet-4-5-1m",
|
|
793
|
-
name: "Claude Sonnet 4.5 (1M)",
|
|
794
|
-
reasoning: true,
|
|
795
|
-
input: MULTIMODAL,
|
|
796
|
-
contextWindow: 1e6,
|
|
797
|
-
maxTokens: 65536
|
|
798
|
-
},
|
|
799
|
-
{
|
|
800
|
-
...KIRO_DEFAULTS,
|
|
801
|
-
id: "claude-sonnet-4",
|
|
802
|
-
name: "Claude Sonnet 4",
|
|
803
|
-
reasoning: true,
|
|
804
|
-
input: MULTIMODAL,
|
|
805
|
-
contextWindow: 200000,
|
|
806
|
-
maxTokens: 65536
|
|
807
|
-
},
|
|
808
|
-
{
|
|
809
|
-
...KIRO_DEFAULTS,
|
|
810
|
-
id: "claude-haiku-4-5",
|
|
811
|
-
name: "Claude Haiku 4.5",
|
|
812
|
-
reasoning: false,
|
|
813
|
-
input: MULTIMODAL,
|
|
814
|
-
contextWindow: 200000,
|
|
815
|
-
maxTokens: 65536
|
|
816
|
-
},
|
|
817
|
-
{
|
|
818
|
-
...KIRO_DEFAULTS,
|
|
819
|
-
id: "deepseek-3-2",
|
|
820
|
-
name: "DeepSeek 3.2",
|
|
821
|
-
reasoning: true,
|
|
822
|
-
input: TEXT_ONLY,
|
|
823
|
-
contextWindow: 128000,
|
|
824
|
-
maxTokens: 8192
|
|
825
|
-
},
|
|
826
|
-
{
|
|
827
|
-
...KIRO_DEFAULTS,
|
|
828
|
-
id: "kimi-k2-5",
|
|
829
|
-
name: "Kimi K2.5",
|
|
830
|
-
reasoning: true,
|
|
831
|
-
input: TEXT_ONLY,
|
|
832
|
-
contextWindow: 200000,
|
|
833
|
-
maxTokens: 8192
|
|
834
|
-
},
|
|
835
|
-
{
|
|
836
|
-
...KIRO_DEFAULTS,
|
|
837
|
-
id: "minimax-m2-5",
|
|
838
|
-
name: "MiniMax M2.5",
|
|
839
|
-
reasoning: false,
|
|
840
|
-
input: TEXT_ONLY,
|
|
841
|
-
contextWindow: 196000,
|
|
842
|
-
maxTokens: 64000
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
...KIRO_DEFAULTS,
|
|
846
|
-
id: "minimax-m2-1",
|
|
847
|
-
name: "MiniMax M2.1",
|
|
848
|
-
reasoning: false,
|
|
849
|
-
input: MULTIMODAL,
|
|
850
|
-
contextWindow: 196000,
|
|
851
|
-
maxTokens: 64000
|
|
852
|
-
},
|
|
853
|
-
{
|
|
854
|
-
...KIRO_DEFAULTS,
|
|
855
|
-
id: "glm-4-7",
|
|
856
|
-
name: "GLM 4.7",
|
|
857
|
-
reasoning: true,
|
|
858
|
-
input: TEXT_ONLY,
|
|
859
|
-
contextWindow: 128000,
|
|
860
|
-
maxTokens: 8192
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
...KIRO_DEFAULTS,
|
|
864
|
-
id: "glm-4-7-flash",
|
|
865
|
-
name: "GLM 4.7 Flash",
|
|
866
|
-
reasoning: false,
|
|
867
|
-
input: TEXT_ONLY,
|
|
868
|
-
contextWindow: 128000,
|
|
869
|
-
maxTokens: 8192
|
|
870
|
-
},
|
|
871
|
-
{
|
|
872
|
-
...KIRO_DEFAULTS,
|
|
873
|
-
id: "qwen3-coder-next",
|
|
874
|
-
name: "Qwen3 Coder Next",
|
|
875
|
-
reasoning: true,
|
|
876
|
-
input: MULTIMODAL,
|
|
877
|
-
contextWindow: 256000,
|
|
878
|
-
maxTokens: 64000
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
...KIRO_DEFAULTS,
|
|
882
|
-
id: "qwen3-coder-480b",
|
|
883
|
-
name: "Qwen3 Coder 480B",
|
|
884
|
-
reasoning: true,
|
|
885
|
-
input: TEXT_ONLY,
|
|
886
|
-
contextWindow: 128000,
|
|
887
|
-
maxTokens: 8192
|
|
888
|
-
},
|
|
889
|
-
{
|
|
890
|
-
...KIRO_DEFAULTS,
|
|
891
|
-
id: "agi-nova-beta-1m",
|
|
892
|
-
name: "AGI Nova Beta (1M)",
|
|
893
|
-
reasoning: true,
|
|
894
|
-
input: MULTIMODAL,
|
|
895
|
-
contextWindow: 1e6,
|
|
896
|
-
maxTokens: 65536
|
|
897
|
-
},
|
|
898
|
-
{
|
|
899
|
-
...KIRO_DEFAULTS,
|
|
900
|
-
id: "auto",
|
|
901
|
-
name: "Auto",
|
|
902
|
-
reasoning: true,
|
|
903
|
-
input: MULTIMODAL,
|
|
904
|
-
contextWindow: 200000,
|
|
905
|
-
maxTokens: 65536
|
|
906
|
-
}
|
|
907
|
-
];
|
|
908
|
-
async function fetchAvailableModels(accessToken, apiRegion, fallbackProfileArn) {
|
|
909
|
-
const runtimeUrl = `https://runtime.${apiRegion}.kiro.dev/`;
|
|
910
|
-
let profileArn = await resolveProfileArn(accessToken, runtimeUrl);
|
|
911
|
-
if (!profileArn && fallbackProfileArn) {
|
|
912
|
-
profileArn = fallbackProfileArn;
|
|
913
|
-
}
|
|
914
|
-
if (!profileArn) {
|
|
915
|
-
throw new Error("Missing profileArn: cannot fetch available models.");
|
|
916
|
-
}
|
|
917
|
-
const url = `https://management.${apiRegion}.kiro.dev/ListAvailableModels?origin=KIRO_CLI&profileArn=${encodeURIComponent(profileArn)}`;
|
|
918
|
-
const resp = await fetch(url, {
|
|
919
|
-
method: "GET",
|
|
920
|
-
headers: {
|
|
921
|
-
Authorization: `Bearer ${accessToken}`,
|
|
922
|
-
Accept: "application/json",
|
|
923
|
-
"User-Agent": "opencode-kiro"
|
|
924
|
-
}
|
|
925
|
-
});
|
|
926
|
-
if (!resp.ok) {
|
|
927
|
-
throw new Error(`ListAvailableModels failed: HTTP ${resp.status}`);
|
|
928
|
-
}
|
|
929
|
-
const data = await resp.json();
|
|
930
|
-
return (data.models ?? []).filter((m) => m.modelId !== "auto");
|
|
931
|
-
}
|
|
932
|
-
var REASONING_FAMILIES = new Set([
|
|
933
|
-
"claude-fable",
|
|
934
|
-
"claude-sonnet",
|
|
935
|
-
"claude-opus",
|
|
936
|
-
"deepseek",
|
|
937
|
-
"kimi",
|
|
938
|
-
"glm",
|
|
939
|
-
"qwen",
|
|
940
|
-
"agi-nova",
|
|
941
|
-
"minimax"
|
|
942
|
-
]);
|
|
943
|
-
function isReasoningModel(dotId) {
|
|
944
|
-
for (const family of REASONING_FAMILIES) {
|
|
945
|
-
if (dotId.startsWith(family))
|
|
946
|
-
return true;
|
|
947
|
-
}
|
|
948
|
-
return false;
|
|
949
|
-
}
|
|
950
|
-
function firstTokenTimeout(dotId) {
|
|
951
|
-
if (dotId.startsWith("claude-fable") || dotId.startsWith("claude-opus"))
|
|
952
|
-
return 180000;
|
|
953
|
-
return 90000;
|
|
954
|
-
}
|
|
955
|
-
function buildModelsFromApi(apiModels) {
|
|
956
|
-
return apiModels.map((m) => {
|
|
957
|
-
KIRO_MODEL_IDS.add(m.modelId);
|
|
958
|
-
const dashId = dotToDash(m.modelId);
|
|
959
|
-
const supportedTypes = m.supportedInputTypes ?? ["TEXT"];
|
|
960
|
-
const input = supportedTypes.includes("IMAGE") ? ["text", "image"] : ["text"];
|
|
961
|
-
const effortEnum = m.additionalModelRequestFieldsSchema?.properties?.output_config?.properties?.effort?.enum;
|
|
962
|
-
const supportedEfforts = Array.isArray(effortEnum) && effortEnum.length > 0 ? effortEnum : undefined;
|
|
963
|
-
const supportsThinkingConfig = !!m.additionalModelRequestFieldsSchema?.properties?.thinking;
|
|
964
|
-
return {
|
|
965
|
-
...KIRO_DEFAULTS,
|
|
966
|
-
id: dashId,
|
|
967
|
-
name: m.modelName,
|
|
968
|
-
reasoning: isReasoningModel(m.modelId),
|
|
969
|
-
input,
|
|
970
|
-
contextWindow: m.tokenLimits?.maxInputTokens ?? 200000,
|
|
971
|
-
maxTokens: m.tokenLimits?.maxOutputTokens ?? 8192,
|
|
972
|
-
firstTokenTimeout: firstTokenTimeout(m.modelId),
|
|
973
|
-
...supportedEfforts ? { supportedEfforts } : {},
|
|
974
|
-
...supportsThinkingConfig ? { supportsThinkingConfig } : {}
|
|
975
|
-
};
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
var cachedDynamicModels = null;
|
|
979
|
-
function getCachedDynamicModels() {
|
|
980
|
-
return cachedDynamicModels;
|
|
981
|
-
}
|
|
982
|
-
function setCachedDynamicModels(models) {
|
|
983
|
-
cachedDynamicModels = models;
|
|
984
|
-
}
|
|
1284
|
+
// src/stream.ts
|
|
1285
|
+
init_models();
|
|
985
1286
|
|
|
986
1287
|
// src/thinking-parser.ts
|
|
987
1288
|
init_debug();
|
|
@@ -1218,7 +1519,9 @@ function countTokens(text) {
|
|
|
1218
1519
|
|
|
1219
1520
|
// src/oauth.ts
|
|
1220
1521
|
init_debug();
|
|
1221
|
-
|
|
1522
|
+
init_models();
|
|
1523
|
+
import { createServer } from "node:http";
|
|
1524
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
1222
1525
|
var BUILDER_ID_REGION = "us-east-1";
|
|
1223
1526
|
var SSO_SCOPES = [
|
|
1224
1527
|
"codewhisperer:completions",
|
|
@@ -1337,10 +1640,12 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1337
1640
|
const refreshToken = parts[0] ?? "";
|
|
1338
1641
|
const clientId = parts[1] ?? "";
|
|
1339
1642
|
const clientSecret = parts[2] ?? "";
|
|
1643
|
+
const source = parts[4] ?? "";
|
|
1644
|
+
const tokenKey = parts[5] ?? "";
|
|
1340
1645
|
if (!refreshToken || !region) {
|
|
1341
1646
|
throw new Error("Refresh token or region is missing — re-login required");
|
|
1342
1647
|
}
|
|
1343
|
-
if (authMethod === "desktop") {
|
|
1648
|
+
if (authMethod === "desktop" || authMethod === "social") {
|
|
1344
1649
|
const desktopEndpoint = `https://prod.${region}.auth.desktop.kiro.dev/refreshToken`;
|
|
1345
1650
|
const resp2 = await fetch(desktopEndpoint, {
|
|
1346
1651
|
method: "POST",
|
|
@@ -1352,9 +1657,23 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1352
1657
|
throw new Error(`Desktop token refresh failed: ${resp2.status} ${body}`);
|
|
1353
1658
|
}
|
|
1354
1659
|
const data2 = await resp2.json();
|
|
1660
|
+
const newPacked2 = `${data2.refreshToken}|||${authMethod}|${source}|${tokenKey}`;
|
|
1661
|
+
if (source === "kiro-cli-db" && tokenKey) {
|
|
1662
|
+
const { saveKiroCliCredentials: saveKiroCliCredentials2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1663
|
+
await saveKiroCliCredentials2({
|
|
1664
|
+
accessToken: data2.accessToken,
|
|
1665
|
+
refreshToken: data2.refreshToken,
|
|
1666
|
+
clientId: "",
|
|
1667
|
+
clientSecret: "",
|
|
1668
|
+
region,
|
|
1669
|
+
authMethod,
|
|
1670
|
+
source: "kiro-cli-db",
|
|
1671
|
+
tokenKey
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1355
1674
|
return {
|
|
1356
1675
|
access: data2.accessToken,
|
|
1357
|
-
refresh:
|
|
1676
|
+
refresh: newPacked2,
|
|
1358
1677
|
expires: Date.now() + (data2.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
1359
1678
|
};
|
|
1360
1679
|
}
|
|
@@ -1372,15 +1691,489 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1372
1691
|
throw new Error(`Token refresh failed: ${resp.status} ${body}`);
|
|
1373
1692
|
}
|
|
1374
1693
|
const data = await resp.json();
|
|
1694
|
+
const newPacked = `${data.refreshToken}|${clientId}|${clientSecret}|${authMethod}|${source}|${tokenKey}`;
|
|
1695
|
+
if (source === "kiro-cli-db" && tokenKey) {
|
|
1696
|
+
const { saveKiroCliCredentials: saveKiroCliCredentials2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1697
|
+
await saveKiroCliCredentials2({
|
|
1698
|
+
accessToken: data.accessToken,
|
|
1699
|
+
refreshToken: data.refreshToken,
|
|
1700
|
+
clientId,
|
|
1701
|
+
clientSecret,
|
|
1702
|
+
region,
|
|
1703
|
+
authMethod,
|
|
1704
|
+
source: "kiro-cli-db",
|
|
1705
|
+
tokenKey
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1375
1708
|
return {
|
|
1376
1709
|
access: data.accessToken,
|
|
1377
|
-
refresh:
|
|
1710
|
+
refresh: newPacked,
|
|
1378
1711
|
expires: Date.now() + (data.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
1379
1712
|
};
|
|
1380
1713
|
}
|
|
1714
|
+
var KIRO_SOCIAL_PORTAL = "https://app.kiro.dev";
|
|
1715
|
+
var KIRO_SOCIAL_AUTH_ENDPOINT = `https://prod.${BUILDER_ID_REGION}.auth.desktop.kiro.dev`;
|
|
1716
|
+
var SOCIAL_REDIRECT_PORT = 49153;
|
|
1717
|
+
var SOCIAL_REDIRECT_URI = `http://localhost:${SOCIAL_REDIRECT_PORT}`;
|
|
1718
|
+
function generateRandomState() {
|
|
1719
|
+
return randomBytes(16).toString("base64url");
|
|
1720
|
+
}
|
|
1721
|
+
function generateCodeVerifier() {
|
|
1722
|
+
return randomBytes(32).toString("base64url");
|
|
1723
|
+
}
|
|
1724
|
+
function generateCodeChallenge(verifier) {
|
|
1725
|
+
return createHash("sha256").update(verifier).digest("base64url");
|
|
1726
|
+
}
|
|
1727
|
+
function buildSocialSignInURL(redirectUri, codeChallenge, state) {
|
|
1728
|
+
const params = new URLSearchParams;
|
|
1729
|
+
params.set("code_challenge", codeChallenge);
|
|
1730
|
+
params.set("code_challenge_method", "S256");
|
|
1731
|
+
params.set("redirect_from", "kirocli");
|
|
1732
|
+
params.set("redirect_uri", redirectUri);
|
|
1733
|
+
params.set("state", state);
|
|
1734
|
+
return `${KIRO_SOCIAL_PORTAL}/signin?${params.toString()}`;
|
|
1735
|
+
}
|
|
1736
|
+
function buildTokenRedirectUri(callbackPath, loginOption) {
|
|
1737
|
+
const path = callbackPath || "/oauth/callback";
|
|
1738
|
+
const base = `${SOCIAL_REDIRECT_URI}${path}`;
|
|
1739
|
+
if (loginOption) {
|
|
1740
|
+
return `${base}?login_option=${encodeURIComponent(loginOption)}`;
|
|
1741
|
+
}
|
|
1742
|
+
return base;
|
|
1743
|
+
}
|
|
1744
|
+
function oauthCallbackPage(kind, title, message, redirectUrl = "https://app.kiro.dev") {
|
|
1745
|
+
const borderColor = kind === "success" ? "#22c55e" : "#ef4444";
|
|
1746
|
+
const iconSvg = kind === "success" ? `<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M16.67 5L7.5 14.17 3.33 10" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>` : `<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M15 5L5 15M5 5l10 10" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg>`;
|
|
1747
|
+
const ghostSvg = `<svg width="56" height="56" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1748
|
+
<path d="M50 5C28.5 5 11 22.5 11 44v36c0 2.8 1.2 5.4 3.2 7.2 2 1.8 4.7 2.6 7.4 2.2l3.4-.5c3.2-.5 6.5.4 9 2.5l2.2 1.8c2.4 2 5.4 3 8.5 3h10.6c3.1 0 6.1-1.1 8.5-3l2.2-1.8c2.5-2.1 5.8-3 9-2.5l3.4.5c2.7.4 5.4-.4 7.4-2.2 2-1.8 3.2-4.4 3.2-7.2V44C89 22.5 71.5 5 50 5z" fill="white"/>
|
|
1749
|
+
<circle cx="37" cy="45" r="7" fill="#0a0a0a"/>
|
|
1750
|
+
<circle cx="63" cy="45" r="7" fill="#0a0a0a"/>
|
|
1751
|
+
</svg>`;
|
|
1752
|
+
const redirectHost = (() => {
|
|
1753
|
+
try {
|
|
1754
|
+
return new URL(redirectUrl).hostname;
|
|
1755
|
+
} catch {
|
|
1756
|
+
return redirectUrl;
|
|
1757
|
+
}
|
|
1758
|
+
})();
|
|
1759
|
+
const countdownHtml = kind === "success" ? `
|
|
1760
|
+
<div class="countdown" id="countdown">
|
|
1761
|
+
<svg class="ring" viewBox="0 0 60 60">
|
|
1762
|
+
<circle cx="30" cy="30" r="26" stroke="#1a1a1a" stroke-width="3" fill="none"/>
|
|
1763
|
+
<circle id="ring-progress" cx="30" cy="30" r="26" stroke="#22c55e" stroke-width="3" fill="none"
|
|
1764
|
+
stroke-dasharray="163.36" stroke-dashoffset="0" stroke-linecap="round"
|
|
1765
|
+
transform="rotate(-90 30 30)" style="transition:stroke-dashoffset 1s linear"/>
|
|
1766
|
+
</svg>
|
|
1767
|
+
<span class="countdown-num" id="countdown-num">3</span>
|
|
1768
|
+
</div>
|
|
1769
|
+
<p class="subtitle">Redirecting to <strong>${redirectHost}</strong>…</p>
|
|
1770
|
+
<script>
|
|
1771
|
+
(function(){
|
|
1772
|
+
var n=3, el=document.getElementById('countdown-num'),
|
|
1773
|
+
ring=document.getElementById('ring-progress'), circ=163.36;
|
|
1774
|
+
function tick(){
|
|
1775
|
+
if(n<=0){window.location.href=${JSON.stringify(redirectUrl)};return}
|
|
1776
|
+
el.textContent=n;
|
|
1777
|
+
ring.setAttribute('stroke-dashoffset', String(circ*(1-n/3)));
|
|
1778
|
+
n--;
|
|
1779
|
+
setTimeout(tick,1000);
|
|
1780
|
+
}
|
|
1781
|
+
tick();
|
|
1782
|
+
})();
|
|
1783
|
+
</script>` : `<p class="subtitle">Please close this window and try again</p>`;
|
|
1784
|
+
return `<!DOCTYPE html>
|
|
1785
|
+
<html lang="en">
|
|
1786
|
+
<head>
|
|
1787
|
+
<meta charset="utf-8">
|
|
1788
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1789
|
+
<title>OPENCODE-KIRO — Authentication</title>
|
|
1790
|
+
<style>
|
|
1791
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
1792
|
+
body{background:#0a0a0a;color:#e5e5e5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;text-align:center}
|
|
1793
|
+
.container{max-width:420px;padding:2rem}
|
|
1794
|
+
.logo{display:flex;align-items:center;justify-content:center;gap:16px;margin-bottom:48px}
|
|
1795
|
+
.logo-text{font-size:32px;font-weight:700;letter-spacing:4px;color:#7c3aed;font-family:'Courier New',monospace}
|
|
1796
|
+
.status-box{border:1.5px solid ${borderColor};border-radius:12px;padding:20px 28px;display:flex;align-items:flex-start;gap:14px;text-align:left;margin-bottom:20px;background:rgba(${kind === "success" ? "34,197,94" : "239,68,68"},0.04)}
|
|
1797
|
+
.status-icon{flex-shrink:0;margin-top:2px}
|
|
1798
|
+
.status-title{font-size:15px;font-weight:600;color:${borderColor};margin-bottom:4px}
|
|
1799
|
+
.status-msg{font-size:13px;color:#a3a3a3;line-height:1.4}
|
|
1800
|
+
.subtitle{font-size:13px;color:#737373;margin-top:4px}
|
|
1801
|
+
.subtitle strong{color:#a3a3a3}
|
|
1802
|
+
.countdown{position:relative;width:60px;height:60px;margin:24px auto 12px}
|
|
1803
|
+
.ring{width:60px;height:60px}
|
|
1804
|
+
.countdown-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;color:#22c55e;font-variant-numeric:tabular-nums}
|
|
1805
|
+
</style>
|
|
1806
|
+
</head>
|
|
1807
|
+
<body>
|
|
1808
|
+
<div class="container">
|
|
1809
|
+
<div class="logo">
|
|
1810
|
+
${ghostSvg}
|
|
1811
|
+
<span class="logo-text">OPENCODE-KIRO</span>
|
|
1812
|
+
</div>
|
|
1813
|
+
<div class="status-box">
|
|
1814
|
+
<span class="status-icon">${iconSvg}</span>
|
|
1815
|
+
<div>
|
|
1816
|
+
<div class="status-title">${title}</div>
|
|
1817
|
+
<div class="status-msg">${message}</div>
|
|
1818
|
+
</div>
|
|
1819
|
+
</div>
|
|
1820
|
+
${countdownHtml}
|
|
1821
|
+
</div>
|
|
1822
|
+
</body>
|
|
1823
|
+
</html>`;
|
|
1824
|
+
}
|
|
1825
|
+
function oauthIdcDelegationPage() {
|
|
1826
|
+
const ghostSvg = `<svg width="56" height="56" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1827
|
+
<path d="M50 5C28.5 5 11 22.5 11 44v36c0 2.8 1.2 5.4 3.2 7.2 2 1.8 4.7 2.6 7.4 2.2l3.4-.5c3.2-.5 6.5.4 9 2.5l2.2 1.8c2.4 2 5.4 3 8.5 3h10.6c3.1 0 6.1-1.1 8.5-3l2.2-1.8c2.5-2.1 5.8-3 9-2.5l3.4.5c2.7.4 5.4-.4 7.4-2.2 2-1.8 3.2-4.4 3.2-7.2V44C89 22.5 71.5 5 50 5z" fill="white"/>
|
|
1828
|
+
<circle cx="37" cy="45" r="7" fill="#0a0a0a"/>
|
|
1829
|
+
<circle cx="63" cy="45" r="7" fill="#0a0a0a"/>
|
|
1830
|
+
</svg>`;
|
|
1831
|
+
return `<!DOCTYPE html>
|
|
1832
|
+
<html lang="en">
|
|
1833
|
+
<head>
|
|
1834
|
+
<meta charset="utf-8">
|
|
1835
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1836
|
+
<title>OPENCODE-KIRO — Enterprise Sign-In</title>
|
|
1837
|
+
<style>
|
|
1838
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
1839
|
+
body{background:#0a0a0a;color:#e5e5e5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;text-align:center}
|
|
1840
|
+
.container{max-width:420px;padding:2rem}
|
|
1841
|
+
.logo{display:flex;align-items:center;justify-content:center;gap:16px;margin-bottom:48px}
|
|
1842
|
+
.logo-text{font-size:32px;font-weight:700;letter-spacing:4px;color:#7c3aed;font-family:'Courier New',monospace}
|
|
1843
|
+
.status-box{border:1.5px solid #22c55e;border-radius:12px;padding:20px 28px;display:flex;align-items:flex-start;gap:14px;text-align:left;margin-bottom:20px;background:rgba(34,197,94,0.04)}
|
|
1844
|
+
.status-icon{flex-shrink:0;margin-top:2px}
|
|
1845
|
+
.status-title{font-size:15px;font-weight:600;color:#22c55e;margin-bottom:4px}
|
|
1846
|
+
.status-msg{font-size:13px;color:#a3a3a3;line-height:1.4}
|
|
1847
|
+
.subtitle{font-size:13px;color:#737373;margin-top:4px}
|
|
1848
|
+
.subtitle strong{color:#a3a3a3}
|
|
1849
|
+
.spinner{width:28px;height:28px;border:3px solid #1a1a1a;border-top-color:#22c55e;border-radius:50%;animation:spin 0.8s linear infinite;margin:24px auto 12px}
|
|
1850
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
1851
|
+
.countdown{position:relative;width:60px;height:60px;margin:24px auto 12px;display:none}
|
|
1852
|
+
.ring{width:60px;height:60px}
|
|
1853
|
+
.countdown-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;color:#22c55e;font-variant-numeric:tabular-nums}
|
|
1854
|
+
</style>
|
|
1855
|
+
</head>
|
|
1856
|
+
<body>
|
|
1857
|
+
<div class="container">
|
|
1858
|
+
<div class="logo">
|
|
1859
|
+
${ghostSvg}
|
|
1860
|
+
<span class="logo-text">OPENCODE-KIRO</span>
|
|
1861
|
+
</div>
|
|
1862
|
+
<div class="status-box">
|
|
1863
|
+
<span class="status-icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M16.67 5L7.5 14.17 3.33 10" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
|
|
1864
|
+
<div>
|
|
1865
|
+
<div class="status-title">Enterprise sign-in</div>
|
|
1866
|
+
<div class="status-msg" id="status-msg">Preparing device authorization…</div>
|
|
1867
|
+
</div>
|
|
1868
|
+
</div>
|
|
1869
|
+
<div class="spinner" id="spinner"></div>
|
|
1870
|
+
<div class="countdown" id="countdown">
|
|
1871
|
+
<svg class="ring" viewBox="0 0 60 60">
|
|
1872
|
+
<circle cx="30" cy="30" r="26" stroke="#1a1a1a" stroke-width="3" fill="none"/>
|
|
1873
|
+
<circle id="ring-progress" cx="30" cy="30" r="26" stroke="#22c55e" stroke-width="3" fill="none"
|
|
1874
|
+
stroke-dasharray="163.36" stroke-dashoffset="0" stroke-linecap="round"
|
|
1875
|
+
transform="rotate(-90 30 30)" style="transition:stroke-dashoffset 1s linear"/>
|
|
1876
|
+
</svg>
|
|
1877
|
+
<span class="countdown-num" id="countdown-num">3</span>
|
|
1878
|
+
</div>
|
|
1879
|
+
<p class="subtitle" id="subtitle">Waiting for device authorization…</p>
|
|
1880
|
+
<script>
|
|
1881
|
+
(function(){
|
|
1882
|
+
var msg=document.getElementById('status-msg'),
|
|
1883
|
+
spinner=document.getElementById('spinner'),
|
|
1884
|
+
cd=document.getElementById('countdown'),
|
|
1885
|
+
cdNum=document.getElementById('countdown-num'),
|
|
1886
|
+
ring=document.getElementById('ring-progress'),
|
|
1887
|
+
sub=document.getElementById('subtitle'),
|
|
1888
|
+
circ=163.36;
|
|
1889
|
+
|
|
1890
|
+
function poll(){
|
|
1891
|
+
fetch('/idc-verify').then(function(r){return r.json()}).then(function(d){
|
|
1892
|
+
if(d.url){
|
|
1893
|
+
spinner.style.display='none';
|
|
1894
|
+
cd.style.display='block';
|
|
1895
|
+
msg.textContent='Device authorization ready';
|
|
1896
|
+
try{sub.innerHTML='Redirecting to <strong>'+new URL(d.url).hostname+'</strong>…'}catch(e){}
|
|
1897
|
+
countdown(3,d.url);
|
|
1898
|
+
} else {
|
|
1899
|
+
setTimeout(poll,500);
|
|
1900
|
+
}
|
|
1901
|
+
}).catch(function(){setTimeout(poll,1000)});
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
function countdown(n,url){
|
|
1905
|
+
if(n<=0){window.location.href=url;return}
|
|
1906
|
+
cdNum.textContent=n;
|
|
1907
|
+
ring.setAttribute('stroke-dashoffset',String(circ*(1-n/3)));
|
|
1908
|
+
setTimeout(function(){countdown(n-1,url)},1000);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
poll();
|
|
1912
|
+
})();
|
|
1913
|
+
</script>
|
|
1914
|
+
</div>
|
|
1915
|
+
</body>
|
|
1916
|
+
</html>`;
|
|
1917
|
+
}
|
|
1918
|
+
function startCallbackServer(expectedState) {
|
|
1919
|
+
return new Promise((resolve2, reject) => {
|
|
1920
|
+
let settleWait;
|
|
1921
|
+
const waitForCodePromise = new Promise((res) => {
|
|
1922
|
+
settleWait = res;
|
|
1923
|
+
});
|
|
1924
|
+
let idcVerifyUrl = null;
|
|
1925
|
+
const server = createServer((req, res) => {
|
|
1926
|
+
try {
|
|
1927
|
+
const url = new URL(req.url ?? "", SOCIAL_REDIRECT_URI);
|
|
1928
|
+
if (url.pathname === "/idc-verify") {
|
|
1929
|
+
res.writeHead(200, {
|
|
1930
|
+
"Content-Type": "application/json",
|
|
1931
|
+
"Access-Control-Allow-Origin": "*",
|
|
1932
|
+
"Cache-Control": "no-store"
|
|
1933
|
+
});
|
|
1934
|
+
res.end(JSON.stringify({ url: idcVerifyUrl }));
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
const code = url.searchParams.get("code");
|
|
1938
|
+
const state = url.searchParams.get("state");
|
|
1939
|
+
const loginOption = url.searchParams.get("login_option");
|
|
1940
|
+
const issuerUrl = url.searchParams.get("issuer_url");
|
|
1941
|
+
const idcRegion = url.searchParams.get("idc_region");
|
|
1942
|
+
const isIdcDelegation = loginOption === "awsidc" && !!issuerUrl && !!state;
|
|
1943
|
+
if (!state || !code && !isIdcDelegation) {
|
|
1944
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1945
|
+
res.end("");
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
if (state !== expectedState) {
|
|
1949
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
1950
|
+
res.end(oauthCallbackPage("error", "State mismatch", "The OAuth state parameter did not match. Please try logging in again."));
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1954
|
+
if (isIdcDelegation) {
|
|
1955
|
+
res.end(oauthIdcDelegationPage());
|
|
1956
|
+
} else {
|
|
1957
|
+
res.end(oauthCallbackPage("success", "Request approved", "OPENCODE-KIRO has been given requested permissions."));
|
|
1958
|
+
}
|
|
1959
|
+
settleWait?.({
|
|
1960
|
+
code,
|
|
1961
|
+
state,
|
|
1962
|
+
callbackPath: url.pathname,
|
|
1963
|
+
loginOption,
|
|
1964
|
+
issuerUrl: issuerUrl ?? undefined,
|
|
1965
|
+
idcRegion: idcRegion ?? undefined
|
|
1966
|
+
});
|
|
1967
|
+
} catch {
|
|
1968
|
+
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1969
|
+
res.end("Internal error");
|
|
1970
|
+
}
|
|
1971
|
+
});
|
|
1972
|
+
server.on("error", (err) => {
|
|
1973
|
+
reject(err);
|
|
1974
|
+
});
|
|
1975
|
+
server.listen(SOCIAL_REDIRECT_PORT, "localhost", () => {
|
|
1976
|
+
resolve2({
|
|
1977
|
+
server,
|
|
1978
|
+
redirectUri: SOCIAL_REDIRECT_URI,
|
|
1979
|
+
cancelWait: () => {
|
|
1980
|
+
settleWait?.(null);
|
|
1981
|
+
},
|
|
1982
|
+
waitForCode: () => waitForCodePromise,
|
|
1983
|
+
setIdcVerifyUrl: (url) => {
|
|
1984
|
+
idcVerifyUrl = url;
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
});
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
async function startSocialLogin() {
|
|
1991
|
+
const verifier = generateCodeVerifier();
|
|
1992
|
+
const challenge = generateCodeChallenge(verifier);
|
|
1993
|
+
const state = generateRandomState();
|
|
1994
|
+
const callbackServer = await startCallbackServer(state);
|
|
1995
|
+
const signInUrl = buildSocialSignInURL(callbackServer.redirectUri, challenge, state);
|
|
1996
|
+
const waitForCredentials = async () => {
|
|
1997
|
+
try {
|
|
1998
|
+
const result = await callbackServer.waitForCode();
|
|
1999
|
+
if (result?.loginOption === "awsidc" && result.issuerUrl) {
|
|
2000
|
+
log.info("[social-login] Enterprise IdC delegation detected");
|
|
2001
|
+
const idcRegion = result.idcRegion || BUILDER_ID_REGION;
|
|
2002
|
+
const regions = [idcRegion];
|
|
2003
|
+
let regResult = null;
|
|
2004
|
+
let detectedRegion = "";
|
|
2005
|
+
for (const region of regions) {
|
|
2006
|
+
regResult = await tryRegisterAndAuthorize(result.issuerUrl, region);
|
|
2007
|
+
if (regResult) {
|
|
2008
|
+
detectedRegion = region;
|
|
2009
|
+
break;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
if (!regResult || !detectedRegion) {
|
|
2013
|
+
throw new Error(`Could not authorize ${result.issuerUrl} in ${regions.join(", ")}. Check your start URL and region and try again.`);
|
|
2014
|
+
}
|
|
2015
|
+
callbackServer.setIdcVerifyUrl(regResult.devAuth.verificationUriComplete);
|
|
2016
|
+
log.info(`[social-login] IdC device auth ready, verification URL sent to browser`);
|
|
2017
|
+
const tok = await pollForToken(regResult.oidcEndpoint, regResult.clientId, regResult.clientSecret, regResult.devAuth, undefined);
|
|
2018
|
+
if (!tok.accessToken || !tok.refreshToken) {
|
|
2019
|
+
throw new Error("IdC authorization completed but no tokens returned");
|
|
2020
|
+
}
|
|
2021
|
+
let profileArn;
|
|
2022
|
+
try {
|
|
2023
|
+
const apiRegion = resolveApiRegion(detectedRegion);
|
|
2024
|
+
const { resolveProfileArn: resolveProfileArn2 } = await Promise.resolve().then(() => (init_models(), exports_models));
|
|
2025
|
+
const resolved = await resolveProfileArn2(tok.accessToken, apiRegion);
|
|
2026
|
+
if (resolved) {
|
|
2027
|
+
profileArn = resolved;
|
|
2028
|
+
try {
|
|
2029
|
+
const apiModels = await fetchAvailableModels(tok.accessToken, apiRegion, profileArn);
|
|
2030
|
+
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
2031
|
+
log.info(`[social-login] IdC: fetched and cached ${apiModels.length} models`);
|
|
2032
|
+
} catch (err) {
|
|
2033
|
+
log.warn(`[social-login] IdC: failed to fetch models: ${err}`);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
} catch (err) {
|
|
2037
|
+
log.warn(`[social-login] IdC: failed to resolve profileArn: ${err}`);
|
|
2038
|
+
}
|
|
2039
|
+
return {
|
|
2040
|
+
accessToken: tok.accessToken,
|
|
2041
|
+
refreshToken: tok.refreshToken,
|
|
2042
|
+
refreshPacked: `${tok.refreshToken}|${regResult.clientId}|${regResult.clientSecret}|idc`,
|
|
2043
|
+
profileArn,
|
|
2044
|
+
region: detectedRegion,
|
|
2045
|
+
authMethod: "idc",
|
|
2046
|
+
expiresAt: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
if (!result?.code) {
|
|
2050
|
+
throw new Error("Missing authorization code — sign-in was not completed");
|
|
2051
|
+
}
|
|
2052
|
+
const tokenRedirectUri = buildTokenRedirectUri(result.callbackPath, result.loginOption);
|
|
2053
|
+
log.info("[social-login] Exchanging authorization code…");
|
|
2054
|
+
const resp = await fetch(`${KIRO_SOCIAL_AUTH_ENDPOINT}/oauth/token`, {
|
|
2055
|
+
method: "POST",
|
|
2056
|
+
headers: { "Content-Type": "application/json", "User-Agent": "opencode-kiro" },
|
|
2057
|
+
body: JSON.stringify({
|
|
2058
|
+
code: result.code,
|
|
2059
|
+
code_verifier: verifier,
|
|
2060
|
+
redirect_uri: tokenRedirectUri
|
|
2061
|
+
})
|
|
2062
|
+
});
|
|
2063
|
+
if (!resp.ok) {
|
|
2064
|
+
const body = await resp.text().catch(() => "");
|
|
2065
|
+
throw new Error(`Token exchange failed: ${resp.status} ${body}`);
|
|
2066
|
+
}
|
|
2067
|
+
const data = await resp.json();
|
|
2068
|
+
if (!data.accessToken || !data.refreshToken) {
|
|
2069
|
+
throw new Error("Token exchange returned no tokens");
|
|
2070
|
+
}
|
|
2071
|
+
if (data.profileArn) {
|
|
2072
|
+
try {
|
|
2073
|
+
const apiRegion = resolveApiRegion(BUILDER_ID_REGION);
|
|
2074
|
+
const apiModels = await fetchAvailableModels(data.accessToken, apiRegion, data.profileArn);
|
|
2075
|
+
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
2076
|
+
log.info(`[social-login] Fetched and cached ${apiModels.length} models`);
|
|
2077
|
+
} catch (err) {
|
|
2078
|
+
log.warn(`[social-login] Failed to fetch models: ${err}`);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
return {
|
|
2082
|
+
accessToken: data.accessToken,
|
|
2083
|
+
refreshToken: data.refreshToken,
|
|
2084
|
+
refreshPacked: `${data.refreshToken}|||social`,
|
|
2085
|
+
profileArn: data.profileArn,
|
|
2086
|
+
region: BUILDER_ID_REGION,
|
|
2087
|
+
authMethod: "social",
|
|
2088
|
+
expiresAt: Date.now() + (data.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
2089
|
+
};
|
|
2090
|
+
} finally {
|
|
2091
|
+
callbackServer.server.close();
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
return { signInUrl, waitForCredentials };
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// src/file-logger.ts
|
|
2098
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
2099
|
+
var LOG_DIR = "/tmp/kiro-logs";
|
|
2100
|
+
var REQUESTS_FILE = `${LOG_DIR}/requests.log`;
|
|
2101
|
+
var RESPONSES_FILE = `${LOG_DIR}/responses.log`;
|
|
2102
|
+
var ERRORS_FILE = `${LOG_DIR}/errors.log`;
|
|
2103
|
+
var dirEnsured = false;
|
|
2104
|
+
function isEnabled() {
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
2107
|
+
function ensureDir() {
|
|
2108
|
+
if (dirEnsured)
|
|
2109
|
+
return;
|
|
2110
|
+
try {
|
|
2111
|
+
mkdirSync2(LOG_DIR, { recursive: true });
|
|
2112
|
+
dirEnsured = true;
|
|
2113
|
+
} catch {}
|
|
2114
|
+
}
|
|
2115
|
+
function writeLine(file, data) {
|
|
2116
|
+
if (!isEnabled())
|
|
2117
|
+
return;
|
|
2118
|
+
ensureDir();
|
|
2119
|
+
const entry = {
|
|
2120
|
+
ts: new Date().toISOString(),
|
|
2121
|
+
...data
|
|
2122
|
+
};
|
|
2123
|
+
try {
|
|
2124
|
+
appendFileSync2(file, JSON.stringify(entry) + `
|
|
2125
|
+
`);
|
|
2126
|
+
} catch {}
|
|
2127
|
+
}
|
|
2128
|
+
function logRequest(meta, requestBody) {
|
|
2129
|
+
writeLine(REQUESTS_FILE, {
|
|
2130
|
+
type: "request",
|
|
2131
|
+
...meta,
|
|
2132
|
+
body: safeParseJson(requestBody)
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
function logResponseEvent(event) {
|
|
2136
|
+
writeLine(RESPONSES_FILE, {
|
|
2137
|
+
type: "response_event",
|
|
2138
|
+
eventType: event.type,
|
|
2139
|
+
seq: event.eventSeq,
|
|
2140
|
+
data: event.data
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
function logResponseDone(meta) {
|
|
2144
|
+
writeLine(RESPONSES_FILE, {
|
|
2145
|
+
type: "response_done",
|
|
2146
|
+
...meta
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
function logHttpError(meta) {
|
|
2150
|
+
writeLine(ERRORS_FILE, {
|
|
2151
|
+
type: "http_error",
|
|
2152
|
+
...meta
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
function logStreamError(meta) {
|
|
2156
|
+
writeLine(ERRORS_FILE, {
|
|
2157
|
+
type: "stream_error",
|
|
2158
|
+
...meta
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
function logCaughtError(meta) {
|
|
2162
|
+
writeLine(ERRORS_FILE, {
|
|
2163
|
+
type: "caught_error",
|
|
2164
|
+
...meta
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
function safeParseJson(s) {
|
|
2168
|
+
try {
|
|
2169
|
+
return JSON.parse(s);
|
|
2170
|
+
} catch {
|
|
2171
|
+
return s;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
1381
2174
|
|
|
1382
2175
|
// src/transform.ts
|
|
1383
|
-
import { createHash } from "node:crypto";
|
|
2176
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1384
2177
|
|
|
1385
2178
|
// src/kiro-defaults.ts
|
|
1386
2179
|
var SYSTEM_SEED_INSTRUCTION = `Follow this instruction: # Kiro CLI Default Agent
|
|
@@ -1463,7 +2256,7 @@ var KIRO_TOOL_USE_ID_RE = /^tooluse_[A-Za-z0-9]+$/;
|
|
|
1463
2256
|
function toKiroToolUseId(id) {
|
|
1464
2257
|
if (KIRO_TOOL_USE_ID_RE.test(id))
|
|
1465
2258
|
return id;
|
|
1466
|
-
const digest =
|
|
2259
|
+
const digest = createHash2("sha256").update(id).digest("hex").slice(0, 22);
|
|
1467
2260
|
return `tooluse_${digest}`;
|
|
1468
2261
|
}
|
|
1469
2262
|
var ALLOWED_SCHEMA_KEYS = new Set([
|
|
@@ -1598,15 +2391,18 @@ ${uim.content}`;
|
|
|
1598
2391
|
}
|
|
1599
2392
|
if (msg.role === "assistant") {
|
|
1600
2393
|
let armContent = "";
|
|
2394
|
+
let armReasoningText = "";
|
|
2395
|
+
let armReasoningSignature = "";
|
|
1601
2396
|
const armToolUses = [];
|
|
1602
2397
|
if (Array.isArray(msg.content)) {
|
|
1603
2398
|
for (const block of msg.content) {
|
|
1604
2399
|
if (block.type === "text") {
|
|
1605
2400
|
armContent += block.text;
|
|
1606
2401
|
} else if (block.type === "thinking") {
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
2402
|
+
const tb = block;
|
|
2403
|
+
armReasoningText += tb.thinking;
|
|
2404
|
+
if (tb.thinkingSignature)
|
|
2405
|
+
armReasoningSignature = tb.thinkingSignature;
|
|
1610
2406
|
} else if (block.type === "toolCall") {
|
|
1611
2407
|
const tc = block;
|
|
1612
2408
|
armToolUses.push({
|
|
@@ -1617,12 +2413,15 @@ ${armContent}`;
|
|
|
1617
2413
|
}
|
|
1618
2414
|
}
|
|
1619
2415
|
}
|
|
1620
|
-
|
|
2416
|
+
const hasReasoning = armReasoningText.length > 0;
|
|
2417
|
+
if (!armContent && armToolUses.length === 0 && !hasReasoning)
|
|
1621
2418
|
continue;
|
|
2419
|
+
const reasoningContent = hasReasoning && armReasoningSignature ? { reasoningText: { text: armReasoningText, signature: armReasoningSignature } } : undefined;
|
|
1622
2420
|
history.push({
|
|
1623
2421
|
assistantResponseMessage: {
|
|
1624
2422
|
content: armContent,
|
|
1625
|
-
...armToolUses.length > 0 ? { toolUses: armToolUses } : {}
|
|
2423
|
+
...armToolUses.length > 0 ? { toolUses: armToolUses } : {},
|
|
2424
|
+
...reasoningContent ? { reasoningContent } : {}
|
|
1626
2425
|
}
|
|
1627
2426
|
});
|
|
1628
2427
|
continue;
|
|
@@ -1794,49 +2593,6 @@ function emitHiddenReasoningLate(output, stream) {
|
|
|
1794
2593
|
partial: output
|
|
1795
2594
|
});
|
|
1796
2595
|
}
|
|
1797
|
-
var profileArnCache = new Map;
|
|
1798
|
-
var profileArnSkipResolution = false;
|
|
1799
|
-
function seedProfileArn(endpoint, arn) {
|
|
1800
|
-
profileArnCache.set(endpoint, arn);
|
|
1801
|
-
}
|
|
1802
|
-
async function resolveProfileArn(accessToken, endpoint) {
|
|
1803
|
-
if (profileArnSkipResolution)
|
|
1804
|
-
return;
|
|
1805
|
-
const cached = profileArnCache.get(endpoint);
|
|
1806
|
-
if (cached !== undefined)
|
|
1807
|
-
return cached;
|
|
1808
|
-
try {
|
|
1809
|
-
const ep = new URL(endpoint);
|
|
1810
|
-
ep.hostname = ep.hostname.replace("runtime.", "management.");
|
|
1811
|
-
ep.pathname = "/";
|
|
1812
|
-
ep.search = "";
|
|
1813
|
-
ep.hash = "";
|
|
1814
|
-
const resp = await fetch(ep.toString(), {
|
|
1815
|
-
method: "POST",
|
|
1816
|
-
headers: {
|
|
1817
|
-
"Content-Type": "application/x-amz-json-1.0",
|
|
1818
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1819
|
-
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableProfiles"
|
|
1820
|
-
},
|
|
1821
|
-
body: "{}"
|
|
1822
|
-
});
|
|
1823
|
-
if (!resp.ok) {
|
|
1824
|
-
log.debug(`profileArn resolution failed: ${resp.status} ${resp.statusText}`);
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
|
-
const j = await resp.json();
|
|
1828
|
-
const arn = j.profiles?.find((p) => p.arn)?.arn;
|
|
1829
|
-
if (!arn) {
|
|
1830
|
-
log.debug("profileArn resolution returned no profile ARN");
|
|
1831
|
-
return;
|
|
1832
|
-
}
|
|
1833
|
-
profileArnCache.set(endpoint, arn);
|
|
1834
|
-
return arn;
|
|
1835
|
-
} catch (error) {
|
|
1836
|
-
log.debug(`profileArn resolution threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
1837
|
-
return;
|
|
1838
|
-
}
|
|
1839
|
-
}
|
|
1840
2596
|
function emitToolCall(state, output, stream) {
|
|
1841
2597
|
if (!state.input.trim())
|
|
1842
2598
|
state.input = "{}";
|
|
@@ -1922,9 +2678,9 @@ ${systemPrompt}` : ""}`;
|
|
|
1922
2678
|
const normalized = normalizeMessages(context.messages);
|
|
1923
2679
|
const {
|
|
1924
2680
|
history,
|
|
1925
|
-
systemPrepended,
|
|
2681
|
+
systemPrepended: _systemPrepended,
|
|
1926
2682
|
currentMsgStartIdx
|
|
1927
|
-
} = buildHistory(normalized, kiroModelId
|
|
2683
|
+
} = buildHistory(normalized, kiroModelId);
|
|
1928
2684
|
const seedInstruction = SYSTEM_SEED_INSTRUCTION.replace("{{modelId}}", kiroModelId);
|
|
1929
2685
|
const seedPair = [
|
|
1930
2686
|
{ userInputMessage: { content: seedInstruction, origin: "KIRO_CLI" } },
|
|
@@ -1964,7 +2720,7 @@ ${systemPrompt}` : ""}`;
|
|
|
1964
2720
|
const hasReasoning = armReasoningText.length > 0;
|
|
1965
2721
|
if (armContent || armToolUses.length > 0 || hasReasoning) {
|
|
1966
2722
|
const last = history[history.length - 1];
|
|
1967
|
-
const reasoningContent = hasReasoning ? { reasoningText: { text: armReasoningText, signature: armReasoningSignature } } : undefined;
|
|
2723
|
+
const reasoningContent = hasReasoning && armReasoningSignature ? { reasoningText: { text: armReasoningText, signature: armReasoningSignature } } : undefined;
|
|
1968
2724
|
if (last && !last.userInputMessage && last.assistantResponseMessage) {
|
|
1969
2725
|
last.assistantResponseMessage.content += `
|
|
1970
2726
|
|
|
@@ -2040,7 +2796,7 @@ ${armContent}`;
|
|
|
2040
2796
|
currentContent = "Tool results provided.";
|
|
2041
2797
|
} else if (firstMsg?.role === "user") {
|
|
2042
2798
|
currentContent = typeof firstMsg.content === "string" ? firstMsg.content : getContentText(firstMsg);
|
|
2043
|
-
if (systemPrompt
|
|
2799
|
+
if (systemPrompt) {
|
|
2044
2800
|
currentContent = `${systemPrompt}
|
|
2045
2801
|
|
|
2046
2802
|
${currentContent}`;
|
|
@@ -2138,6 +2894,14 @@ ${currentContent}`;
|
|
|
2138
2894
|
__require("fs").writeFileSync("/tmp/kiro-last-request.json", requestBody);
|
|
2139
2895
|
} catch {}
|
|
2140
2896
|
}
|
|
2897
|
+
logRequest({
|
|
2898
|
+
endpoint,
|
|
2899
|
+
model: model.id,
|
|
2900
|
+
historyLength: history.length,
|
|
2901
|
+
requestBodyChars: requestBody.length,
|
|
2902
|
+
attempt: retryCount,
|
|
2903
|
+
conversationId
|
|
2904
|
+
}, requestBody);
|
|
2141
2905
|
response = await fetch(endpoint, {
|
|
2142
2906
|
method: "POST",
|
|
2143
2907
|
headers: {
|
|
@@ -2169,6 +2933,15 @@ ${currentContent}`;
|
|
|
2169
2933
|
status: response.status,
|
|
2170
2934
|
body: errText
|
|
2171
2935
|
});
|
|
2936
|
+
logHttpError({
|
|
2937
|
+
status: response.status,
|
|
2938
|
+
statusText: response.statusText,
|
|
2939
|
+
body: errText,
|
|
2940
|
+
endpoint,
|
|
2941
|
+
model: model.id,
|
|
2942
|
+
attempt: retryCount,
|
|
2943
|
+
historyLength: history.length
|
|
2944
|
+
});
|
|
2172
2945
|
if (isCapacityError(errText) && capacityRetryCount < CAPACITY_MAX_RETRIES) {
|
|
2173
2946
|
capacityRetryCount++;
|
|
2174
2947
|
const delayMs = exponentialBackoff(capacityRetryCount - 1, CAPACITY_BASE_DELAY_MS, CAPACITY_MAX_DELAY_MS);
|
|
@@ -2202,12 +2975,12 @@ ${currentContent}`;
|
|
|
2202
2975
|
if (response.status === 401) {
|
|
2203
2976
|
const permanent = isPermanentError(errText);
|
|
2204
2977
|
if (permanent) {
|
|
2205
|
-
|
|
2978
|
+
resetProfileArnCache();
|
|
2206
2979
|
throw new Error(`Kiro API error: credentials permanently invalid — run /login kiro to re-authenticate. ${errText}`);
|
|
2207
2980
|
}
|
|
2208
2981
|
}
|
|
2209
2982
|
if (response.status === 403) {
|
|
2210
|
-
|
|
2983
|
+
resetProfileArnCache();
|
|
2211
2984
|
throw new Error(`Kiro API error: access token rejected (403) — run /login kiro to re-authenticate. ${errText}`);
|
|
2212
2985
|
}
|
|
2213
2986
|
throw new Error(`Kiro API error: ${response.status} ${response.statusText} ${errText}`);
|
|
@@ -2312,6 +3085,9 @@ ${currentContent}`;
|
|
|
2312
3085
|
log.debug("stream.event", { seq: eventSeq++, event: ev });
|
|
2313
3086
|
}
|
|
2314
3087
|
}
|
|
3088
|
+
for (const ev of events) {
|
|
3089
|
+
logResponseEvent({ type: ev.type, data: ev.data, eventSeq });
|
|
3090
|
+
}
|
|
2315
3091
|
for (const event of events) {
|
|
2316
3092
|
switch (event.type) {
|
|
2317
3093
|
case "contextUsage": {
|
|
@@ -2402,6 +3178,12 @@ ${currentContent}`;
|
|
|
2402
3178
|
}
|
|
2403
3179
|
case "error": {
|
|
2404
3180
|
streamError = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
|
|
3181
|
+
logStreamError({
|
|
3182
|
+
error: streamError,
|
|
3183
|
+
context: "stream_event",
|
|
3184
|
+
model: model.id,
|
|
3185
|
+
attempt: retryCount
|
|
3186
|
+
});
|
|
2405
3187
|
reader.cancel().catch(() => {});
|
|
2406
3188
|
break;
|
|
2407
3189
|
}
|
|
@@ -2416,7 +3198,14 @@ ${currentContent}`;
|
|
|
2416
3198
|
if (retryCount < MAX_RETRIES) {
|
|
2417
3199
|
retryCount++;
|
|
2418
3200
|
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
2419
|
-
|
|
3201
|
+
const streamErrDesc = firstTokenTimedOut ? "first-token timed out" : idleCancelled ? "idle timed out" : `error: ${streamError}`;
|
|
3202
|
+
logStreamError({
|
|
3203
|
+
error: streamErrDesc,
|
|
3204
|
+
context: "retry",
|
|
3205
|
+
model: model.id,
|
|
3206
|
+
attempt: retryCount
|
|
3207
|
+
});
|
|
3208
|
+
log.info(`stream ${streamErrDesc} — retrying (${retryCount}/${MAX_RETRIES})`);
|
|
2420
3209
|
cancelHiddenShim();
|
|
2421
3210
|
await abortableDelay(delayMs, options?.signal);
|
|
2422
3211
|
output.content = [];
|
|
@@ -2491,6 +3280,13 @@ ${currentContent}`;
|
|
|
2491
3280
|
sawAnyToolCalls,
|
|
2492
3281
|
usage: output.usage
|
|
2493
3282
|
});
|
|
3283
|
+
logResponseDone({
|
|
3284
|
+
stopReason: output.stopReason,
|
|
3285
|
+
emittedToolCalls,
|
|
3286
|
+
usage: output.usage,
|
|
3287
|
+
contentBlocks: output.content.length,
|
|
3288
|
+
model: model.id
|
|
3289
|
+
});
|
|
2494
3290
|
stream.end();
|
|
2495
3291
|
return;
|
|
2496
3292
|
}
|
|
@@ -2498,6 +3294,11 @@ ${currentContent}`;
|
|
|
2498
3294
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2499
3295
|
output.errorMessage = error instanceof Error ? error.message : String(error);
|
|
2500
3296
|
log.debug("response.caught", { stopReason: output.stopReason, error: output.errorMessage });
|
|
3297
|
+
logCaughtError({
|
|
3298
|
+
stopReason: output.stopReason,
|
|
3299
|
+
errorMessage: output.errorMessage,
|
|
3300
|
+
model: model.id
|
|
3301
|
+
});
|
|
2501
3302
|
if (hiddenShimTimer) {
|
|
2502
3303
|
clearTimeout(hiddenShimTimer);
|
|
2503
3304
|
hiddenShimTimer = null;
|
|
@@ -2515,6 +3316,8 @@ ${currentContent}`;
|
|
|
2515
3316
|
|
|
2516
3317
|
// src/server.ts
|
|
2517
3318
|
init_debug();
|
|
3319
|
+
init_models();
|
|
3320
|
+
init_models();
|
|
2518
3321
|
|
|
2519
3322
|
// src/dashboard-stats.ts
|
|
2520
3323
|
var MAX_HISTORY = 100;
|
|
@@ -2887,11 +3690,29 @@ async function initGatewayAuth() {
|
|
|
2887
3690
|
expiresAt: Date.now() + 3500000
|
|
2888
3691
|
};
|
|
2889
3692
|
log.info(`[gateway-auth] Initialized (method=${imported.authMethod}, region=${imported.region})`);
|
|
3693
|
+
let activeAccessToken = imported.accessToken;
|
|
3694
|
+
if (imported.refreshToken) {
|
|
3695
|
+
try {
|
|
3696
|
+
log.info("[gateway-auth] Refreshing token at startup…");
|
|
3697
|
+
const refreshed = await refreshKiroToken(_creds.refreshPacked, _creds.region, _creds.authMethod);
|
|
3698
|
+
_creds.accessToken = refreshed.access;
|
|
3699
|
+
_creds.refreshPacked = refreshed.refresh;
|
|
3700
|
+
_creds.expiresAt = refreshed.expires;
|
|
3701
|
+
activeAccessToken = refreshed.access;
|
|
3702
|
+
log.info("[gateway-auth] Token refreshed on startup successfully");
|
|
3703
|
+
} catch (err) {
|
|
3704
|
+
log.warn("[gateway-auth] Startup token refresh failed, trying with existing token", err);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
2890
3707
|
try {
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
3708
|
+
if (imported.profileArn) {
|
|
3709
|
+
const apiModels = await fetchAvailableModels(activeAccessToken, imported.region, imported.profileArn);
|
|
3710
|
+
const built = buildModelsFromApi(apiModels);
|
|
3711
|
+
setCachedDynamicModels(built);
|
|
3712
|
+
log.info(`[kiro-models.fetched] Found ${built.length} available models`);
|
|
3713
|
+
} else {
|
|
3714
|
+
log.info(`[kiro-models.fetched] Skipping fetch, no profileArn available`);
|
|
3715
|
+
}
|
|
2895
3716
|
} catch (err) {
|
|
2896
3717
|
log.warn("[gateway-auth] Failed to fetch dynamic models (will fallback to static list)", err);
|
|
2897
3718
|
}
|
|
@@ -2952,6 +3773,36 @@ function startGatewayServer(port = 0) {
|
|
|
2952
3773
|
headers: { "Content-Type": "application/json" }
|
|
2953
3774
|
});
|
|
2954
3775
|
}
|
|
3776
|
+
if (url.pathname === "/auth/login" && req.method === "GET") {
|
|
3777
|
+
try {
|
|
3778
|
+
const { signInUrl, waitForCredentials } = await startSocialLogin();
|
|
3779
|
+
log.info("[gateway] Social login initiated, redirecting to Kiro sign-in");
|
|
3780
|
+
waitForCredentials().then((creds) => {
|
|
3781
|
+
_creds = {
|
|
3782
|
+
accessToken: creds.accessToken,
|
|
3783
|
+
refreshPacked: creds.refreshPacked,
|
|
3784
|
+
region: creds.region,
|
|
3785
|
+
authMethod: creds.authMethod,
|
|
3786
|
+
profileArn: creds.profileArn,
|
|
3787
|
+
expiresAt: creds.expiresAt
|
|
3788
|
+
};
|
|
3789
|
+
if (creds.profileArn) {
|
|
3790
|
+
seedProfileArn(creds.profileArn);
|
|
3791
|
+
}
|
|
3792
|
+
log.info(`[gateway] Login completed (${creds.authMethod}) — credentials updated`);
|
|
3793
|
+
}).catch((err) => {
|
|
3794
|
+
log.error("[gateway] Login failed:", err);
|
|
3795
|
+
});
|
|
3796
|
+
return new Response(null, {
|
|
3797
|
+
status: 302,
|
|
3798
|
+
headers: { Location: signInUrl }
|
|
3799
|
+
});
|
|
3800
|
+
} catch (err) {
|
|
3801
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3802
|
+
log.error("[gateway] Failed to start social login:", msg);
|
|
3803
|
+
return anthropicError(500, "api_error", `Failed to start login: ${msg}`);
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
2955
3806
|
if ((url.pathname === "/v1/messages" || url.pathname === "/messages") && req.method === "POST") {
|
|
2956
3807
|
let accessToken;
|
|
2957
3808
|
try {
|
|
@@ -3003,7 +3854,7 @@ function startGatewayServer(port = 0) {
|
|
|
3003
3854
|
const reasoningEffort = body.output_config?.effort ?? body.reasoning_effort ?? undefined;
|
|
3004
3855
|
log.info(`[gateway] → ${kiroEndpoint} model=${anthropicModelId} region=${apiRegion} stream=${streamRequested}`);
|
|
3005
3856
|
if (_creds.profileArn) {
|
|
3006
|
-
seedProfileArn(
|
|
3857
|
+
seedProfileArn(_creds.profileArn);
|
|
3007
3858
|
}
|
|
3008
3859
|
const kiroStream = streamKiro(piModel, context, {
|
|
3009
3860
|
apiKey: accessToken,
|
|
@@ -3374,6 +4225,7 @@ function translateAnthropicToolsToPi(tools) {
|
|
|
3374
4225
|
|
|
3375
4226
|
// src/index.ts
|
|
3376
4227
|
init_debug();
|
|
4228
|
+
init_models();
|
|
3377
4229
|
process.env.KIRO_LOG = process.env.KIRO_LOG || "debug";
|
|
3378
4230
|
process.env.KIRO_LOG_FILE = process.env.KIRO_LOG_FILE || "/tmp/opencode-kiro.log";
|
|
3379
4231
|
var gatewayServer = null;
|
|
@@ -3422,30 +4274,28 @@ var KiroPlugin = async (input) => {
|
|
|
3422
4274
|
label: "AWS Builder ID (personal account)",
|
|
3423
4275
|
prompts: [],
|
|
3424
4276
|
authorize: async () => {
|
|
3425
|
-
const
|
|
3426
|
-
if (!result) {
|
|
3427
|
-
throw new Error("Could not authorize with AWS Builder ID.");
|
|
3428
|
-
}
|
|
4277
|
+
const { signInUrl, waitForCredentials } = await startSocialLogin();
|
|
3429
4278
|
return {
|
|
3430
|
-
url:
|
|
3431
|
-
instructions:
|
|
3432
|
-
Complete authorization in your browser, then OpenCode will continue automatically.`,
|
|
4279
|
+
url: signInUrl,
|
|
4280
|
+
instructions: "Complete sign-in in your browser. OpenCode will continue automatically.",
|
|
3433
4281
|
method: "auto",
|
|
3434
4282
|
callback: async () => {
|
|
3435
|
-
|
|
3436
|
-
|
|
4283
|
+
try {
|
|
4284
|
+
const creds = await waitForCredentials();
|
|
4285
|
+
return {
|
|
4286
|
+
type: "success",
|
|
4287
|
+
access: creds.accessToken,
|
|
4288
|
+
refresh: creds.refreshPacked,
|
|
4289
|
+
expires: creds.expiresAt,
|
|
4290
|
+
metadata: {
|
|
4291
|
+
region: creds.region,
|
|
4292
|
+
authMethod: creds.authMethod,
|
|
4293
|
+
profileArn: creds.profileArn
|
|
4294
|
+
}
|
|
4295
|
+
};
|
|
4296
|
+
} catch {
|
|
3437
4297
|
return { type: "failed" };
|
|
3438
4298
|
}
|
|
3439
|
-
return {
|
|
3440
|
-
type: "success",
|
|
3441
|
-
access: tok.accessToken,
|
|
3442
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|builder-id`,
|
|
3443
|
-
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
3444
|
-
metadata: {
|
|
3445
|
-
region: BUILDER_ID_REGION,
|
|
3446
|
-
authMethod: "builder-id"
|
|
3447
|
-
}
|
|
3448
|
-
};
|
|
3449
4299
|
}
|
|
3450
4300
|
};
|
|
3451
4301
|
}
|
|
@@ -3496,14 +4346,25 @@ Complete authorization in your browser, then OpenCode will continue automaticall
|
|
|
3496
4346
|
if (!tok.accessToken || !tok.refreshToken) {
|
|
3497
4347
|
return { type: "failed" };
|
|
3498
4348
|
}
|
|
4349
|
+
const apiRegion = resolveApiRegion(detectedRegion);
|
|
4350
|
+
const arn = await resolveProfileArn(tok.accessToken, apiRegion);
|
|
4351
|
+
if (arn) {
|
|
4352
|
+
try {
|
|
4353
|
+
const models = await fetchAvailableModels(tok.accessToken, apiRegion, arn);
|
|
4354
|
+
setCachedDynamicModels(buildModelsFromApi(models));
|
|
4355
|
+
} catch (e) {
|
|
4356
|
+
log.warn("Failed to precache models", e);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
3499
4359
|
return {
|
|
3500
4360
|
type: "success",
|
|
3501
4361
|
access: tok.accessToken,
|
|
3502
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc
|
|
4362
|
+
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc||`,
|
|
3503
4363
|
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
3504
4364
|
metadata: {
|
|
3505
4365
|
region: detectedRegion,
|
|
3506
|
-
authMethod: "idc"
|
|
4366
|
+
authMethod: "idc",
|
|
4367
|
+
profileArn: arn
|
|
3507
4368
|
}
|
|
3508
4369
|
};
|
|
3509
4370
|
}
|
|
@@ -3527,7 +4388,9 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3527
4388
|
imported.refreshToken,
|
|
3528
4389
|
imported.clientId || "",
|
|
3529
4390
|
imported.clientSecret || "",
|
|
3530
|
-
authMethod
|
|
4391
|
+
authMethod,
|
|
4392
|
+
imported.source || "",
|
|
4393
|
+
imported.tokenKey || ""
|
|
3531
4394
|
];
|
|
3532
4395
|
return {
|
|
3533
4396
|
url: "",
|
|
@@ -3583,7 +4446,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3583
4446
|
throw new Error("No refresh token provided.");
|
|
3584
4447
|
}
|
|
3585
4448
|
const region = inputs.region?.trim() || "us-east-1";
|
|
3586
|
-
const packed = `${refreshToken}|||desktop
|
|
4449
|
+
const packed = `${refreshToken}|||desktop||`;
|
|
3587
4450
|
return {
|
|
3588
4451
|
url: "",
|
|
3589
4452
|
method: "auto",
|
|
@@ -3611,7 +4474,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3611
4474
|
cfg.provider = cfg.provider ?? {};
|
|
3612
4475
|
cfg.provider.kiro = cfg.provider.kiro ?? {};
|
|
3613
4476
|
const kiro = cfg.provider.kiro;
|
|
3614
|
-
kiro.name = kiro.name ?? "Kiro";
|
|
4477
|
+
kiro.name = kiro.name ?? "Kiro AWS";
|
|
3615
4478
|
kiro.npm = kiro.npm ?? "@ai-sdk/anthropic";
|
|
3616
4479
|
kiro.api = kiro.api ?? `http://127.0.0.1:${localPort}/v1`;
|
|
3617
4480
|
kiro.models = kiro.models ?? {};
|