@javargasm/pi-kiro 0.2.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.js CHANGED
@@ -120,19 +120,37 @@ var init_debug = __esm(() => {
120
120
  // src/kiro-cli-sync.ts
121
121
  var exports_kiro_cli_sync = {};
122
122
  __export(exports_kiro_cli_sync, {
123
+ selectKiroTokenRowForWrite: () => selectKiroTokenRowForWrite,
123
124
  saveKiroCliCredentials: () => saveKiroCliCredentials,
125
+ sameKiroCliCredential: () => sameKiroCliCredential,
126
+ importFromKiroSsoCache: () => importFromKiroSsoCache,
124
127
  importFromKiroCli: () => importFromKiroCli,
125
128
  getKiroCliCredentialsAllowExpired: () => getKiroCliCredentialsAllowExpired
126
129
  });
127
130
  import { homedir } from "node:os";
128
131
  import { join } from "node:path";
129
- import { existsSync } from "node:fs";
132
+ import { spawn } from "node:child_process";
133
+ import { existsSync, readFileSync } from "node:fs";
130
134
  function getKiroDbPath() {
131
135
  const home = homedir();
136
+ if (process.platform === "darwin") {
137
+ return join(home, "Library", "Application Support", "kiro-cli", "data.sqlite3");
138
+ }
132
139
  if (process.platform === "win32") {
133
- return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "kiro", "db", "kiro.db");
140
+ return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "kiro-cli", "data.sqlite3");
141
+ }
142
+ const xdgData = process.env.XDG_DATA_HOME;
143
+ if (xdgData && xdgData.length > 0) {
144
+ return join(xdgData, "kiro-cli", "data.sqlite3");
134
145
  }
135
- return join(home, ".kiro", "db", "kiro.db");
146
+ return join(home, ".local", "share", "kiro-cli", "data.sqlite3");
147
+ }
148
+ function getKiroSsoCachePath() {
149
+ const home = homedir();
150
+ if (process.platform === "win32") {
151
+ return join(process.env.USERPROFILE || home, ".aws", "sso", "cache", "kiro-auth-token.json");
152
+ }
153
+ return join(home, ".aws", "sso", "cache", "kiro-auth-token.json");
136
154
  }
137
155
  function safeJsonParse(value) {
138
156
  if (typeof value !== "string")
@@ -143,11 +161,112 @@ function safeJsonParse(value) {
143
161
  return null;
144
162
  }
145
163
  }
164
+ function sqliteQuote(value) {
165
+ return `'${value.replaceAll("'", "''")}'`;
166
+ }
167
+ function runSqliteCli(dbPath, sql, options) {
168
+ return new Promise((resolve2) => {
169
+ const args = [
170
+ ...options.readonly ? ["-readonly"] : [],
171
+ "-cmd",
172
+ `.timeout ${SQLITE_CLI_TIMEOUT_MS}`,
173
+ ...options.json ? ["-json"] : [],
174
+ dbPath
175
+ ];
176
+ const child = spawn("sqlite3", args, { stdio: "pipe" });
177
+ let stdout = "";
178
+ let settled = false;
179
+ const finish = (result) => {
180
+ if (settled)
181
+ return;
182
+ settled = true;
183
+ clearTimeout(timeout);
184
+ resolve2(result);
185
+ };
186
+ const timeout = setTimeout(() => {
187
+ child.kill();
188
+ log.debug("sqlite3 CLI timed out while reading Kiro DB");
189
+ finish(null);
190
+ }, SQLITE_CLI_TIMEOUT_MS);
191
+ child.stdout?.setEncoding("utf8");
192
+ child.stdout?.on("data", (chunk) => {
193
+ stdout += chunk;
194
+ });
195
+ child.on("error", () => {
196
+ log.debug("sqlite3 CLI unavailable for Kiro DB access");
197
+ finish(null);
198
+ });
199
+ child.on("close", (code) => {
200
+ if (code === 0)
201
+ finish(stdout);
202
+ else {
203
+ log.debug("sqlite3 CLI failed while accessing Kiro DB");
204
+ finish(null);
205
+ }
206
+ });
207
+ child.stdin?.end(`${sql}
208
+ `);
209
+ });
210
+ }
211
+ function parseAuthKvRows(value) {
212
+ if (!Array.isArray(value))
213
+ return null;
214
+ const rows = [];
215
+ for (const item of value) {
216
+ if (!item || typeof item !== "object")
217
+ continue;
218
+ const record = item;
219
+ if (typeof record.key === "string" && typeof record.value === "string") {
220
+ rows.push({ key: record.key, value: record.value });
221
+ }
222
+ }
223
+ return rows;
224
+ }
225
+ function extractActiveProfileArnFromStateRows(value) {
226
+ if (!Array.isArray(value))
227
+ return;
228
+ const first = value[0];
229
+ if (!first || typeof first !== "object")
230
+ return;
231
+ const record = first;
232
+ const parsed = safeJsonParse(record.value);
233
+ const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
234
+ return typeof arn === "string" && arn.trim() ? arn.trim() : undefined;
235
+ }
236
+ function parseSqliteChanges(value) {
237
+ if (!Array.isArray(value))
238
+ return 0;
239
+ const first = value[0];
240
+ if (!first || typeof first !== "object")
241
+ return 0;
242
+ const changes = first.changes;
243
+ return typeof changes === "number" ? changes : 0;
244
+ }
245
+ async function readKiroAuthKvRowsWithSqliteCli(dbPath) {
246
+ const rowsRaw = await runSqliteCli(dbPath, "SELECT key, value FROM auth_kv", { readonly: true, json: true });
247
+ if (!rowsRaw)
248
+ return null;
249
+ return parseAuthKvRows(safeJsonParse(rowsRaw));
250
+ }
251
+ async function readKiroDbWithSqliteCli(dbPath) {
252
+ const rows = await readKiroAuthKvRowsWithSqliteCli(dbPath);
253
+ if (!rows)
254
+ return null;
255
+ const stateRaw = await runSqliteCli(dbPath, "SELECT value FROM state WHERE key = 'api.codewhisperer.profile'", { readonly: true, json: true });
256
+ const activeProfileArn = stateRaw ? extractActiveProfileArnFromStateRows(safeJsonParse(stateRaw)) : undefined;
257
+ return { rows, activeProfileArn };
258
+ }
259
+ async function writeKiroDbWithSqliteCli(dbPath, tokenKey, updatedValue) {
260
+ const resultRaw = await runSqliteCli(dbPath, `UPDATE auth_kv SET value = ${sqliteQuote(updatedValue)} WHERE key = ${sqliteQuote(tokenKey)}; ` + "SELECT changes() AS changes", { readonly: false, json: true });
261
+ return parseSqliteChanges(safeJsonParse(resultRaw)) > 0;
262
+ }
146
263
  function findClientCreds(obj) {
147
264
  if (!obj || typeof obj !== "object")
148
265
  return {};
149
- if (typeof obj.clientId === "string" && typeof obj.clientSecret === "string") {
150
- return { clientId: obj.clientId, clientSecret: obj.clientSecret };
266
+ const id = obj.clientId ?? obj.client_id;
267
+ const secret = obj.clientSecret ?? obj.client_secret;
268
+ if (typeof id === "string" && typeof secret === "string") {
269
+ return { clientId: id, clientSecret: secret };
151
270
  }
152
271
  for (const key of Object.keys(obj)) {
153
272
  const result = findClientCreds(obj[key]);
@@ -156,6 +275,26 @@ function findClientCreds(obj) {
156
275
  }
157
276
  return {};
158
277
  }
278
+ function isIdcTokenKey(key) {
279
+ return key.includes("odic") || key.includes("oidc") || key.includes("idc");
280
+ }
281
+ function isTokenRow(row) {
282
+ return row.key.includes(":token");
283
+ }
284
+ function tokenReadRank(row) {
285
+ return isIdcTokenKey(row.key) ? 0 : 1;
286
+ }
287
+ function selectKiroTokenRowForWrite(rows, creds) {
288
+ const tokenRows = rows.filter(isTokenRow);
289
+ if (!creds.tokenKey)
290
+ return;
291
+ return tokenRows.find((row) => row.key === creds.tokenKey);
292
+ }
293
+ function sameKiroCliCredential(left, right) {
294
+ if (!left || !right)
295
+ return false;
296
+ 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;
297
+ }
159
298
  function extractRegionFromArn(arn) {
160
299
  if (!arn)
161
300
  return;
@@ -165,55 +304,64 @@ function extractRegionFromArn(arn) {
165
304
  const region = parts[3];
166
305
  return region && region.length > 0 ? region : undefined;
167
306
  }
168
- async function importFromKiroCli() {
307
+ async function importFromKiroDb() {
169
308
  const dbPath = getKiroDbPath();
170
309
  if (!existsSync(dbPath)) {
171
310
  log.debug(`Kiro CLI DB not found at ${dbPath}`);
172
311
  return null;
173
312
  }
174
313
  try {
175
- let Database;
314
+ let rows;
315
+ let activeProfileArn;
316
+ let Database = null;
176
317
  try {
177
318
  Database = (await import("bun:sqlite")).Database;
178
319
  } catch {
179
320
  try {
180
321
  Database = (await import("better-sqlite3")).default;
181
322
  } catch {
182
- log.debug("No SQLite driver available (need bun:sqlite or better-sqlite3)");
183
- return null;
323
+ log.debug("No SQLite driver available (need bun:sqlite or better-sqlite3); trying sqlite3 CLI");
184
324
  }
185
325
  }
186
- const db = new Database(dbPath, { readonly: true });
187
- try {
188
- db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
189
- } catch {}
190
- let rows;
191
- try {
192
- const stmt = db.prepare("SELECT key, value FROM auth_kv");
193
- rows = stmt.all();
194
- } catch {
195
- log.debug("Failed to read auth_kv table from Kiro DB");
326
+ if (Database) {
327
+ const db = new Database(dbPath, { readonly: true });
328
+ try {
329
+ db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
330
+ } catch {}
331
+ try {
332
+ const stmt = db.prepare("SELECT key, value FROM auth_kv");
333
+ rows = stmt.all();
334
+ } catch {
335
+ log.debug("Failed to read auth_kv table from Kiro DB");
336
+ try {
337
+ db.close();
338
+ } catch {}
339
+ return null;
340
+ }
341
+ try {
342
+ const stateStmt = db.prepare("SELECT value FROM state WHERE key = ?");
343
+ const stateRow = stateStmt.get("api.codewhisperer.profile");
344
+ const parsed = safeJsonParse(stateRow?.value);
345
+ const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
346
+ if (typeof arn === "string" && arn.trim()) {
347
+ activeProfileArn = arn.trim();
348
+ }
349
+ } catch {}
196
350
  try {
197
351
  db.close();
198
352
  } catch {}
199
- return null;
353
+ } else {
354
+ const snapshot = await readKiroDbWithSqliteCli(dbPath);
355
+ if (!snapshot)
356
+ return null;
357
+ rows = snapshot.rows;
358
+ activeProfileArn = snapshot.activeProfileArn;
200
359
  }
201
- let activeProfileArn;
202
- try {
203
- const stateStmt = db.prepare("SELECT value FROM state WHERE key = ?");
204
- const stateRow = stateStmt.get("api.codewhisperer.profile");
205
- const parsed = safeJsonParse(stateRow?.value);
206
- const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
207
- if (typeof arn === "string" && arn.trim()) {
208
- activeProfileArn = arn.trim();
209
- }
210
- } catch {}
211
360
  const deviceRegRow = rows.find((r) => typeof r?.key === "string" && r.key.includes("device-registration"));
212
361
  const deviceReg = safeJsonParse(deviceRegRow?.value);
213
362
  const regCreds = deviceReg ? findClientCreds(deviceReg) : {};
214
- for (const row of rows) {
215
- if (!row.key.includes(":token"))
216
- continue;
363
+ const tokenRows = rows.filter(isTokenRow).sort((a, b) => tokenReadRank(a) - tokenReadRank(b));
364
+ for (const row of tokenRows) {
217
365
  const data = safeJsonParse(row.value);
218
366
  if (!data)
219
367
  continue;
@@ -221,7 +369,7 @@ async function importFromKiroCli() {
221
369
  const refreshToken = data.refreshToken || data.refresh_token;
222
370
  if (!accessToken && !refreshToken)
223
371
  continue;
224
- const isIdc = row.key.includes("oidc") || row.key.includes("idc");
372
+ const isIdc = isIdcTokenKey(row.key);
225
373
  const authMethod = isIdc ? "idc" : "desktop";
226
374
  const oidcRegion = data.region || "us-east-1";
227
375
  let profileArn = data.profile_arn || data.profileArn;
@@ -235,21 +383,17 @@ async function importFromKiroCli() {
235
383
  region: serviceRegion,
236
384
  authMethod,
237
385
  profileArn,
238
- email: data.email || data.emailAddress
386
+ email: data.email || data.emailAddress,
387
+ source: "kiro-cli-db",
388
+ tokenKey: row.key
239
389
  };
240
390
  if (isIdc && regCreds.clientId) {
241
391
  result.clientId = regCreds.clientId;
242
392
  result.clientSecret = regCreds.clientSecret;
243
393
  }
244
- try {
245
- db.close();
246
- } catch {}
247
394
  log.info(`Imported Kiro CLI credentials (method=${authMethod}, region=${serviceRegion}${result.email ? `, email=${result.email}` : ""})`);
248
395
  return result;
249
396
  }
250
- try {
251
- db.close();
252
- } catch {}
253
397
  log.debug("No valid token entries found in Kiro CLI DB");
254
398
  return null;
255
399
  } catch (err) {
@@ -257,47 +401,108 @@ async function importFromKiroCli() {
257
401
  return null;
258
402
  }
259
403
  }
260
- async function getKiroCliCredentialsAllowExpired() {
261
- return importFromKiroCli();
404
+ function mapSsoCacheAuthMethod(value) {
405
+ if (typeof value !== "string")
406
+ return "idc";
407
+ const v = value.toLowerCase();
408
+ if (v === "builderid" || v === "builder-id")
409
+ return "desktop";
410
+ return "idc";
411
+ }
412
+ async function importFromKiroSsoCache() {
413
+ const path = getKiroSsoCachePath();
414
+ if (!existsSync(path)) {
415
+ log.debug(`Kiro SSO cache not found at ${path}`);
416
+ return null;
417
+ }
418
+ let raw;
419
+ try {
420
+ raw = readFileSync(path, "utf8");
421
+ } catch (err) {
422
+ log.warn(`Failed to read Kiro SSO cache at ${path}: ${err}`);
423
+ return null;
424
+ }
425
+ const token = safeJsonParse(raw);
426
+ if (!token || typeof token !== "object") {
427
+ log.debug(`Kiro SSO cache at ${path} is not valid JSON`);
428
+ return null;
429
+ }
430
+ const accessToken = typeof token.accessToken === "string" ? token.accessToken : "";
431
+ const refreshToken = typeof token.refreshToken === "string" ? token.refreshToken : "";
432
+ if (!accessToken && !refreshToken) {
433
+ log.debug(`Kiro SSO cache at ${path} has no tokens`);
434
+ return null;
435
+ }
436
+ const region = typeof token.region === "string" && token.region.length > 0 ? token.region : "us-east-1";
437
+ const authMethod = mapSsoCacheAuthMethod(token.authMethod);
438
+ log.info(`Imported Kiro SSO cache credentials (method=${authMethod}, region=${region})`);
439
+ return {
440
+ accessToken,
441
+ refreshToken,
442
+ region,
443
+ authMethod,
444
+ source: "kiro-sso-cache"
445
+ };
446
+ }
447
+ async function importFromKiroCli() {
448
+ const dbResult = await importFromKiroDb();
449
+ if (dbResult)
450
+ return dbResult;
451
+ return importFromKiroSsoCache();
452
+ }
453
+ async function getKiroCliCredentialsAllowExpired(exclude) {
454
+ const imported = await importFromKiroCli();
455
+ return sameKiroCliCredential(imported, exclude ?? null) ? null : imported;
262
456
  }
263
457
  async function saveKiroCliCredentials(creds) {
458
+ if (creds.source !== "kiro-cli-db" || !creds.tokenKey) {
459
+ log.debug("Credential write-back skipped: credential did not originate from kiro-cli DB");
460
+ return false;
461
+ }
264
462
  const dbPath = getKiroDbPath();
265
463
  if (!existsSync(dbPath)) {
266
464
  log.debug(`Kiro CLI DB not found at ${dbPath} — cannot save credentials`);
267
465
  return false;
268
466
  }
269
467
  try {
270
- let Database;
468
+ let Database = null;
271
469
  try {
272
470
  Database = (await import("bun:sqlite")).Database;
273
471
  } catch {
274
472
  try {
275
473
  Database = (await import("better-sqlite3")).default;
276
474
  } catch {
277
- log.debug("No SQLite driver available for credential write-back");
278
- return false;
475
+ log.debug("No SQLite driver available for credential write-back; trying sqlite3 CLI");
279
476
  }
280
477
  }
281
- const db = new Database(dbPath);
282
- try {
283
- db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
284
- } catch {}
285
478
  let rows;
286
- try {
287
- const stmt = db.prepare("SELECT key, value FROM auth_kv");
288
- rows = stmt.all();
289
- } catch {
290
- log.debug("Failed to read auth_kv table for credential write-back");
479
+ let db = null;
480
+ if (Database) {
481
+ db = new Database(dbPath);
291
482
  try {
292
- db.close();
483
+ db.run?.("PRAGMA busy_timeout = 5000") ?? db.exec?.("PRAGMA busy_timeout = 5000");
293
484
  } catch {}
294
- return false;
485
+ try {
486
+ const stmt = db.prepare("SELECT key, value FROM auth_kv");
487
+ rows = stmt.all();
488
+ } catch {
489
+ log.debug("Failed to read auth_kv table for credential write-back");
490
+ try {
491
+ db.close();
492
+ } catch {}
493
+ return false;
494
+ }
495
+ } else {
496
+ const cliRows = await readKiroAuthKvRowsWithSqliteCli(dbPath);
497
+ if (!cliRows)
498
+ return false;
499
+ rows = cliRows;
295
500
  }
296
- const tokenRow = rows.find((r) => r.key.includes(":token"));
501
+ const tokenRow = selectKiroTokenRowForWrite(rows, creds);
297
502
  if (!tokenRow) {
298
- log.debug("No token entry found in auth_kv — cannot write back");
503
+ log.debug("No matching token entry found in auth_kv — cannot write back");
299
504
  try {
300
- db.close();
505
+ db?.close();
301
506
  } catch {}
302
507
  return false;
303
508
  }
@@ -309,19 +514,26 @@ async function saveKiroCliCredentials(creds) {
309
514
  refreshToken: creds.refreshToken,
310
515
  refresh_token: creds.refreshToken
311
516
  };
312
- try {
313
- const updateStmt = db.prepare("UPDATE auth_kv SET value = ? WHERE key = ?");
314
- updateStmt.run(JSON.stringify(updated), tokenRow.key);
315
- } catch (err) {
316
- log.warn(`Failed to write credentials back to Kiro CLI DB: ${err}`);
517
+ const updatedValue = JSON.stringify(updated);
518
+ if (db) {
519
+ try {
520
+ const updateStmt = db.prepare("UPDATE auth_kv SET value = ? WHERE key = ?");
521
+ updateStmt.run(updatedValue, tokenRow.key);
522
+ } catch (err) {
523
+ log.warn(`Failed to write credentials back to Kiro CLI DB: ${err}`);
524
+ try {
525
+ db.close();
526
+ } catch {}
527
+ return false;
528
+ }
317
529
  try {
318
530
  db.close();
319
531
  } catch {}
320
- return false;
532
+ } else {
533
+ const wrote = await writeKiroDbWithSqliteCli(dbPath, tokenRow.key, updatedValue);
534
+ if (!wrote)
535
+ return false;
321
536
  }
322
- try {
323
- db.close();
324
- } catch {}
325
537
  log.info("Wrote refreshed credentials back to Kiro CLI DB");
326
538
  return true;
327
539
  } catch (err) {
@@ -329,6 +541,7 @@ async function saveKiroCliCredentials(creds) {
329
541
  return false;
330
542
  }
331
543
  }
544
+ var SQLITE_CLI_TIMEOUT_MS = 5000;
332
545
  var init_kiro_cli_sync = __esm(() => {
333
546
  init_debug();
334
547
  });
@@ -1611,8 +1824,9 @@ ${currentContent}`;
1611
1824
  let transientRetryCount = 0;
1612
1825
  let contextTruncationAttempt = 0;
1613
1826
  while (true) {
1614
- const mid = crypto.randomUUID().replace(/-/g, "");
1615
- const ua = `aws-sdk-rust/1.0.0 ua/2.1 os/other lang/rust api/codewhispererstreaming#1.28.3 m/E app/AmazonQ-For-CLI md/appVersion-1.28.3-${mid}`;
1827
+ const osName = resolveOS();
1828
+ 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`;
1829
+ 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`;
1616
1830
  const requestBody = JSON.stringify(request);
1617
1831
  const requestShape = log.isDebug() ? summarizeKiroRequest(request, requestBody) : undefined;
1618
1832
  if (requestShape)
@@ -1630,15 +1844,17 @@ ${currentContent}`;
1630
1844
  method: "POST",
1631
1845
  headers: {
1632
1846
  "Content-Type": "application/x-amz-json-1.0",
1633
- Accept: "application/json",
1847
+ Accept: "*/*",
1848
+ "Accept-Encoding": "gzip",
1634
1849
  Authorization: `Bearer ${accessToken}`,
1635
1850
  "X-Amz-Target": "AmazonCodeWhispererStreamingService.GenerateAssistantResponse",
1636
1851
  "x-amzn-codewhisperer-optout": "true",
1637
1852
  "amz-sdk-invocation-id": crypto.randomUUID(),
1638
- "amz-sdk-request": "attempt=1; max=1",
1639
- "x-amzn-kiro-agent-mode": "vibe",
1640
- "x-amz-user-agent": ua,
1641
- "user-agent": ua
1853
+ "amz-sdk-request": "attempt=1; max=3",
1854
+ "user-agent": ua,
1855
+ "x-amz-user-agent": xAmzUa,
1856
+ Pragma: "no-cache",
1857
+ "Cache-Control": "no-cache"
1642
1858
  },
1643
1859
  body: requestBody,
1644
1860
  signal: options?.signal
@@ -1992,6 +2208,7 @@ ${currentContent}`;
1992
2208
 
1993
2209
  // src/models.ts
1994
2210
  var KIRO_MODEL_IDS = new Set([
2211
+ "claude-fable-5",
1995
2212
  "claude-opus-4.8",
1996
2213
  "claude-opus-4.7",
1997
2214
  "claude-opus-4.6",
@@ -2053,6 +2270,7 @@ function resolveApiRegion(ssoRegion) {
2053
2270
  }
2054
2271
  var MODELS_BY_REGION = {
2055
2272
  "us-east-1": new Set([
2273
+ "claude-fable-5",
2056
2274
  "claude-opus-4-8",
2057
2275
  "claude-opus-4-7",
2058
2276
  "claude-opus-4-6",
@@ -2076,6 +2294,7 @@ var MODELS_BY_REGION = {
2076
2294
  "auto"
2077
2295
  ]),
2078
2296
  "eu-central-1": new Set([
2297
+ "claude-fable-5",
2079
2298
  "claude-opus-4-8",
2080
2299
  "claude-opus-4-7",
2081
2300
  "claude-opus-4-6",
@@ -2116,6 +2335,17 @@ var KIRO_DEFAULTS = {
2116
2335
  var MULTIMODAL = ["text", "image"];
2117
2336
  var TEXT_ONLY = ["text"];
2118
2337
  var kiroModels = [
2338
+ {
2339
+ ...KIRO_DEFAULTS,
2340
+ id: "claude-fable-5",
2341
+ name: "Claude Fable 5",
2342
+ reasoning: true,
2343
+ input: MULTIMODAL,
2344
+ contextWindow: 1e6,
2345
+ maxTokens: 128000,
2346
+ firstTokenTimeout: 180000,
2347
+ supportedEfforts: ["low", "medium", "high", "xhigh", "max"]
2348
+ },
2119
2349
  {
2120
2350
  ...KIRO_DEFAULTS,
2121
2351
  id: "claude-opus-4-8",
@@ -2336,6 +2566,7 @@ async function fetchAvailableModels(accessToken, apiRegion) {
2336
2566
  return (data.models ?? []).filter((m) => m.modelId !== "auto");
2337
2567
  }
2338
2568
  var REASONING_FAMILIES = new Set([
2569
+ "claude-fable",
2339
2570
  "claude-sonnet",
2340
2571
  "claude-opus",
2341
2572
  "deepseek",
@@ -2353,7 +2584,7 @@ function isReasoningModel(dotId) {
2353
2584
  return false;
2354
2585
  }
2355
2586
  function firstTokenTimeout(dotId) {
2356
- if (dotId.startsWith("claude-opus"))
2587
+ if (dotId.startsWith("claude-fable") || dotId.startsWith("claude-opus"))
2357
2588
  return 180000;
2358
2589
  return 90000;
2359
2590
  }
@@ -2503,7 +2734,7 @@ async function loginKiro(callbacks) {
2503
2734
  options: [
2504
2735
  { id: "builder-id", label: "AWS Builder ID (personal account)" },
2505
2736
  { id: "idc", label: "IAM Identity Center (enterprise SSO)" },
2506
- { id: "sync", label: "Import from Kiro IDE (auto-sync local DB)" },
2737
+ { id: "sync", label: "Import from Kiro CLI/IDE (auto-sync local DB)" },
2507
2738
  { id: "desktop", label: "Desktop refresh token (manual)" }
2508
2739
  ]
2509
2740
  });
@@ -2555,16 +2786,7 @@ Alternatively, use 'desktop' to paste a refresh token manually.`);
2555
2786
  } catch (err) {
2556
2787
  log.warn(`Failed to fetch models after CLI sync: ${err}`);
2557
2788
  }
2558
- const refreshPacked = imported.clientId ? `${imported.refreshToken}|${imported.clientId}|${imported.clientSecret ?? ""}|${imported.authMethod}` : `${imported.refreshToken}|||desktop`;
2559
- return {
2560
- refresh: refreshPacked,
2561
- access: imported.accessToken,
2562
- expires: Date.now() + 3600000 - EXPIRES_BUFFER_MS,
2563
- clientId: imported.clientId ?? "",
2564
- clientSecret: imported.clientSecret ?? "",
2565
- region: imported.region,
2566
- authMethod: imported.authMethod
2567
- };
2789
+ return kiroCredsFromCliImport(imported);
2568
2790
  }
2569
2791
  async function loginDesktopManual(callbacks) {
2570
2792
  const refreshRaw = await callbacks.onPrompt({
@@ -2639,13 +2861,19 @@ Complete authorization within 10 minutes.`
2639
2861
  };
2640
2862
  }
2641
2863
  async function syncBackToKiroCli(result) {
2864
+ if (result.kiroSyncSource !== "kiro-cli-db" || !result.kiroSyncTokenKey) {
2865
+ log.debug("Credential sync-back skipped: credential did not originate from kiro-cli DB");
2866
+ return;
2867
+ }
2642
2868
  try {
2643
2869
  const { saveKiroCliCredentials: saveKiroCliCredentials2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
2644
2870
  const synced = await saveKiroCliCredentials2({
2645
2871
  accessToken: result.access,
2646
2872
  refreshToken: result.refresh.split("|")[0] ?? "",
2647
2873
  region: result.region,
2648
- authMethod: result.authMethod === "builder-id" ? "idc" : result.authMethod
2874
+ authMethod: result.authMethod === "builder-id" ? "idc" : result.authMethod,
2875
+ source: result.kiroSyncSource,
2876
+ tokenKey: result.kiroSyncTokenKey
2649
2877
  });
2650
2878
  if (synced)
2651
2879
  log.info("Synced refreshed credentials back to Kiro CLI DB");
@@ -2654,8 +2882,9 @@ async function syncBackToKiroCli(result) {
2654
2882
  }
2655
2883
  }
2656
2884
  function kiroCredsFromCliImport(imported) {
2657
- const authMethod = imported.authMethod === "idc" ? "idc" : "desktop";
2658
- const refreshPacked = imported.clientId ? `${imported.refreshToken}|${imported.clientId}|${imported.clientSecret ?? ""}|${authMethod}` : `${imported.refreshToken}|||desktop`;
2885
+ const hasOidcCreds = !!imported.clientId && !!imported.clientSecret;
2886
+ const authMethod = hasOidcCreds && imported.authMethod === "idc" ? "idc" : "desktop";
2887
+ const refreshPacked = hasOidcCreds ? `${imported.refreshToken}|${imported.clientId}|${imported.clientSecret ?? ""}|${authMethod}` : `${imported.refreshToken}|||desktop`;
2659
2888
  return {
2660
2889
  refresh: refreshPacked,
2661
2890
  access: imported.accessToken,
@@ -2663,7 +2892,9 @@ function kiroCredsFromCliImport(imported) {
2663
2892
  clientId: imported.clientId ?? "",
2664
2893
  clientSecret: imported.clientSecret ?? "",
2665
2894
  region: imported.region,
2666
- authMethod
2895
+ authMethod,
2896
+ kiroSyncSource: imported.source,
2897
+ kiroSyncTokenKey: imported.tokenKey
2667
2898
  };
2668
2899
  }
2669
2900
  async function refreshTokenInner(credentials) {
@@ -2706,7 +2937,9 @@ async function refreshTokenInner(credentials) {
2706
2937
  clientId: "",
2707
2938
  clientSecret: "",
2708
2939
  region,
2709
- authMethod: "desktop"
2940
+ authMethod: "desktop",
2941
+ kiroSyncSource: credentials.kiroSyncSource,
2942
+ kiroSyncTokenKey: credentials.kiroSyncTokenKey
2710
2943
  };
2711
2944
  }
2712
2945
  const endpoint = `https://oidc.${region}.amazonaws.com/token`;
@@ -2735,7 +2968,9 @@ async function refreshTokenInner(credentials) {
2735
2968
  clientId,
2736
2969
  clientSecret,
2737
2970
  region,
2738
- authMethod
2971
+ authMethod,
2972
+ kiroSyncSource: credentials.kiroSyncSource,
2973
+ kiroSyncTokenKey: credentials.kiroSyncTokenKey
2739
2974
  };
2740
2975
  }
2741
2976
  async function refreshKiroToken(credentials) {
@@ -2797,14 +3032,15 @@ async function refreshKiroToken(credentials) {
2797
3032
  try {
2798
3033
  log.debug("refresh.cascade: layer 4 — expired kiro-cli import");
2799
3034
  const { getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
2800
- expiredImport = await getKiroCliCredentialsAllowExpired2();
2801
- if (expiredImport?.accessToken && expiredImport !== freshImport) {
3035
+ expiredImport = await getKiroCliCredentialsAllowExpired2(freshImport);
3036
+ if (expiredImport?.accessToken) {
2802
3037
  const result = kiroCredsFromCliImport(expiredImport);
2803
3038
  log.info("refresh.cascade: layer 4 succeeded — using expired kiro-cli credentials");
2804
3039
  return result;
3040
+ } else {
3041
+ errors.push("L4(expired-import): no different expired credentials");
3042
+ log.debug("refresh.cascade: layer 4 — no additional expired credentials");
2805
3043
  }
2806
- errors.push("L4(expired-import): no different expired credentials");
2807
- log.debug("refresh.cascade: layer 4 — no additional expired credentials");
2808
3044
  } catch (err) {
2809
3045
  const msg = err instanceof Error ? err.message : String(err);
2810
3046
  errors.push(`L4(expired-import): ${msg}`);