@javargasm/opencode-kiro-auth 0.1.2 → 0.2.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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1214 -480
- 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/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,411 @@ 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
|
+
}
|
|
1381
2096
|
|
|
1382
2097
|
// src/transform.ts
|
|
1383
|
-
import { createHash } from "node:crypto";
|
|
2098
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1384
2099
|
|
|
1385
2100
|
// src/kiro-defaults.ts
|
|
1386
2101
|
var SYSTEM_SEED_INSTRUCTION = `Follow this instruction: # Kiro CLI Default Agent
|
|
@@ -1463,7 +2178,7 @@ var KIRO_TOOL_USE_ID_RE = /^tooluse_[A-Za-z0-9]+$/;
|
|
|
1463
2178
|
function toKiroToolUseId(id) {
|
|
1464
2179
|
if (KIRO_TOOL_USE_ID_RE.test(id))
|
|
1465
2180
|
return id;
|
|
1466
|
-
const digest =
|
|
2181
|
+
const digest = createHash2("sha256").update(id).digest("hex").slice(0, 22);
|
|
1467
2182
|
return `tooluse_${digest}`;
|
|
1468
2183
|
}
|
|
1469
2184
|
var ALLOWED_SCHEMA_KEYS = new Set([
|
|
@@ -1794,49 +2509,6 @@ function emitHiddenReasoningLate(output, stream) {
|
|
|
1794
2509
|
partial: output
|
|
1795
2510
|
});
|
|
1796
2511
|
}
|
|
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
2512
|
function emitToolCall(state, output, stream) {
|
|
1841
2513
|
if (!state.input.trim())
|
|
1842
2514
|
state.input = "{}";
|
|
@@ -2202,12 +2874,12 @@ ${currentContent}`;
|
|
|
2202
2874
|
if (response.status === 401) {
|
|
2203
2875
|
const permanent = isPermanentError(errText);
|
|
2204
2876
|
if (permanent) {
|
|
2205
|
-
|
|
2877
|
+
resetProfileArnCache();
|
|
2206
2878
|
throw new Error(`Kiro API error: credentials permanently invalid — run /login kiro to re-authenticate. ${errText}`);
|
|
2207
2879
|
}
|
|
2208
2880
|
}
|
|
2209
2881
|
if (response.status === 403) {
|
|
2210
|
-
|
|
2882
|
+
resetProfileArnCache();
|
|
2211
2883
|
throw new Error(`Kiro API error: access token rejected (403) — run /login kiro to re-authenticate. ${errText}`);
|
|
2212
2884
|
}
|
|
2213
2885
|
throw new Error(`Kiro API error: ${response.status} ${response.statusText} ${errText}`);
|
|
@@ -2515,6 +3187,8 @@ ${currentContent}`;
|
|
|
2515
3187
|
|
|
2516
3188
|
// src/server.ts
|
|
2517
3189
|
init_debug();
|
|
3190
|
+
init_models();
|
|
3191
|
+
init_models();
|
|
2518
3192
|
|
|
2519
3193
|
// src/dashboard-stats.ts
|
|
2520
3194
|
var MAX_HISTORY = 100;
|
|
@@ -2887,11 +3561,29 @@ async function initGatewayAuth() {
|
|
|
2887
3561
|
expiresAt: Date.now() + 3500000
|
|
2888
3562
|
};
|
|
2889
3563
|
log.info(`[gateway-auth] Initialized (method=${imported.authMethod}, region=${imported.region})`);
|
|
3564
|
+
let activeAccessToken = imported.accessToken;
|
|
3565
|
+
if (imported.refreshToken) {
|
|
3566
|
+
try {
|
|
3567
|
+
log.info("[gateway-auth] Refreshing token at startup…");
|
|
3568
|
+
const refreshed = await refreshKiroToken(_creds.refreshPacked, _creds.region, _creds.authMethod);
|
|
3569
|
+
_creds.accessToken = refreshed.access;
|
|
3570
|
+
_creds.refreshPacked = refreshed.refresh;
|
|
3571
|
+
_creds.expiresAt = refreshed.expires;
|
|
3572
|
+
activeAccessToken = refreshed.access;
|
|
3573
|
+
log.info("[gateway-auth] Token refreshed on startup successfully");
|
|
3574
|
+
} catch (err) {
|
|
3575
|
+
log.warn("[gateway-auth] Startup token refresh failed, trying with existing token", err);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
2890
3578
|
try {
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
3579
|
+
if (imported.profileArn) {
|
|
3580
|
+
const apiModels = await fetchAvailableModels(activeAccessToken, imported.region, imported.profileArn);
|
|
3581
|
+
const built = buildModelsFromApi(apiModels);
|
|
3582
|
+
setCachedDynamicModels(built);
|
|
3583
|
+
log.info(`[kiro-models.fetched] Found ${built.length} available models`);
|
|
3584
|
+
} else {
|
|
3585
|
+
log.info(`[kiro-models.fetched] Skipping fetch, no profileArn available`);
|
|
3586
|
+
}
|
|
2895
3587
|
} catch (err) {
|
|
2896
3588
|
log.warn("[gateway-auth] Failed to fetch dynamic models (will fallback to static list)", err);
|
|
2897
3589
|
}
|
|
@@ -2952,6 +3644,36 @@ function startGatewayServer(port = 0) {
|
|
|
2952
3644
|
headers: { "Content-Type": "application/json" }
|
|
2953
3645
|
});
|
|
2954
3646
|
}
|
|
3647
|
+
if (url.pathname === "/auth/login" && req.method === "GET") {
|
|
3648
|
+
try {
|
|
3649
|
+
const { signInUrl, waitForCredentials } = await startSocialLogin();
|
|
3650
|
+
log.info("[gateway] Social login initiated, redirecting to Kiro sign-in");
|
|
3651
|
+
waitForCredentials().then((creds) => {
|
|
3652
|
+
_creds = {
|
|
3653
|
+
accessToken: creds.accessToken,
|
|
3654
|
+
refreshPacked: creds.refreshPacked,
|
|
3655
|
+
region: creds.region,
|
|
3656
|
+
authMethod: creds.authMethod,
|
|
3657
|
+
profileArn: creds.profileArn,
|
|
3658
|
+
expiresAt: creds.expiresAt
|
|
3659
|
+
};
|
|
3660
|
+
if (creds.profileArn) {
|
|
3661
|
+
seedProfileArn(creds.profileArn);
|
|
3662
|
+
}
|
|
3663
|
+
log.info(`[gateway] Login completed (${creds.authMethod}) — credentials updated`);
|
|
3664
|
+
}).catch((err) => {
|
|
3665
|
+
log.error("[gateway] Login failed:", err);
|
|
3666
|
+
});
|
|
3667
|
+
return new Response(null, {
|
|
3668
|
+
status: 302,
|
|
3669
|
+
headers: { Location: signInUrl }
|
|
3670
|
+
});
|
|
3671
|
+
} catch (err) {
|
|
3672
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3673
|
+
log.error("[gateway] Failed to start social login:", msg);
|
|
3674
|
+
return anthropicError(500, "api_error", `Failed to start login: ${msg}`);
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
2955
3677
|
if ((url.pathname === "/v1/messages" || url.pathname === "/messages") && req.method === "POST") {
|
|
2956
3678
|
let accessToken;
|
|
2957
3679
|
try {
|
|
@@ -3003,7 +3725,7 @@ function startGatewayServer(port = 0) {
|
|
|
3003
3725
|
const reasoningEffort = body.output_config?.effort ?? body.reasoning_effort ?? undefined;
|
|
3004
3726
|
log.info(`[gateway] → ${kiroEndpoint} model=${anthropicModelId} region=${apiRegion} stream=${streamRequested}`);
|
|
3005
3727
|
if (_creds.profileArn) {
|
|
3006
|
-
seedProfileArn(
|
|
3728
|
+
seedProfileArn(_creds.profileArn);
|
|
3007
3729
|
}
|
|
3008
3730
|
const kiroStream = streamKiro(piModel, context, {
|
|
3009
3731
|
apiKey: accessToken,
|
|
@@ -3374,6 +4096,7 @@ function translateAnthropicToolsToPi(tools) {
|
|
|
3374
4096
|
|
|
3375
4097
|
// src/index.ts
|
|
3376
4098
|
init_debug();
|
|
4099
|
+
init_models();
|
|
3377
4100
|
process.env.KIRO_LOG = process.env.KIRO_LOG || "debug";
|
|
3378
4101
|
process.env.KIRO_LOG_FILE = process.env.KIRO_LOG_FILE || "/tmp/opencode-kiro.log";
|
|
3379
4102
|
var gatewayServer = null;
|
|
@@ -3422,30 +4145,28 @@ var KiroPlugin = async (input) => {
|
|
|
3422
4145
|
label: "AWS Builder ID (personal account)",
|
|
3423
4146
|
prompts: [],
|
|
3424
4147
|
authorize: async () => {
|
|
3425
|
-
const
|
|
3426
|
-
if (!result) {
|
|
3427
|
-
throw new Error("Could not authorize with AWS Builder ID.");
|
|
3428
|
-
}
|
|
4148
|
+
const { signInUrl, waitForCredentials } = await startSocialLogin();
|
|
3429
4149
|
return {
|
|
3430
|
-
url:
|
|
3431
|
-
instructions:
|
|
3432
|
-
Complete authorization in your browser, then OpenCode will continue automatically.`,
|
|
4150
|
+
url: signInUrl,
|
|
4151
|
+
instructions: "Complete sign-in in your browser. OpenCode will continue automatically.",
|
|
3433
4152
|
method: "auto",
|
|
3434
4153
|
callback: async () => {
|
|
3435
|
-
|
|
3436
|
-
|
|
4154
|
+
try {
|
|
4155
|
+
const creds = await waitForCredentials();
|
|
4156
|
+
return {
|
|
4157
|
+
type: "success",
|
|
4158
|
+
access: creds.accessToken,
|
|
4159
|
+
refresh: creds.refreshPacked,
|
|
4160
|
+
expires: creds.expiresAt,
|
|
4161
|
+
metadata: {
|
|
4162
|
+
region: creds.region,
|
|
4163
|
+
authMethod: creds.authMethod,
|
|
4164
|
+
profileArn: creds.profileArn
|
|
4165
|
+
}
|
|
4166
|
+
};
|
|
4167
|
+
} catch {
|
|
3437
4168
|
return { type: "failed" };
|
|
3438
4169
|
}
|
|
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
4170
|
}
|
|
3450
4171
|
};
|
|
3451
4172
|
}
|
|
@@ -3496,14 +4217,25 @@ Complete authorization in your browser, then OpenCode will continue automaticall
|
|
|
3496
4217
|
if (!tok.accessToken || !tok.refreshToken) {
|
|
3497
4218
|
return { type: "failed" };
|
|
3498
4219
|
}
|
|
4220
|
+
const apiRegion = resolveApiRegion(detectedRegion);
|
|
4221
|
+
const arn = await resolveProfileArn(tok.accessToken, apiRegion);
|
|
4222
|
+
if (arn) {
|
|
4223
|
+
try {
|
|
4224
|
+
const models = await fetchAvailableModels(tok.accessToken, apiRegion, arn);
|
|
4225
|
+
setCachedDynamicModels(buildModelsFromApi(models));
|
|
4226
|
+
} catch (e) {
|
|
4227
|
+
log.warn("Failed to precache models", e);
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
3499
4230
|
return {
|
|
3500
4231
|
type: "success",
|
|
3501
4232
|
access: tok.accessToken,
|
|
3502
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc
|
|
4233
|
+
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc||`,
|
|
3503
4234
|
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
3504
4235
|
metadata: {
|
|
3505
4236
|
region: detectedRegion,
|
|
3506
|
-
authMethod: "idc"
|
|
4237
|
+
authMethod: "idc",
|
|
4238
|
+
profileArn: arn
|
|
3507
4239
|
}
|
|
3508
4240
|
};
|
|
3509
4241
|
}
|
|
@@ -3527,7 +4259,9 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3527
4259
|
imported.refreshToken,
|
|
3528
4260
|
imported.clientId || "",
|
|
3529
4261
|
imported.clientSecret || "",
|
|
3530
|
-
authMethod
|
|
4262
|
+
authMethod,
|
|
4263
|
+
imported.source || "",
|
|
4264
|
+
imported.tokenKey || ""
|
|
3531
4265
|
];
|
|
3532
4266
|
return {
|
|
3533
4267
|
url: "",
|
|
@@ -3583,7 +4317,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3583
4317
|
throw new Error("No refresh token provided.");
|
|
3584
4318
|
}
|
|
3585
4319
|
const region = inputs.region?.trim() || "us-east-1";
|
|
3586
|
-
const packed = `${refreshToken}|||desktop
|
|
4320
|
+
const packed = `${refreshToken}|||desktop||`;
|
|
3587
4321
|
return {
|
|
3588
4322
|
url: "",
|
|
3589
4323
|
method: "auto",
|
|
@@ -3611,7 +4345,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3611
4345
|
cfg.provider = cfg.provider ?? {};
|
|
3612
4346
|
cfg.provider.kiro = cfg.provider.kiro ?? {};
|
|
3613
4347
|
const kiro = cfg.provider.kiro;
|
|
3614
|
-
kiro.name = kiro.name ?? "Kiro";
|
|
4348
|
+
kiro.name = kiro.name ?? "Kiro AWS";
|
|
3615
4349
|
kiro.npm = kiro.npm ?? "@ai-sdk/anthropic";
|
|
3616
4350
|
kiro.api = kiro.api ?? `http://127.0.0.1:${localPort}/v1`;
|
|
3617
4351
|
kiro.models = kiro.models ?? {};
|