@javargasm/opencode-kiro-auth 0.1.0 → 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/README.md +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1229 -488
- 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 +5 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
3
7
|
var __export = (target, all) => {
|
|
4
8
|
for (var name in all)
|
|
5
9
|
__defProp(target, name, {
|
|
6
10
|
get: all[name],
|
|
7
11
|
enumerable: true,
|
|
8
12
|
configurable: true,
|
|
9
|
-
set: (
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
10
14
|
});
|
|
11
15
|
};
|
|
12
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -113,13 +117,430 @@ var init_debug = __esm(() => {
|
|
|
113
117
|
};
|
|
114
118
|
});
|
|
115
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
|
+
|
|
116
531
|
// src/kiro-cli-sync.ts
|
|
117
532
|
var exports_kiro_cli_sync = {};
|
|
118
533
|
__export(exports_kiro_cli_sync, {
|
|
119
|
-
|
|
534
|
+
selectKiroTokenRowForWrite: () => selectKiroTokenRowForWrite,
|
|
535
|
+
saveKiroCliCredentials: () => saveKiroCliCredentials,
|
|
536
|
+
sameKiroCliCredential: () => sameKiroCliCredential,
|
|
537
|
+
importFromKiroSsoCache: () => importFromKiroSsoCache,
|
|
538
|
+
importFromKiroCli: () => importFromKiroCli,
|
|
539
|
+
getKiroCliCredentialsAllowExpired: () => getKiroCliCredentialsAllowExpired
|
|
120
540
|
});
|
|
121
541
|
import { homedir } from "node:os";
|
|
122
542
|
import { join } from "node:path";
|
|
543
|
+
import { spawn } from "node:child_process";
|
|
123
544
|
import { existsSync, readFileSync } from "node:fs";
|
|
124
545
|
function getKiroDbPath() {
|
|
125
546
|
const home = homedir();
|
|
@@ -151,6 +572,105 @@ function safeJsonParse(value) {
|
|
|
151
572
|
return null;
|
|
152
573
|
}
|
|
153
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
|
+
}
|
|
154
674
|
function findClientCreds(obj) {
|
|
155
675
|
if (!obj || typeof obj !== "object")
|
|
156
676
|
return {};
|
|
@@ -169,6 +689,23 @@ function findClientCreds(obj) {
|
|
|
169
689
|
function isIdcTokenKey(key) {
|
|
170
690
|
return key.includes("odic") || key.includes("oidc") || key.includes("idc");
|
|
171
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
|
+
}
|
|
172
709
|
function extractRegionFromArn(arn) {
|
|
173
710
|
if (!arn)
|
|
174
711
|
return;
|
|
@@ -185,37 +722,56 @@ async function importFromKiroDb() {
|
|
|
185
722
|
return null;
|
|
186
723
|
}
|
|
187
724
|
try {
|
|
188
|
-
const { Database } = await import("bun:sqlite");
|
|
189
|
-
const db = new Database(dbPath, { readonly: true });
|
|
190
|
-
try {
|
|
191
|
-
db.exec("PRAGMA busy_timeout = 5000");
|
|
192
|
-
} catch {}
|
|
193
725
|
let rows;
|
|
726
|
+
let activeProfileArn;
|
|
727
|
+
let Database = null;
|
|
194
728
|
try {
|
|
195
|
-
|
|
729
|
+
Database = (await import("bun:sqlite")).Database;
|
|
196
730
|
} catch {
|
|
197
|
-
|
|
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 {}
|
|
198
761
|
try {
|
|
199
762
|
db.close();
|
|
200
763
|
} catch {}
|
|
201
|
-
|
|
764
|
+
} else {
|
|
765
|
+
const snapshot = await readKiroDbWithSqliteCli(dbPath);
|
|
766
|
+
if (!snapshot)
|
|
767
|
+
return null;
|
|
768
|
+
rows = snapshot.rows;
|
|
769
|
+
activeProfileArn = snapshot.activeProfileArn;
|
|
202
770
|
}
|
|
203
|
-
let activeProfileArn;
|
|
204
|
-
try {
|
|
205
|
-
const stateRow = db.prepare("SELECT value FROM state WHERE key = ?").get("api.codewhisperer.profile");
|
|
206
|
-
const parsed = safeJsonParse(stateRow?.value);
|
|
207
|
-
const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
|
|
208
|
-
if (typeof arn === "string" && arn.trim()) {
|
|
209
|
-
activeProfileArn = arn.trim();
|
|
210
|
-
}
|
|
211
|
-
} catch {}
|
|
212
|
-
try {
|
|
213
|
-
db.close();
|
|
214
|
-
} catch {}
|
|
215
771
|
const deviceRegRow = rows.find((r) => typeof r?.key === "string" && r.key.includes("device-registration"));
|
|
216
772
|
const deviceReg = safeJsonParse(deviceRegRow?.value);
|
|
217
773
|
const regCreds = deviceReg ? findClientCreds(deviceReg) : {};
|
|
218
|
-
const tokenRows = rows.filter(
|
|
774
|
+
const tokenRows = rows.filter(isTokenRow).sort((a, b) => tokenReadRank(a) - tokenReadRank(b));
|
|
219
775
|
for (const row of tokenRows) {
|
|
220
776
|
const data = safeJsonParse(row.value);
|
|
221
777
|
if (!data)
|
|
@@ -228,16 +784,19 @@ async function importFromKiroDb() {
|
|
|
228
784
|
const authMethod = isIdc ? "idc" : "desktop";
|
|
229
785
|
const oidcRegion = data.region || "us-east-1";
|
|
230
786
|
let profileArn = data.profile_arn || data.profileArn;
|
|
231
|
-
if (!profileArn)
|
|
787
|
+
if (!profileArn && isIdc) {
|
|
232
788
|
profileArn = activeProfileArn;
|
|
789
|
+
}
|
|
233
790
|
const serviceRegion = extractRegionFromArn(profileArn) || oidcRegion;
|
|
234
791
|
const result = {
|
|
235
792
|
accessToken: accessToken || "",
|
|
236
793
|
refreshToken: refreshToken || "",
|
|
237
794
|
region: serviceRegion,
|
|
238
795
|
authMethod,
|
|
796
|
+
profileArn,
|
|
239
797
|
email: data.email || data.emailAddress,
|
|
240
|
-
|
|
798
|
+
source: "kiro-cli-db",
|
|
799
|
+
tokenKey: row.key
|
|
241
800
|
};
|
|
242
801
|
if (isIdc && regCreds.clientId) {
|
|
243
802
|
result.clientId = regCreds.clientId;
|
|
@@ -253,30 +812,48 @@ async function importFromKiroDb() {
|
|
|
253
812
|
return null;
|
|
254
813
|
}
|
|
255
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
|
+
}
|
|
256
823
|
async function importFromKiroSsoCache() {
|
|
257
|
-
const
|
|
258
|
-
if (!existsSync(
|
|
259
|
-
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}`);
|
|
260
827
|
return null;
|
|
261
828
|
}
|
|
262
829
|
let raw;
|
|
263
830
|
try {
|
|
264
|
-
raw = readFileSync(
|
|
831
|
+
raw = readFileSync(path, "utf8");
|
|
265
832
|
} catch (err) {
|
|
266
|
-
log.warn(`Failed to read Kiro SSO cache at ${
|
|
833
|
+
log.warn(`Failed to read Kiro SSO cache at ${path}: ${err}`);
|
|
267
834
|
return null;
|
|
268
835
|
}
|
|
269
836
|
const token = safeJsonParse(raw);
|
|
270
|
-
if (!token || typeof token !== "object")
|
|
837
|
+
if (!token || typeof token !== "object") {
|
|
838
|
+
log.debug(`Kiro SSO cache at ${path} is not valid JSON`);
|
|
271
839
|
return null;
|
|
840
|
+
}
|
|
272
841
|
const accessToken = typeof token.accessToken === "string" ? token.accessToken : "";
|
|
273
842
|
const refreshToken = typeof token.refreshToken === "string" ? token.refreshToken : "";
|
|
274
|
-
if (!accessToken && !refreshToken)
|
|
843
|
+
if (!accessToken && !refreshToken) {
|
|
844
|
+
log.debug(`Kiro SSO cache at ${path} has no tokens`);
|
|
275
845
|
return null;
|
|
846
|
+
}
|
|
276
847
|
const region = typeof token.region === "string" && token.region.length > 0 ? token.region : "us-east-1";
|
|
277
|
-
const authMethod =
|
|
278
|
-
log.info(`Imported Kiro SSO cache credentials (region=${region})`);
|
|
279
|
-
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
|
+
};
|
|
280
857
|
}
|
|
281
858
|
async function importFromKiroCli() {
|
|
282
859
|
const dbResult = await importFromKiroDb();
|
|
@@ -284,6 +861,98 @@ async function importFromKiroCli() {
|
|
|
284
861
|
return dbResult;
|
|
285
862
|
return importFromKiroSsoCache();
|
|
286
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;
|
|
287
956
|
var init_kiro_cli_sync = __esm(() => {
|
|
288
957
|
init_debug();
|
|
289
958
|
});
|
|
@@ -612,372 +1281,8 @@ function isPermanentError(reason) {
|
|
|
612
1281
|
return PERMANENT_PATTERNS.some((p) => reason.includes(p));
|
|
613
1282
|
}
|
|
614
1283
|
|
|
615
|
-
// src/
|
|
616
|
-
|
|
617
|
-
"claude-fable-5",
|
|
618
|
-
"claude-opus-4.8",
|
|
619
|
-
"claude-opus-4.7",
|
|
620
|
-
"claude-opus-4.6",
|
|
621
|
-
"claude-opus-4.6-1m",
|
|
622
|
-
"claude-sonnet-4.6",
|
|
623
|
-
"claude-sonnet-4.6-1m",
|
|
624
|
-
"claude-opus-4.5",
|
|
625
|
-
"claude-sonnet-4.5",
|
|
626
|
-
"claude-sonnet-4.5-1m",
|
|
627
|
-
"claude-sonnet-4",
|
|
628
|
-
"claude-haiku-4.5",
|
|
629
|
-
"deepseek-3.2",
|
|
630
|
-
"kimi-k2.5",
|
|
631
|
-
"minimax-m2.1",
|
|
632
|
-
"minimax-m2.5",
|
|
633
|
-
"glm-4.7",
|
|
634
|
-
"glm-4.7-flash",
|
|
635
|
-
"qwen3-coder-next",
|
|
636
|
-
"agi-nova-beta-1m",
|
|
637
|
-
"qwen3-coder-480b",
|
|
638
|
-
"auto"
|
|
639
|
-
]);
|
|
640
|
-
function dotToDash(modelId) {
|
|
641
|
-
return modelId.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
642
|
-
}
|
|
643
|
-
function resolveKiroModel(modelId) {
|
|
644
|
-
const kiroId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
645
|
-
if (!KIRO_MODEL_IDS.has(kiroId)) {
|
|
646
|
-
throw new Error(`Unknown Kiro model ID: ${modelId}`);
|
|
647
|
-
}
|
|
648
|
-
return kiroId;
|
|
649
|
-
}
|
|
650
|
-
var API_REGION_MAP = {
|
|
651
|
-
"us-west-1": "us-east-1",
|
|
652
|
-
"us-west-2": "us-east-1",
|
|
653
|
-
"us-east-2": "us-east-1",
|
|
654
|
-
"eu-west-1": "eu-central-1",
|
|
655
|
-
"eu-west-2": "eu-central-1",
|
|
656
|
-
"eu-west-3": "eu-central-1",
|
|
657
|
-
"eu-north-1": "eu-central-1",
|
|
658
|
-
"eu-south-1": "eu-central-1",
|
|
659
|
-
"eu-south-2": "eu-central-1",
|
|
660
|
-
"eu-central-2": "eu-central-1",
|
|
661
|
-
"ap-northeast-1": "us-east-1",
|
|
662
|
-
"ap-northeast-2": "us-east-1",
|
|
663
|
-
"ap-northeast-3": "us-east-1",
|
|
664
|
-
"ap-southeast-1": "us-east-1",
|
|
665
|
-
"ap-southeast-2": "us-east-1",
|
|
666
|
-
"ap-south-1": "us-east-1",
|
|
667
|
-
"ap-east-1": "us-east-1",
|
|
668
|
-
"ap-south-2": "us-east-1",
|
|
669
|
-
"ap-southeast-3": "us-east-1",
|
|
670
|
-
"ap-southeast-4": "us-east-1"
|
|
671
|
-
};
|
|
672
|
-
function resolveApiRegion(ssoRegion) {
|
|
673
|
-
if (!ssoRegion)
|
|
674
|
-
return "us-east-1";
|
|
675
|
-
return API_REGION_MAP[ssoRegion] ?? ssoRegion;
|
|
676
|
-
}
|
|
677
|
-
var BASE_URL = "https://runtime.us-east-1.kiro.dev";
|
|
678
|
-
var ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
679
|
-
var KIRO_DEFAULTS = {
|
|
680
|
-
api: "kiro-api",
|
|
681
|
-
provider: "kiro",
|
|
682
|
-
baseUrl: BASE_URL,
|
|
683
|
-
cost: ZERO_COST
|
|
684
|
-
};
|
|
685
|
-
var MULTIMODAL = ["text", "image"];
|
|
686
|
-
var TEXT_ONLY = ["text"];
|
|
687
|
-
var kiroModels = [
|
|
688
|
-
{
|
|
689
|
-
...KIRO_DEFAULTS,
|
|
690
|
-
id: "claude-fable-5",
|
|
691
|
-
name: "Claude Fable 5",
|
|
692
|
-
reasoning: true,
|
|
693
|
-
input: MULTIMODAL,
|
|
694
|
-
contextWindow: 1e6,
|
|
695
|
-
maxTokens: 128000,
|
|
696
|
-
firstTokenTimeout: 180000,
|
|
697
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
698
|
-
supportsThinkingConfig: true
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
...KIRO_DEFAULTS,
|
|
702
|
-
id: "claude-opus-4-8",
|
|
703
|
-
name: "Claude Opus 4.8",
|
|
704
|
-
reasoning: true,
|
|
705
|
-
input: MULTIMODAL,
|
|
706
|
-
contextWindow: 1e6,
|
|
707
|
-
maxTokens: 128000,
|
|
708
|
-
firstTokenTimeout: 180000,
|
|
709
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
710
|
-
supportsThinkingConfig: true
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
...KIRO_DEFAULTS,
|
|
714
|
-
id: "claude-opus-4-7",
|
|
715
|
-
name: "Claude Opus 4.7",
|
|
716
|
-
reasoning: true,
|
|
717
|
-
input: MULTIMODAL,
|
|
718
|
-
contextWindow: 1e6,
|
|
719
|
-
maxTokens: 128000,
|
|
720
|
-
firstTokenTimeout: 180000,
|
|
721
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
722
|
-
supportsThinkingConfig: true
|
|
723
|
-
},
|
|
724
|
-
{
|
|
725
|
-
...KIRO_DEFAULTS,
|
|
726
|
-
id: "claude-opus-4-6",
|
|
727
|
-
name: "Claude Opus 4.6",
|
|
728
|
-
reasoning: true,
|
|
729
|
-
input: MULTIMODAL,
|
|
730
|
-
contextWindow: 1e6,
|
|
731
|
-
maxTokens: 64000,
|
|
732
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
733
|
-
supportsThinkingConfig: true
|
|
734
|
-
},
|
|
735
|
-
{
|
|
736
|
-
...KIRO_DEFAULTS,
|
|
737
|
-
id: "claude-opus-4-6-1m",
|
|
738
|
-
name: "Claude Opus 4.6 (1M)",
|
|
739
|
-
reasoning: true,
|
|
740
|
-
input: MULTIMODAL,
|
|
741
|
-
contextWindow: 1e6,
|
|
742
|
-
maxTokens: 64000,
|
|
743
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
744
|
-
supportsThinkingConfig: true
|
|
745
|
-
},
|
|
746
|
-
{
|
|
747
|
-
...KIRO_DEFAULTS,
|
|
748
|
-
id: "claude-sonnet-4-6",
|
|
749
|
-
name: "Claude Sonnet 4.6",
|
|
750
|
-
reasoning: true,
|
|
751
|
-
input: MULTIMODAL,
|
|
752
|
-
contextWindow: 1e6,
|
|
753
|
-
maxTokens: 64000,
|
|
754
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
755
|
-
supportsThinkingConfig: true
|
|
756
|
-
},
|
|
757
|
-
{
|
|
758
|
-
...KIRO_DEFAULTS,
|
|
759
|
-
id: "claude-sonnet-4-6-1m",
|
|
760
|
-
name: "Claude Sonnet 4.6 (1M)",
|
|
761
|
-
reasoning: true,
|
|
762
|
-
input: MULTIMODAL,
|
|
763
|
-
contextWindow: 1e6,
|
|
764
|
-
maxTokens: 64000,
|
|
765
|
-
supportedEfforts: ["low", "medium", "high", "max"],
|
|
766
|
-
supportsThinkingConfig: true
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
...KIRO_DEFAULTS,
|
|
770
|
-
id: "claude-opus-4-5",
|
|
771
|
-
name: "Claude Opus 4.5",
|
|
772
|
-
reasoning: true,
|
|
773
|
-
input: MULTIMODAL,
|
|
774
|
-
contextWindow: 200000,
|
|
775
|
-
maxTokens: 64000
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
...KIRO_DEFAULTS,
|
|
779
|
-
id: "claude-sonnet-4-5",
|
|
780
|
-
name: "Claude Sonnet 4.5",
|
|
781
|
-
reasoning: true,
|
|
782
|
-
input: MULTIMODAL,
|
|
783
|
-
contextWindow: 200000,
|
|
784
|
-
maxTokens: 65536
|
|
785
|
-
},
|
|
786
|
-
{
|
|
787
|
-
...KIRO_DEFAULTS,
|
|
788
|
-
id: "claude-sonnet-4-5-1m",
|
|
789
|
-
name: "Claude Sonnet 4.5 (1M)",
|
|
790
|
-
reasoning: true,
|
|
791
|
-
input: MULTIMODAL,
|
|
792
|
-
contextWindow: 1e6,
|
|
793
|
-
maxTokens: 65536
|
|
794
|
-
},
|
|
795
|
-
{
|
|
796
|
-
...KIRO_DEFAULTS,
|
|
797
|
-
id: "claude-sonnet-4",
|
|
798
|
-
name: "Claude Sonnet 4",
|
|
799
|
-
reasoning: true,
|
|
800
|
-
input: MULTIMODAL,
|
|
801
|
-
contextWindow: 200000,
|
|
802
|
-
maxTokens: 65536
|
|
803
|
-
},
|
|
804
|
-
{
|
|
805
|
-
...KIRO_DEFAULTS,
|
|
806
|
-
id: "claude-haiku-4-5",
|
|
807
|
-
name: "Claude Haiku 4.5",
|
|
808
|
-
reasoning: false,
|
|
809
|
-
input: MULTIMODAL,
|
|
810
|
-
contextWindow: 200000,
|
|
811
|
-
maxTokens: 65536
|
|
812
|
-
},
|
|
813
|
-
{
|
|
814
|
-
...KIRO_DEFAULTS,
|
|
815
|
-
id: "deepseek-3-2",
|
|
816
|
-
name: "DeepSeek 3.2",
|
|
817
|
-
reasoning: true,
|
|
818
|
-
input: TEXT_ONLY,
|
|
819
|
-
contextWindow: 128000,
|
|
820
|
-
maxTokens: 8192
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
...KIRO_DEFAULTS,
|
|
824
|
-
id: "kimi-k2-5",
|
|
825
|
-
name: "Kimi K2.5",
|
|
826
|
-
reasoning: true,
|
|
827
|
-
input: TEXT_ONLY,
|
|
828
|
-
contextWindow: 200000,
|
|
829
|
-
maxTokens: 8192
|
|
830
|
-
},
|
|
831
|
-
{
|
|
832
|
-
...KIRO_DEFAULTS,
|
|
833
|
-
id: "minimax-m2-5",
|
|
834
|
-
name: "MiniMax M2.5",
|
|
835
|
-
reasoning: false,
|
|
836
|
-
input: TEXT_ONLY,
|
|
837
|
-
contextWindow: 196000,
|
|
838
|
-
maxTokens: 64000
|
|
839
|
-
},
|
|
840
|
-
{
|
|
841
|
-
...KIRO_DEFAULTS,
|
|
842
|
-
id: "minimax-m2-1",
|
|
843
|
-
name: "MiniMax M2.1",
|
|
844
|
-
reasoning: false,
|
|
845
|
-
input: MULTIMODAL,
|
|
846
|
-
contextWindow: 196000,
|
|
847
|
-
maxTokens: 64000
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
...KIRO_DEFAULTS,
|
|
851
|
-
id: "glm-4-7",
|
|
852
|
-
name: "GLM 4.7",
|
|
853
|
-
reasoning: true,
|
|
854
|
-
input: TEXT_ONLY,
|
|
855
|
-
contextWindow: 128000,
|
|
856
|
-
maxTokens: 8192
|
|
857
|
-
},
|
|
858
|
-
{
|
|
859
|
-
...KIRO_DEFAULTS,
|
|
860
|
-
id: "glm-4-7-flash",
|
|
861
|
-
name: "GLM 4.7 Flash",
|
|
862
|
-
reasoning: false,
|
|
863
|
-
input: TEXT_ONLY,
|
|
864
|
-
contextWindow: 128000,
|
|
865
|
-
maxTokens: 8192
|
|
866
|
-
},
|
|
867
|
-
{
|
|
868
|
-
...KIRO_DEFAULTS,
|
|
869
|
-
id: "qwen3-coder-next",
|
|
870
|
-
name: "Qwen3 Coder Next",
|
|
871
|
-
reasoning: true,
|
|
872
|
-
input: MULTIMODAL,
|
|
873
|
-
contextWindow: 256000,
|
|
874
|
-
maxTokens: 64000
|
|
875
|
-
},
|
|
876
|
-
{
|
|
877
|
-
...KIRO_DEFAULTS,
|
|
878
|
-
id: "qwen3-coder-480b",
|
|
879
|
-
name: "Qwen3 Coder 480B",
|
|
880
|
-
reasoning: true,
|
|
881
|
-
input: TEXT_ONLY,
|
|
882
|
-
contextWindow: 128000,
|
|
883
|
-
maxTokens: 8192
|
|
884
|
-
},
|
|
885
|
-
{
|
|
886
|
-
...KIRO_DEFAULTS,
|
|
887
|
-
id: "agi-nova-beta-1m",
|
|
888
|
-
name: "AGI Nova Beta (1M)",
|
|
889
|
-
reasoning: true,
|
|
890
|
-
input: MULTIMODAL,
|
|
891
|
-
contextWindow: 1e6,
|
|
892
|
-
maxTokens: 65536
|
|
893
|
-
},
|
|
894
|
-
{
|
|
895
|
-
...KIRO_DEFAULTS,
|
|
896
|
-
id: "auto",
|
|
897
|
-
name: "Auto",
|
|
898
|
-
reasoning: true,
|
|
899
|
-
input: MULTIMODAL,
|
|
900
|
-
contextWindow: 200000,
|
|
901
|
-
maxTokens: 65536
|
|
902
|
-
}
|
|
903
|
-
];
|
|
904
|
-
async function fetchAvailableModels(accessToken, apiRegion, fallbackProfileArn) {
|
|
905
|
-
const runtimeUrl = `https://runtime.${apiRegion}.kiro.dev/`;
|
|
906
|
-
let profileArn = await resolveProfileArn(accessToken, runtimeUrl);
|
|
907
|
-
if (!profileArn && fallbackProfileArn) {
|
|
908
|
-
profileArn = fallbackProfileArn;
|
|
909
|
-
}
|
|
910
|
-
if (!profileArn) {
|
|
911
|
-
throw new Error("Missing profileArn: cannot fetch available models.");
|
|
912
|
-
}
|
|
913
|
-
const url = `https://management.${apiRegion}.kiro.dev/ListAvailableModels?origin=KIRO_CLI&profileArn=${encodeURIComponent(profileArn)}`;
|
|
914
|
-
const resp = await fetch(url, {
|
|
915
|
-
method: "GET",
|
|
916
|
-
headers: {
|
|
917
|
-
Authorization: `Bearer ${accessToken}`,
|
|
918
|
-
Accept: "application/json",
|
|
919
|
-
"User-Agent": "opencode-kiro"
|
|
920
|
-
}
|
|
921
|
-
});
|
|
922
|
-
if (!resp.ok) {
|
|
923
|
-
throw new Error(`ListAvailableModels failed: HTTP ${resp.status}`);
|
|
924
|
-
}
|
|
925
|
-
const data = await resp.json();
|
|
926
|
-
return (data.models ?? []).filter((m) => m.modelId !== "auto");
|
|
927
|
-
}
|
|
928
|
-
var REASONING_FAMILIES = new Set([
|
|
929
|
-
"claude-fable",
|
|
930
|
-
"claude-sonnet",
|
|
931
|
-
"claude-opus",
|
|
932
|
-
"deepseek",
|
|
933
|
-
"kimi",
|
|
934
|
-
"glm",
|
|
935
|
-
"qwen",
|
|
936
|
-
"agi-nova",
|
|
937
|
-
"minimax"
|
|
938
|
-
]);
|
|
939
|
-
function isReasoningModel(dotId) {
|
|
940
|
-
for (const family of REASONING_FAMILIES) {
|
|
941
|
-
if (dotId.startsWith(family))
|
|
942
|
-
return true;
|
|
943
|
-
}
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
function firstTokenTimeout(dotId) {
|
|
947
|
-
if (dotId.startsWith("claude-fable") || dotId.startsWith("claude-opus"))
|
|
948
|
-
return 180000;
|
|
949
|
-
return 90000;
|
|
950
|
-
}
|
|
951
|
-
function buildModelsFromApi(apiModels) {
|
|
952
|
-
return apiModels.map((m) => {
|
|
953
|
-
KIRO_MODEL_IDS.add(m.modelId);
|
|
954
|
-
const dashId = dotToDash(m.modelId);
|
|
955
|
-
const supportedTypes = m.supportedInputTypes ?? ["TEXT"];
|
|
956
|
-
const input = supportedTypes.includes("IMAGE") ? ["text", "image"] : ["text"];
|
|
957
|
-
const effortEnum = m.additionalModelRequestFieldsSchema?.properties?.output_config?.properties?.effort?.enum;
|
|
958
|
-
const supportedEfforts = Array.isArray(effortEnum) && effortEnum.length > 0 ? effortEnum : undefined;
|
|
959
|
-
const supportsThinkingConfig = !!m.additionalModelRequestFieldsSchema?.properties?.thinking;
|
|
960
|
-
return {
|
|
961
|
-
...KIRO_DEFAULTS,
|
|
962
|
-
id: dashId,
|
|
963
|
-
name: m.modelName,
|
|
964
|
-
reasoning: isReasoningModel(m.modelId),
|
|
965
|
-
input,
|
|
966
|
-
contextWindow: m.tokenLimits?.maxInputTokens ?? 200000,
|
|
967
|
-
maxTokens: m.tokenLimits?.maxOutputTokens ?? 8192,
|
|
968
|
-
firstTokenTimeout: firstTokenTimeout(m.modelId),
|
|
969
|
-
...supportedEfforts ? { supportedEfforts } : {},
|
|
970
|
-
...supportsThinkingConfig ? { supportsThinkingConfig } : {}
|
|
971
|
-
};
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
var cachedDynamicModels = null;
|
|
975
|
-
function getCachedDynamicModels() {
|
|
976
|
-
return cachedDynamicModels;
|
|
977
|
-
}
|
|
978
|
-
function setCachedDynamicModels(models) {
|
|
979
|
-
cachedDynamicModels = models;
|
|
980
|
-
}
|
|
1284
|
+
// src/stream.ts
|
|
1285
|
+
init_models();
|
|
981
1286
|
|
|
982
1287
|
// src/thinking-parser.ts
|
|
983
1288
|
init_debug();
|
|
@@ -1214,7 +1519,9 @@ function countTokens(text) {
|
|
|
1214
1519
|
|
|
1215
1520
|
// src/oauth.ts
|
|
1216
1521
|
init_debug();
|
|
1217
|
-
|
|
1522
|
+
init_models();
|
|
1523
|
+
import { createServer } from "node:http";
|
|
1524
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
1218
1525
|
var BUILDER_ID_REGION = "us-east-1";
|
|
1219
1526
|
var SSO_SCOPES = [
|
|
1220
1527
|
"codewhisperer:completions",
|
|
@@ -1333,10 +1640,12 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1333
1640
|
const refreshToken = parts[0] ?? "";
|
|
1334
1641
|
const clientId = parts[1] ?? "";
|
|
1335
1642
|
const clientSecret = parts[2] ?? "";
|
|
1643
|
+
const source = parts[4] ?? "";
|
|
1644
|
+
const tokenKey = parts[5] ?? "";
|
|
1336
1645
|
if (!refreshToken || !region) {
|
|
1337
1646
|
throw new Error("Refresh token or region is missing — re-login required");
|
|
1338
1647
|
}
|
|
1339
|
-
if (authMethod === "desktop") {
|
|
1648
|
+
if (authMethod === "desktop" || authMethod === "social") {
|
|
1340
1649
|
const desktopEndpoint = `https://prod.${region}.auth.desktop.kiro.dev/refreshToken`;
|
|
1341
1650
|
const resp2 = await fetch(desktopEndpoint, {
|
|
1342
1651
|
method: "POST",
|
|
@@ -1348,9 +1657,23 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1348
1657
|
throw new Error(`Desktop token refresh failed: ${resp2.status} ${body}`);
|
|
1349
1658
|
}
|
|
1350
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
|
+
}
|
|
1351
1674
|
return {
|
|
1352
1675
|
access: data2.accessToken,
|
|
1353
|
-
refresh:
|
|
1676
|
+
refresh: newPacked2,
|
|
1354
1677
|
expires: Date.now() + (data2.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
1355
1678
|
};
|
|
1356
1679
|
}
|
|
@@ -1368,15 +1691,411 @@ async function refreshKiroToken(refreshTokenPacked, region, authMethod) {
|
|
|
1368
1691
|
throw new Error(`Token refresh failed: ${resp.status} ${body}`);
|
|
1369
1692
|
}
|
|
1370
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
|
+
}
|
|
1371
1708
|
return {
|
|
1372
1709
|
access: data.accessToken,
|
|
1373
|
-
refresh:
|
|
1710
|
+
refresh: newPacked,
|
|
1374
1711
|
expires: Date.now() + (data.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS
|
|
1375
1712
|
};
|
|
1376
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
|
+
}
|
|
1377
2096
|
|
|
1378
2097
|
// src/transform.ts
|
|
1379
|
-
import { createHash } from "node:crypto";
|
|
2098
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1380
2099
|
|
|
1381
2100
|
// src/kiro-defaults.ts
|
|
1382
2101
|
var SYSTEM_SEED_INSTRUCTION = `Follow this instruction: # Kiro CLI Default Agent
|
|
@@ -1459,7 +2178,7 @@ var KIRO_TOOL_USE_ID_RE = /^tooluse_[A-Za-z0-9]+$/;
|
|
|
1459
2178
|
function toKiroToolUseId(id) {
|
|
1460
2179
|
if (KIRO_TOOL_USE_ID_RE.test(id))
|
|
1461
2180
|
return id;
|
|
1462
|
-
const digest =
|
|
2181
|
+
const digest = createHash2("sha256").update(id).digest("hex").slice(0, 22);
|
|
1463
2182
|
return `tooluse_${digest}`;
|
|
1464
2183
|
}
|
|
1465
2184
|
var ALLOWED_SCHEMA_KEYS = new Set([
|
|
@@ -1790,49 +2509,6 @@ function emitHiddenReasoningLate(output, stream) {
|
|
|
1790
2509
|
partial: output
|
|
1791
2510
|
});
|
|
1792
2511
|
}
|
|
1793
|
-
var profileArnCache = new Map;
|
|
1794
|
-
var profileArnSkipResolution = false;
|
|
1795
|
-
function seedProfileArn(endpoint, arn) {
|
|
1796
|
-
profileArnCache.set(endpoint, arn);
|
|
1797
|
-
}
|
|
1798
|
-
async function resolveProfileArn(accessToken, endpoint) {
|
|
1799
|
-
if (profileArnSkipResolution)
|
|
1800
|
-
return;
|
|
1801
|
-
const cached = profileArnCache.get(endpoint);
|
|
1802
|
-
if (cached !== undefined)
|
|
1803
|
-
return cached;
|
|
1804
|
-
try {
|
|
1805
|
-
const ep = new URL(endpoint);
|
|
1806
|
-
ep.hostname = ep.hostname.replace("runtime.", "management.");
|
|
1807
|
-
ep.pathname = "/";
|
|
1808
|
-
ep.search = "";
|
|
1809
|
-
ep.hash = "";
|
|
1810
|
-
const resp = await fetch(ep.toString(), {
|
|
1811
|
-
method: "POST",
|
|
1812
|
-
headers: {
|
|
1813
|
-
"Content-Type": "application/x-amz-json-1.0",
|
|
1814
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1815
|
-
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableProfiles"
|
|
1816
|
-
},
|
|
1817
|
-
body: "{}"
|
|
1818
|
-
});
|
|
1819
|
-
if (!resp.ok) {
|
|
1820
|
-
log.debug(`profileArn resolution failed: ${resp.status} ${resp.statusText}`);
|
|
1821
|
-
return;
|
|
1822
|
-
}
|
|
1823
|
-
const j = await resp.json();
|
|
1824
|
-
const arn = j.profiles?.find((p) => p.arn)?.arn;
|
|
1825
|
-
if (!arn) {
|
|
1826
|
-
log.debug("profileArn resolution returned no profile ARN");
|
|
1827
|
-
return;
|
|
1828
|
-
}
|
|
1829
|
-
profileArnCache.set(endpoint, arn);
|
|
1830
|
-
return arn;
|
|
1831
|
-
} catch (error) {
|
|
1832
|
-
log.debug(`profileArn resolution threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
1833
|
-
return;
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
2512
|
function emitToolCall(state, output, stream) {
|
|
1837
2513
|
if (!state.input.trim())
|
|
1838
2514
|
state.input = "{}";
|
|
@@ -2115,8 +2791,9 @@ ${currentContent}`;
|
|
|
2115
2791
|
let transientRetryCount = 0;
|
|
2116
2792
|
let contextTruncationAttempt = 0;
|
|
2117
2793
|
while (true) {
|
|
2118
|
-
const
|
|
2119
|
-
const ua = `aws-sdk-rust/1.
|
|
2794
|
+
const osName = resolveOS();
|
|
2795
|
+
const ua = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 md/appVersion-2.7.1 app/AmazonQ-For-CLI`;
|
|
2796
|
+
const xAmzUa = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 m/F app/AmazonQ-For-CLI`;
|
|
2120
2797
|
const requestBody = JSON.stringify(request);
|
|
2121
2798
|
log.debug("request.send", {
|
|
2122
2799
|
attempt: retryCount,
|
|
@@ -2137,15 +2814,17 @@ ${currentContent}`;
|
|
|
2137
2814
|
method: "POST",
|
|
2138
2815
|
headers: {
|
|
2139
2816
|
"Content-Type": "application/x-amz-json-1.0",
|
|
2140
|
-
Accept: "
|
|
2817
|
+
Accept: "*/*",
|
|
2818
|
+
"Accept-Encoding": "gzip",
|
|
2141
2819
|
Authorization: `Bearer ${accessToken}`,
|
|
2142
2820
|
"X-Amz-Target": "AmazonCodeWhispererStreamingService.GenerateAssistantResponse",
|
|
2143
2821
|
"x-amzn-codewhisperer-optout": "true",
|
|
2144
2822
|
"amz-sdk-invocation-id": crypto.randomUUID(),
|
|
2145
|
-
"amz-sdk-request": "attempt=1; max=
|
|
2146
|
-
"
|
|
2147
|
-
"x-amz-user-agent":
|
|
2148
|
-
"
|
|
2823
|
+
"amz-sdk-request": "attempt=1; max=3",
|
|
2824
|
+
"user-agent": ua,
|
|
2825
|
+
"x-amz-user-agent": xAmzUa,
|
|
2826
|
+
Pragma: "no-cache",
|
|
2827
|
+
"Cache-Control": "no-cache"
|
|
2149
2828
|
},
|
|
2150
2829
|
body: requestBody,
|
|
2151
2830
|
signal: options?.signal
|
|
@@ -2195,12 +2874,12 @@ ${currentContent}`;
|
|
|
2195
2874
|
if (response.status === 401) {
|
|
2196
2875
|
const permanent = isPermanentError(errText);
|
|
2197
2876
|
if (permanent) {
|
|
2198
|
-
|
|
2877
|
+
resetProfileArnCache();
|
|
2199
2878
|
throw new Error(`Kiro API error: credentials permanently invalid — run /login kiro to re-authenticate. ${errText}`);
|
|
2200
2879
|
}
|
|
2201
2880
|
}
|
|
2202
2881
|
if (response.status === 403) {
|
|
2203
|
-
|
|
2882
|
+
resetProfileArnCache();
|
|
2204
2883
|
throw new Error(`Kiro API error: access token rejected (403) — run /login kiro to re-authenticate. ${errText}`);
|
|
2205
2884
|
}
|
|
2206
2885
|
throw new Error(`Kiro API error: ${response.status} ${response.statusText} ${errText}`);
|
|
@@ -2508,6 +3187,8 @@ ${currentContent}`;
|
|
|
2508
3187
|
|
|
2509
3188
|
// src/server.ts
|
|
2510
3189
|
init_debug();
|
|
3190
|
+
init_models();
|
|
3191
|
+
init_models();
|
|
2511
3192
|
|
|
2512
3193
|
// src/dashboard-stats.ts
|
|
2513
3194
|
var MAX_HISTORY = 100;
|
|
@@ -2880,11 +3561,29 @@ async function initGatewayAuth() {
|
|
|
2880
3561
|
expiresAt: Date.now() + 3500000
|
|
2881
3562
|
};
|
|
2882
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
|
+
}
|
|
2883
3578
|
try {
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
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
|
+
}
|
|
2888
3587
|
} catch (err) {
|
|
2889
3588
|
log.warn("[gateway-auth] Failed to fetch dynamic models (will fallback to static list)", err);
|
|
2890
3589
|
}
|
|
@@ -2945,6 +3644,36 @@ function startGatewayServer(port = 0) {
|
|
|
2945
3644
|
headers: { "Content-Type": "application/json" }
|
|
2946
3645
|
});
|
|
2947
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
|
+
}
|
|
2948
3677
|
if ((url.pathname === "/v1/messages" || url.pathname === "/messages") && req.method === "POST") {
|
|
2949
3678
|
let accessToken;
|
|
2950
3679
|
try {
|
|
@@ -2996,7 +3725,7 @@ function startGatewayServer(port = 0) {
|
|
|
2996
3725
|
const reasoningEffort = body.output_config?.effort ?? body.reasoning_effort ?? undefined;
|
|
2997
3726
|
log.info(`[gateway] → ${kiroEndpoint} model=${anthropicModelId} region=${apiRegion} stream=${streamRequested}`);
|
|
2998
3727
|
if (_creds.profileArn) {
|
|
2999
|
-
seedProfileArn(
|
|
3728
|
+
seedProfileArn(_creds.profileArn);
|
|
3000
3729
|
}
|
|
3001
3730
|
const kiroStream = streamKiro(piModel, context, {
|
|
3002
3731
|
apiKey: accessToken,
|
|
@@ -3367,6 +4096,7 @@ function translateAnthropicToolsToPi(tools) {
|
|
|
3367
4096
|
|
|
3368
4097
|
// src/index.ts
|
|
3369
4098
|
init_debug();
|
|
4099
|
+
init_models();
|
|
3370
4100
|
process.env.KIRO_LOG = process.env.KIRO_LOG || "debug";
|
|
3371
4101
|
process.env.KIRO_LOG_FILE = process.env.KIRO_LOG_FILE || "/tmp/opencode-kiro.log";
|
|
3372
4102
|
var gatewayServer = null;
|
|
@@ -3415,30 +4145,28 @@ var KiroPlugin = async (input) => {
|
|
|
3415
4145
|
label: "AWS Builder ID (personal account)",
|
|
3416
4146
|
prompts: [],
|
|
3417
4147
|
authorize: async () => {
|
|
3418
|
-
const
|
|
3419
|
-
if (!result) {
|
|
3420
|
-
throw new Error("Could not authorize with AWS Builder ID.");
|
|
3421
|
-
}
|
|
4148
|
+
const { signInUrl, waitForCredentials } = await startSocialLogin();
|
|
3422
4149
|
return {
|
|
3423
|
-
url:
|
|
3424
|
-
instructions:
|
|
3425
|
-
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.",
|
|
3426
4152
|
method: "auto",
|
|
3427
4153
|
callback: async () => {
|
|
3428
|
-
|
|
3429
|
-
|
|
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 {
|
|
3430
4168
|
return { type: "failed" };
|
|
3431
4169
|
}
|
|
3432
|
-
return {
|
|
3433
|
-
type: "success",
|
|
3434
|
-
access: tok.accessToken,
|
|
3435
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|builder-id`,
|
|
3436
|
-
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
3437
|
-
metadata: {
|
|
3438
|
-
region: BUILDER_ID_REGION,
|
|
3439
|
-
authMethod: "builder-id"
|
|
3440
|
-
}
|
|
3441
|
-
};
|
|
3442
4170
|
}
|
|
3443
4171
|
};
|
|
3444
4172
|
}
|
|
@@ -3489,14 +4217,25 @@ Complete authorization in your browser, then OpenCode will continue automaticall
|
|
|
3489
4217
|
if (!tok.accessToken || !tok.refreshToken) {
|
|
3490
4218
|
return { type: "failed" };
|
|
3491
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
|
+
}
|
|
3492
4230
|
return {
|
|
3493
4231
|
type: "success",
|
|
3494
4232
|
access: tok.accessToken,
|
|
3495
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc
|
|
4233
|
+
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|idc||`,
|
|
3496
4234
|
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
3497
4235
|
metadata: {
|
|
3498
4236
|
region: detectedRegion,
|
|
3499
|
-
authMethod: "idc"
|
|
4237
|
+
authMethod: "idc",
|
|
4238
|
+
profileArn: arn
|
|
3500
4239
|
}
|
|
3501
4240
|
};
|
|
3502
4241
|
}
|
|
@@ -3520,7 +4259,9 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3520
4259
|
imported.refreshToken,
|
|
3521
4260
|
imported.clientId || "",
|
|
3522
4261
|
imported.clientSecret || "",
|
|
3523
|
-
authMethod
|
|
4262
|
+
authMethod,
|
|
4263
|
+
imported.source || "",
|
|
4264
|
+
imported.tokenKey || ""
|
|
3524
4265
|
];
|
|
3525
4266
|
return {
|
|
3526
4267
|
url: "",
|
|
@@ -3576,7 +4317,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3576
4317
|
throw new Error("No refresh token provided.");
|
|
3577
4318
|
}
|
|
3578
4319
|
const region = inputs.region?.trim() || "us-east-1";
|
|
3579
|
-
const packed = `${refreshToken}|||desktop
|
|
4320
|
+
const packed = `${refreshToken}|||desktop||`;
|
|
3580
4321
|
return {
|
|
3581
4322
|
url: "",
|
|
3582
4323
|
method: "auto",
|
|
@@ -3604,7 +4345,7 @@ Make sure Kiro CLI or IDE is installed and you're logged in, then try again.`);
|
|
|
3604
4345
|
cfg.provider = cfg.provider ?? {};
|
|
3605
4346
|
cfg.provider.kiro = cfg.provider.kiro ?? {};
|
|
3606
4347
|
const kiro = cfg.provider.kiro;
|
|
3607
|
-
kiro.name = kiro.name ?? "Kiro";
|
|
4348
|
+
kiro.name = kiro.name ?? "Kiro AWS";
|
|
3608
4349
|
kiro.npm = kiro.npm ?? "@ai-sdk/anthropic";
|
|
3609
4350
|
kiro.api = kiro.api ?? `http://127.0.0.1:${localPort}/v1`;
|
|
3610
4351
|
kiro.models = kiro.models ?? {};
|