@pentoshi/clai 4.11.24 → 4.11.25
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/agent/stream-recovery.d.ts +1 -0
- package/dist/agent/stream-recovery.js +9 -0
- package/dist/agent/stream-recovery.js.map +1 -1
- package/dist/agent/turn/compaction-coordinator.js +2 -2
- package/dist/agent/turn/compaction-coordinator.js.map +1 -1
- package/dist/agent/turn/loop/stream-failure.js +4 -1
- package/dist/agent/turn/loop/stream-failure.js.map +1 -1
- package/dist/app/commands/catalog.js +31 -0
- package/dist/app/commands/catalog.js.map +1 -1
- package/dist/app/controllers/session-compact-helper.js +6 -8
- package/dist/app/controllers/session-compact-helper.js.map +1 -1
- package/dist/app/controllers/session-controller.js +0 -9
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/commands/providers.d.ts +14 -0
- package/dist/commands/providers.js +223 -0
- package/dist/commands/providers.js.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/llm/capabilities.js +10 -3
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/capability/state.d.ts +1 -1
- package/dist/llm/capability/tool-dialect.js +2 -0
- package/dist/llm/capability/tool-dialect.js.map +1 -1
- package/dist/llm/capability/vision-patterns.js +5 -0
- package/dist/llm/capability/vision-patterns.js.map +1 -1
- package/dist/llm/cline-auth.d.ts +12 -1
- package/dist/llm/cline-auth.js +229 -88
- package/dist/llm/cline-auth.js.map +1 -1
- package/dist/llm/cline.js +146 -48
- package/dist/llm/cline.js.map +1 -1
- package/dist/llm/codex-auth.d.ts +3 -1
- package/dist/llm/codex-auth.js +42 -8
- package/dist/llm/codex-auth.js.map +1 -1
- package/dist/llm/codex.js +17 -14
- package/dist/llm/codex.js.map +1 -1
- package/dist/llm/context-windows.js +6 -0
- package/dist/llm/context-windows.js.map +1 -1
- package/dist/llm/free.js +30 -9
- package/dist/llm/free.js.map +1 -1
- package/dist/llm/kiro-auth.d.ts +113 -0
- package/dist/llm/kiro-auth.js +1017 -0
- package/dist/llm/kiro-auth.js.map +1 -0
- package/dist/llm/kiro.d.ts +10 -0
- package/dist/llm/kiro.js +846 -0
- package/dist/llm/kiro.js.map +1 -0
- package/dist/llm/mimo.d.ts +2 -0
- package/dist/llm/mimo.js +89 -0
- package/dist/llm/mimo.js.map +1 -0
- package/dist/llm/model-families.js +9 -0
- package/dist/llm/model-families.js.map +1 -1
- package/dist/llm/model-layers/model-rules.js +33 -0
- package/dist/llm/model-layers/model-rules.js.map +1 -1
- package/dist/llm/provider-identity.d.ts +1 -0
- package/dist/llm/provider-identity.js +15 -0
- package/dist/llm/provider-identity.js.map +1 -1
- package/dist/llm/provider-info-text.js +87 -1
- package/dist/llm/provider-info-text.js.map +1 -1
- package/dist/llm/provider-profile-layers.d.ts +1 -0
- package/dist/llm/provider-profile-layers.js +67 -1
- package/dist/llm/provider-profile-layers.js.map +1 -1
- package/dist/llm/provider-profiles.js +5 -1
- package/dist/llm/provider-profiles.js.map +1 -1
- package/dist/llm/reasoning-capability.js +2 -0
- package/dist/llm/reasoning-capability.js.map +1 -1
- package/dist/llm/responses-complete.js +7 -1
- package/dist/llm/responses-complete.js.map +1 -1
- package/dist/llm/responses-config.d.ts +1 -1
- package/dist/llm/responses-config.js.map +1 -1
- package/dist/llm/responses-http.d.ts +2 -2
- package/dist/llm/responses-http.js +2 -2
- package/dist/llm/responses-http.js.map +1 -1
- package/dist/llm/responses-stream.js +9 -3
- package/dist/llm/responses-stream.js.map +1 -1
- package/dist/llm/router.js +14 -12
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/routing/provider-selection.js +6 -0
- package/dist/llm/routing/provider-selection.js.map +1 -1
- package/dist/llm/sampling.js +6 -0
- package/dist/llm/sampling.js.map +1 -1
- package/dist/llm/wire/chat-body.js +47 -12
- package/dist/llm/wire/chat-body.js.map +1 -1
- package/dist/noninteractive/stream-blocks.js +3 -0
- package/dist/noninteractive/stream-blocks.js.map +1 -1
- package/dist/store/config.js +2 -0
- package/dist/store/config.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/ui-core/bootstrap/session-resume.d.ts +3 -0
- package/dist/ui-core/bootstrap/session-resume.js +52 -0
- package/dist/ui-core/bootstrap/session-resume.js.map +1 -1
- package/dist/ui-core/commands/key-commands.d.ts +9 -0
- package/dist/ui-core/commands/key-commands.js +438 -16
- package/dist/ui-core/commands/key-commands.js.map +1 -1
- package/dist/ui-core/commands/picker-commands.js +18 -0
- package/dist/ui-core/commands/picker-commands.js.map +1 -1
- package/dist/ui-core/state/hydrate/classic-transcript.js +4 -0
- package/dist/ui-core/state/hydrate/classic-transcript.js.map +1 -1
- package/dist/ui-core/state/transcript/apply-app-event.js +17 -0
- package/dist/ui-core/state/transcript/apply-app-event.js.map +1 -1
- package/dist/ui-core/state/transcript-hydrate.js +165 -80
- package/dist/ui-core/state/transcript-hydrate.js.map +1 -1
- package/dist/ui-core/state/transcript-store.js +7 -1
- package/dist/ui-core/state/transcript-store.js.map +1 -1
- package/dist/ui-core/state/transcript-types.d.ts +2 -0
- package/dist/ui-core/state/transcript-types.js +5 -2
- package/dist/ui-core/state/transcript-types.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +1 -1
|
@@ -5,10 +5,11 @@ import { appendSearchProviderKey, getProviderKeys, getSearchProviderKeys, listPr
|
|
|
5
5
|
import { searchProviderIds } from "../../tools/web/types.js";
|
|
6
6
|
import { formatKeyStatus } from "../rendering/format-keys.js";
|
|
7
7
|
import { notice, openEndpointsEditor, openSearchKeysEditor, resolveEditorRowsDetailed } from "./keys/editors.js";
|
|
8
|
-
import { pollClineDeviceAuth, startClineDeviceAuth, } from "../../llm/cline-auth.js";
|
|
8
|
+
import { maybeRefreshClineToken, pollClineDeviceAuth, startClineDeviceAuth, } from "../../llm/cline-auth.js";
|
|
9
9
|
import { codexKeyFromAccessToken, encodeCodexKey, pollCodexDeviceAuth, startCodexBrowserAuth, startCodexDeviceAuth, } from "../../llm/codex-auth.js";
|
|
10
10
|
import { openSystemBrowser } from "../../mcp/auth/loopback.js";
|
|
11
11
|
import { pollCopilotDeviceAuth, startCopilotDeviceAuth, } from "../../llm/copilot-auth.js";
|
|
12
|
+
import { encodeKiroKey, decodeKiroKey, isKiroOAuthToken, maybeRefreshKiroCredential, pollKiroDeviceAuth, startKiroDeviceAuth, startKiroSocialAuth, exchangeKiroSocialCode, importExistingKiroAuth, refreshKiroToken, listenForKiroSocialCallback, createKiroCliAuthorizationFlow, exchangeKiroPortalCode, listenForKiroCliCallback, parseKiroCliCallback, isHeadlessEnvironment, KIRO_KEY_PREFIX, } from "../../llm/kiro-auth.js";
|
|
12
13
|
import { appendProviderKey, replaceProviderKey } from "../../store/keys.js";
|
|
13
14
|
import { MAX_PROVIDER_KEYS } from "../../llm/key-rotation.js";
|
|
14
15
|
const SEARCH_IDS = new Set(["brave", "tavily", "duckduckgo", "exa"]);
|
|
@@ -331,6 +332,7 @@ export async function runClineAuthForUI(services) {
|
|
|
331
332
|
notice(services, "warn", `could not start Cline sign-in: ${error instanceof Error ? error.message : String(error)}`);
|
|
332
333
|
return undefined;
|
|
333
334
|
}
|
|
335
|
+
void openSystemBrowser(start.verificationUrl).catch(() => { });
|
|
334
336
|
services.overlay.openPager("Cline sign-in", [
|
|
335
337
|
"Authenticate Cline on any device:",
|
|
336
338
|
"",
|
|
@@ -341,20 +343,33 @@ export async function runClineAuthForUI(services) {
|
|
|
341
343
|
"Approve in your browser — clai will continue automatically.",
|
|
342
344
|
"(close this and press Ctrl-C to cancel)",
|
|
343
345
|
].join("\n"), undefined, undefined, "plain");
|
|
346
|
+
const abortController = new AbortController();
|
|
347
|
+
const unsubscribe = services.overlay.subscribe(() => {
|
|
348
|
+
if (!services.overlay.isOpen())
|
|
349
|
+
abortController.abort();
|
|
350
|
+
});
|
|
344
351
|
const waiting = services.toast.info("waiting for Cline approval…", {
|
|
345
352
|
sticky: true,
|
|
346
353
|
});
|
|
347
354
|
try {
|
|
348
|
-
const tokens = await pollClineDeviceAuth(start
|
|
349
|
-
|
|
355
|
+
const tokens = await pollClineDeviceAuth(start, {
|
|
356
|
+
signal: abortController.signal,
|
|
357
|
+
});
|
|
350
358
|
notice(services, "info", "Cline authenticated");
|
|
351
359
|
return tokens;
|
|
352
360
|
}
|
|
353
361
|
catch (error) {
|
|
354
|
-
|
|
362
|
+
if (abortController.signal.aborted) {
|
|
363
|
+
notice(services, "info", "cancelled");
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
355
366
|
notice(services, "warn", `Cline sign-in failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
356
367
|
return undefined;
|
|
357
368
|
}
|
|
369
|
+
finally {
|
|
370
|
+
unsubscribe();
|
|
371
|
+
services.toast.dismiss(waiting);
|
|
372
|
+
}
|
|
358
373
|
}
|
|
359
374
|
async function clineAddAccount(services) {
|
|
360
375
|
const tokens = await runClineAuthForUI(services);
|
|
@@ -436,21 +451,40 @@ export async function runCodexAuthForUI(services) {
|
|
|
436
451
|
"clai will continue automatically once authorization completes.",
|
|
437
452
|
"(close this and press Ctrl-C to cancel)",
|
|
438
453
|
].join("\n"), undefined, undefined, "plain");
|
|
454
|
+
const abortController = new AbortController();
|
|
455
|
+
const unsubscribe = services.overlay.subscribe(() => {
|
|
456
|
+
if (!services.overlay.isOpen())
|
|
457
|
+
abortController.abort();
|
|
458
|
+
});
|
|
439
459
|
const waiting = services.toast.info("waiting for ChatGPT approval…", {
|
|
440
460
|
sticky: true,
|
|
441
461
|
});
|
|
442
462
|
try {
|
|
443
463
|
await openSystemBrowser(handle.url).catch(() => { });
|
|
444
|
-
const credential = await
|
|
445
|
-
|
|
464
|
+
const credential = await Promise.race([
|
|
465
|
+
handle.waitForCredential(),
|
|
466
|
+
new Promise((_, reject) => {
|
|
467
|
+
if (abortController.signal.aborted)
|
|
468
|
+
reject(new Error("ChatGPT authentication cancelled"));
|
|
469
|
+
abortController.signal.addEventListener("abort", () => reject(new Error("ChatGPT authentication cancelled")));
|
|
470
|
+
}),
|
|
471
|
+
]);
|
|
446
472
|
notice(services, "info", "ChatGPT Subscription authenticated");
|
|
447
473
|
return credential;
|
|
448
474
|
}
|
|
449
475
|
catch (error) {
|
|
450
|
-
|
|
476
|
+
if (abortController.signal.aborted) {
|
|
477
|
+
notice(services, "info", "cancelled");
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
451
480
|
notice(services, "warn", `ChatGPT Subscription sign-in failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
452
481
|
return undefined;
|
|
453
482
|
}
|
|
483
|
+
finally {
|
|
484
|
+
unsubscribe();
|
|
485
|
+
services.toast.dismiss(waiting);
|
|
486
|
+
handle.close();
|
|
487
|
+
}
|
|
454
488
|
}
|
|
455
489
|
export async function runCodexDeviceAuthForUI(services) {
|
|
456
490
|
let start;
|
|
@@ -461,6 +495,7 @@ export async function runCodexDeviceAuthForUI(services) {
|
|
|
461
495
|
notice(services, "warn", `could not start ChatGPT Subscription sign-in: ${error instanceof Error ? error.message : String(error)}`);
|
|
462
496
|
return undefined;
|
|
463
497
|
}
|
|
498
|
+
void openSystemBrowser(start.verificationUrl).catch(() => { });
|
|
464
499
|
services.overlay.openPager("ChatGPT Subscription sign-in", [
|
|
465
500
|
"Authenticate ChatGPT Subscription on any device:",
|
|
466
501
|
"",
|
|
@@ -471,20 +506,33 @@ export async function runCodexDeviceAuthForUI(services) {
|
|
|
471
506
|
"Approve in your browser — clai will continue automatically.",
|
|
472
507
|
"(close this and press Ctrl-C to cancel)",
|
|
473
508
|
].join("\n"), undefined, undefined, "plain");
|
|
509
|
+
const abortController = new AbortController();
|
|
510
|
+
const unsubscribe = services.overlay.subscribe(() => {
|
|
511
|
+
if (!services.overlay.isOpen())
|
|
512
|
+
abortController.abort();
|
|
513
|
+
});
|
|
474
514
|
const waiting = services.toast.info("waiting for ChatGPT approval…", {
|
|
475
515
|
sticky: true,
|
|
476
516
|
});
|
|
477
517
|
try {
|
|
478
|
-
const credential = await pollCodexDeviceAuth(start
|
|
479
|
-
|
|
518
|
+
const credential = await pollCodexDeviceAuth(start, {
|
|
519
|
+
signal: abortController.signal,
|
|
520
|
+
});
|
|
480
521
|
notice(services, "info", "ChatGPT Subscription authenticated");
|
|
481
522
|
return credential;
|
|
482
523
|
}
|
|
483
524
|
catch (error) {
|
|
484
|
-
|
|
525
|
+
if (abortController.signal.aborted) {
|
|
526
|
+
notice(services, "info", "cancelled");
|
|
527
|
+
return undefined;
|
|
528
|
+
}
|
|
485
529
|
notice(services, "warn", `ChatGPT Subscription sign-in failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
486
530
|
return undefined;
|
|
487
531
|
}
|
|
532
|
+
finally {
|
|
533
|
+
unsubscribe();
|
|
534
|
+
services.toast.dismiss(waiting);
|
|
535
|
+
}
|
|
488
536
|
}
|
|
489
537
|
export async function runCopilotAuthForUI(services) {
|
|
490
538
|
let start;
|
|
@@ -505,20 +553,33 @@ export async function runCopilotAuthForUI(services) {
|
|
|
505
553
|
"Approve in your browser — clai will continue automatically.",
|
|
506
554
|
"(close this and press Ctrl-C to cancel)",
|
|
507
555
|
].join("\n"), undefined, undefined, "plain");
|
|
556
|
+
const abortController = new AbortController();
|
|
557
|
+
const unsubscribe = services.overlay.subscribe(() => {
|
|
558
|
+
if (!services.overlay.isOpen())
|
|
559
|
+
abortController.abort();
|
|
560
|
+
});
|
|
508
561
|
const waiting = services.toast.info("waiting for Github Copilot approval…", {
|
|
509
562
|
sticky: true,
|
|
510
563
|
});
|
|
511
564
|
try {
|
|
512
|
-
const token = await pollCopilotDeviceAuth(start
|
|
513
|
-
|
|
565
|
+
const token = await pollCopilotDeviceAuth(start, {
|
|
566
|
+
signal: abortController.signal,
|
|
567
|
+
});
|
|
514
568
|
notice(services, "info", "Github Copilot authenticated");
|
|
515
569
|
return token;
|
|
516
570
|
}
|
|
517
571
|
catch (error) {
|
|
518
|
-
|
|
572
|
+
if (abortController.signal.aborted) {
|
|
573
|
+
notice(services, "info", "cancelled");
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
519
576
|
notice(services, "warn", `Github Copilot sign-in failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
520
577
|
return undefined;
|
|
521
578
|
}
|
|
579
|
+
finally {
|
|
580
|
+
unsubscribe();
|
|
581
|
+
services.toast.dismiss(waiting);
|
|
582
|
+
}
|
|
522
583
|
}
|
|
523
584
|
async function loadClineKeys() {
|
|
524
585
|
const multi = await getProviderKeys("cline");
|
|
@@ -539,13 +600,27 @@ async function storeClineAccount(services, tokens) {
|
|
|
539
600
|
}
|
|
540
601
|
notice(services, "info", `added Cline account ${maskSecret(tokens.accessToken)}`);
|
|
541
602
|
}
|
|
542
|
-
async function refreshClineAccount(services,
|
|
603
|
+
async function refreshClineAccount(services, slot) {
|
|
604
|
+
if (slot.refreshToken) {
|
|
605
|
+
notice(services, "info", "refreshing Cline account…");
|
|
606
|
+
const fresh = await maybeRefreshClineToken(slot.value, slot.refreshToken);
|
|
607
|
+
if (fresh?.accessToken) {
|
|
608
|
+
const replaced = await replaceProviderKey("cline", slot.value, fresh.accessToken, {
|
|
609
|
+
...(fresh.refreshToken ? { refreshToken: fresh.refreshToken } : {}),
|
|
610
|
+
...(fresh.expiresAt !== undefined ? { expiresAt: fresh.expiresAt } : {}),
|
|
611
|
+
});
|
|
612
|
+
if (replaced) {
|
|
613
|
+
notice(services, "info", `refreshed Cline account ${maskSecret(fresh.accessToken)}`);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
543
618
|
const tokens = await runClineAuthForUI(services);
|
|
544
619
|
if (!tokens) {
|
|
545
620
|
notice(services, "info", "cancelled");
|
|
546
621
|
return;
|
|
547
622
|
}
|
|
548
|
-
const replaced = await replaceProviderKey("cline",
|
|
623
|
+
const replaced = await replaceProviderKey("cline", slot.value, tokens.accessToken, {
|
|
549
624
|
...(tokens.refreshToken ? { refreshToken: tokens.refreshToken } : {}),
|
|
550
625
|
...(tokens.expiresAt !== undefined ? { expiresAt: tokens.expiresAt } : {}),
|
|
551
626
|
});
|
|
@@ -607,6 +682,7 @@ async function openClineKeysFlow(services) {
|
|
|
607
682
|
return;
|
|
608
683
|
}
|
|
609
684
|
if (answer.action === "pick") {
|
|
685
|
+
await savePickerDraft("cline", answer, keys, activeIndex);
|
|
610
686
|
const tokens = await clineAddAccount(services);
|
|
611
687
|
if (tokens)
|
|
612
688
|
await storeClineAccount(services, tokens);
|
|
@@ -616,7 +692,7 @@ async function openClineKeysFlow(services) {
|
|
|
616
692
|
if (answer.action === "refresh") {
|
|
617
693
|
const selected = keys.find((key) => key.id === answer.slotId);
|
|
618
694
|
if (selected) {
|
|
619
|
-
await refreshClineAccount(services, selected
|
|
695
|
+
await refreshClineAccount(services, selected);
|
|
620
696
|
}
|
|
621
697
|
else {
|
|
622
698
|
notice(services, "warn", "Cline account was not found");
|
|
@@ -635,6 +711,18 @@ async function loadOAuthKeys(provider) {
|
|
|
635
711
|
activeIndex: multi.source === "env" ? 0 : multi.activeIndex,
|
|
636
712
|
};
|
|
637
713
|
}
|
|
714
|
+
async function savePickerDraft(provider, answer, keys, activeIndex) {
|
|
715
|
+
const byId = new Map(keys.map((key) => [key.id, key.value]));
|
|
716
|
+
const detailed = answer.rows.flatMap((row) => {
|
|
717
|
+
if (row.slotId) {
|
|
718
|
+
const value = byId.get(row.slotId);
|
|
719
|
+
return value ? [{ value, disabled: row.disabled === true }] : [];
|
|
720
|
+
}
|
|
721
|
+
const value = row.value.trim();
|
|
722
|
+
return value ? [{ value, disabled: row.disabled === true }] : [];
|
|
723
|
+
});
|
|
724
|
+
await setProviderKeys(provider, detailed.map((row) => row.value), answer.activeIndex ?? activeIndex, detailed.filter((row) => row.disabled).map((row) => row.value));
|
|
725
|
+
}
|
|
638
726
|
async function saveOAuthKeys(services, provider, answer, keys, activeIndex) {
|
|
639
727
|
const label = getProvider(provider).displayName;
|
|
640
728
|
const byId = new Map(keys.map((key) => [key.id, key.value]));
|
|
@@ -688,6 +776,7 @@ async function openCodexKeysFlow(services) {
|
|
|
688
776
|
return;
|
|
689
777
|
}
|
|
690
778
|
if (answer.action === "pick") {
|
|
779
|
+
await savePickerDraft("codex", answer, keys, activeIndex);
|
|
691
780
|
const credential = await runCodexAuthForUI(services);
|
|
692
781
|
const manualKey = credential?.manualKey;
|
|
693
782
|
if (credential) {
|
|
@@ -757,6 +846,7 @@ async function openCopilotKeysFlow(services) {
|
|
|
757
846
|
return;
|
|
758
847
|
}
|
|
759
848
|
if (answer.action === "pick") {
|
|
849
|
+
await savePickerDraft("copilot", answer, keys, activeIndex);
|
|
760
850
|
const token = await runCopilotAuthForUI(services);
|
|
761
851
|
if (token) {
|
|
762
852
|
await appendProviderKey("copilot", token);
|
|
@@ -795,6 +885,334 @@ async function openCopilotKeysFlow(services) {
|
|
|
795
885
|
return;
|
|
796
886
|
}
|
|
797
887
|
}
|
|
888
|
+
function pickKiroAuthMethod(services) {
|
|
889
|
+
return new Promise((resolve) => {
|
|
890
|
+
const opened = services.overlay.openPicker({
|
|
891
|
+
title: "Kiro AI sign-in method",
|
|
892
|
+
options: [
|
|
893
|
+
{
|
|
894
|
+
value: "kiro-cli",
|
|
895
|
+
label: "Sign in with Kiro (recommended)",
|
|
896
|
+
description: "Google, GitHub, or Builder ID — browser sign-in",
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
value: "builder-id",
|
|
900
|
+
label: "Sign in with AWS Builder ID",
|
|
901
|
+
description: "device code — works on any device or terminal",
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
value: "idc",
|
|
905
|
+
label: "Sign in with AWS IAM Identity Center (IDC)",
|
|
906
|
+
description: "enterprise SSO start URL",
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
value: "import",
|
|
910
|
+
label: "Import existing Kiro CLI / Desktop sign-in",
|
|
911
|
+
description: "reads local Kiro or AWS SSO credentials",
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
value: "apikey",
|
|
915
|
+
label: "Manually enter API key / token",
|
|
916
|
+
description: "paste an API key or token",
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
}, (value) => {
|
|
920
|
+
services.overlay.close();
|
|
921
|
+
resolve(value);
|
|
922
|
+
});
|
|
923
|
+
if (!opened)
|
|
924
|
+
resolve(undefined);
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
export async function runKiroDeviceAuthForUI(services, options = {}) {
|
|
928
|
+
let start;
|
|
929
|
+
try {
|
|
930
|
+
start = await startKiroDeviceAuth(options);
|
|
931
|
+
}
|
|
932
|
+
catch (error) {
|
|
933
|
+
notice(services, "warn", `could not start Kiro sign-in: ${error instanceof Error ? error.message : String(error)}`);
|
|
934
|
+
return undefined;
|
|
935
|
+
}
|
|
936
|
+
const completeUrl = start.verificationUriComplete || start.verificationUri;
|
|
937
|
+
void openSystemBrowser(completeUrl).catch(() => { });
|
|
938
|
+
services.overlay.openPager("Kiro AI sign-in", [
|
|
939
|
+
"Authenticate Kiro AI on any device:",
|
|
940
|
+
"",
|
|
941
|
+
` ${completeUrl}`,
|
|
942
|
+
"",
|
|
943
|
+
` Code: ${start.userCode}`,
|
|
944
|
+
"",
|
|
945
|
+
"Approve in your browser — clai will continue automatically.",
|
|
946
|
+
"(close this and press Ctrl-C to cancel)",
|
|
947
|
+
].join("\n"), undefined, undefined, "plain");
|
|
948
|
+
const abortController = new AbortController();
|
|
949
|
+
const unsubscribe = services.overlay.subscribe(() => {
|
|
950
|
+
if (!services.overlay.isOpen())
|
|
951
|
+
abortController.abort();
|
|
952
|
+
});
|
|
953
|
+
const waiting = services.toast.info("waiting for Kiro approval…", {
|
|
954
|
+
sticky: true,
|
|
955
|
+
});
|
|
956
|
+
try {
|
|
957
|
+
const credential = await pollKiroDeviceAuth(start, {
|
|
958
|
+
signal: abortController.signal,
|
|
959
|
+
});
|
|
960
|
+
notice(services, "info", "Kiro AI authenticated");
|
|
961
|
+
return credential;
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
if (abortController.signal.aborted) {
|
|
965
|
+
notice(services, "info", "cancelled");
|
|
966
|
+
return undefined;
|
|
967
|
+
}
|
|
968
|
+
notice(services, "warn", `Kiro AI sign-in failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
969
|
+
return undefined;
|
|
970
|
+
}
|
|
971
|
+
finally {
|
|
972
|
+
unsubscribe();
|
|
973
|
+
services.toast.dismiss(waiting);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
export async function runKiroPortalAuthForUI(services) {
|
|
977
|
+
if (isHeadlessEnvironment()) {
|
|
978
|
+
return runKiroDeviceAuthForUI(services, { authMethod: "builder-id" });
|
|
979
|
+
}
|
|
980
|
+
const { authorizeUrl, codeVerifier, state } = createKiroCliAuthorizationFlow();
|
|
981
|
+
let server;
|
|
982
|
+
try {
|
|
983
|
+
server = listenForKiroCliCallback({ expectedState: state });
|
|
984
|
+
}
|
|
985
|
+
catch {
|
|
986
|
+
server = undefined;
|
|
987
|
+
}
|
|
988
|
+
if (server) {
|
|
989
|
+
server.promise
|
|
990
|
+
.then((cb) => services.overlay.answerSecret(`${cb.path}?code=${cb.code}&login_option=${cb.loginOption}${cb.state ? `&state=${cb.state}` : ""}`))
|
|
991
|
+
.catch(() => { });
|
|
992
|
+
}
|
|
993
|
+
await openSystemBrowser(authorizeUrl).catch(() => { });
|
|
994
|
+
const waiting = services.toast.info("waiting for Kiro sign-in…", {
|
|
995
|
+
sticky: true,
|
|
996
|
+
});
|
|
997
|
+
const prompt = "Sign in with Google, GitHub, or Builder ID in your browser.\nclai will continue automatically — or paste the redirect URL / code below:";
|
|
998
|
+
try {
|
|
999
|
+
const pasted = await services.overlay.openSecret({
|
|
1000
|
+
title: "Kiro AI sign-in",
|
|
1001
|
+
prompt,
|
|
1002
|
+
reveal: true,
|
|
1003
|
+
});
|
|
1004
|
+
if (!pasted?.trim())
|
|
1005
|
+
return undefined;
|
|
1006
|
+
const cb = parseKiroCliCallback(pasted.trim());
|
|
1007
|
+
const cred = await exchangeKiroPortalCode(cb, codeVerifier);
|
|
1008
|
+
notice(services, "info", "Kiro AI authenticated");
|
|
1009
|
+
return cred;
|
|
1010
|
+
}
|
|
1011
|
+
catch (err) {
|
|
1012
|
+
notice(services, "warn", `Kiro sign-in failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1013
|
+
return undefined;
|
|
1014
|
+
}
|
|
1015
|
+
finally {
|
|
1016
|
+
services.toast.dismiss(waiting);
|
|
1017
|
+
server?.close();
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
export async function runKiroSocialAuthForUI(services, provider) {
|
|
1021
|
+
const { url, codeVerifier } = await startKiroSocialAuth(provider);
|
|
1022
|
+
const label = provider === "google" ? "Google" : "GitHub";
|
|
1023
|
+
let callbackHandle;
|
|
1024
|
+
try {
|
|
1025
|
+
callbackHandle = await listenForKiroSocialCallback({
|
|
1026
|
+
onCode: (urlOrCode) => {
|
|
1027
|
+
services.overlay.answerSecret(urlOrCode);
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
catch { }
|
|
1032
|
+
await openSystemBrowser(url).catch(() => { });
|
|
1033
|
+
const waiting = services.toast.info(`waiting for Kiro (${label}) sign-in…`, {
|
|
1034
|
+
sticky: true,
|
|
1035
|
+
});
|
|
1036
|
+
try {
|
|
1037
|
+
const pasted = await services.overlay.openSecret({
|
|
1038
|
+
title: `Kiro AI (${label}) sign-in`,
|
|
1039
|
+
prompt: `Sign in with ${label} in your browser.\nWhen prompted "Open Kiro", click Open (or paste the redirect URL / code below):`,
|
|
1040
|
+
reveal: true,
|
|
1041
|
+
});
|
|
1042
|
+
if (!pasted?.trim())
|
|
1043
|
+
return undefined;
|
|
1044
|
+
const cred = await exchangeKiroSocialCode(pasted.trim(), codeVerifier, provider);
|
|
1045
|
+
notice(services, "info", `Kiro AI (${label}) authenticated`);
|
|
1046
|
+
return cred;
|
|
1047
|
+
}
|
|
1048
|
+
catch (err) {
|
|
1049
|
+
notice(services, "warn", `Kiro sign-in failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1050
|
+
return undefined;
|
|
1051
|
+
}
|
|
1052
|
+
finally {
|
|
1053
|
+
services.toast.dismiss(waiting);
|
|
1054
|
+
callbackHandle?.close();
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
export async function runKiroAuthForUI(services) {
|
|
1058
|
+
const method = await pickKiroAuthMethod(services);
|
|
1059
|
+
if (!method)
|
|
1060
|
+
return undefined;
|
|
1061
|
+
if (method === "kiro-cli") {
|
|
1062
|
+
return runKiroPortalAuthForUI(services);
|
|
1063
|
+
}
|
|
1064
|
+
if (method === "builder-id") {
|
|
1065
|
+
return runKiroDeviceAuthForUI(services, { authMethod: "builder-id" });
|
|
1066
|
+
}
|
|
1067
|
+
if (method === "idc") {
|
|
1068
|
+
const startUrl = await services.overlay.openSecret({
|
|
1069
|
+
title: "AWS IAM Identity Center Start URL",
|
|
1070
|
+
prompt: "Enter AWS IDC Start URL (e.g. https://my-org.awsapps.com/start):",
|
|
1071
|
+
reveal: true,
|
|
1072
|
+
});
|
|
1073
|
+
if (!startUrl?.trim())
|
|
1074
|
+
return undefined;
|
|
1075
|
+
const region = await services.overlay.openSecret({
|
|
1076
|
+
title: "AWS Region",
|
|
1077
|
+
prompt: "Enter AWS Region (default: us-east-1):",
|
|
1078
|
+
reveal: true,
|
|
1079
|
+
});
|
|
1080
|
+
return runKiroDeviceAuthForUI(services, {
|
|
1081
|
+
authMethod: "idc",
|
|
1082
|
+
startUrl: startUrl.trim(),
|
|
1083
|
+
region: region?.trim() || "us-east-1",
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
if (method === "import") {
|
|
1087
|
+
const imported = await importExistingKiroAuth();
|
|
1088
|
+
if (!imported) {
|
|
1089
|
+
notice(services, "warn", "no existing Kiro credential found to import");
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
1092
|
+
notice(services, "info", "imported existing Kiro credentials");
|
|
1093
|
+
return imported;
|
|
1094
|
+
}
|
|
1095
|
+
if (method === "apikey") {
|
|
1096
|
+
const answer = await services.overlay.openSecret({
|
|
1097
|
+
title: "Kiro API Key or Token",
|
|
1098
|
+
prompt: "Paste Kiro API key, access token, or refresh token:",
|
|
1099
|
+
});
|
|
1100
|
+
const raw = answer?.trim();
|
|
1101
|
+
if (!raw)
|
|
1102
|
+
return undefined;
|
|
1103
|
+
if (raw.startsWith(KIRO_KEY_PREFIX)) {
|
|
1104
|
+
const decoded = decodeKiroKey(raw);
|
|
1105
|
+
if (decoded)
|
|
1106
|
+
return decoded;
|
|
1107
|
+
}
|
|
1108
|
+
if (raw.startsWith("aorAAAAAG")) {
|
|
1109
|
+
try {
|
|
1110
|
+
return await refreshKiroToken(raw);
|
|
1111
|
+
}
|
|
1112
|
+
catch (err) {
|
|
1113
|
+
notice(services, "warn", `failed to validate refresh token: ${err instanceof Error ? err.message : String(err)}`);
|
|
1114
|
+
return undefined;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
accessToken: raw,
|
|
1119
|
+
apiKey: raw,
|
|
1120
|
+
authMethod: "api_key",
|
|
1121
|
+
region: "us-east-1",
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
return undefined;
|
|
1125
|
+
}
|
|
1126
|
+
async function openKiroKeysFlow(services) {
|
|
1127
|
+
let { keys, activeIndex } = await loadOAuthKeys("kiro");
|
|
1128
|
+
for (;;) {
|
|
1129
|
+
services.overlay.close();
|
|
1130
|
+
const answer = await services.overlay.openKeysEditor({
|
|
1131
|
+
provider: "kiro",
|
|
1132
|
+
heading: "KIRO AI ACCOUNTS",
|
|
1133
|
+
itemLabel: "account",
|
|
1134
|
+
addViaPicker: true,
|
|
1135
|
+
refreshable: true,
|
|
1136
|
+
initialKeys: keys.map((key) => ({
|
|
1137
|
+
id: key.id,
|
|
1138
|
+
masked: maskSecret(key.value),
|
|
1139
|
+
disabled: key.disabled === true,
|
|
1140
|
+
})),
|
|
1141
|
+
activeIndex,
|
|
1142
|
+
});
|
|
1143
|
+
if (!answer) {
|
|
1144
|
+
notice(services, "info", "cancelled");
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (answer.action === "reset") {
|
|
1148
|
+
await unsetProviderSecret("kiro");
|
|
1149
|
+
notice(services, "info", "unset all keys for Kiro AI");
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
if (answer.action === "pick") {
|
|
1153
|
+
await savePickerDraft("kiro", answer, keys, activeIndex);
|
|
1154
|
+
const credential = await runKiroAuthForUI(services);
|
|
1155
|
+
if (credential) {
|
|
1156
|
+
const key = encodeKiroKey(credential);
|
|
1157
|
+
await appendProviderKey("kiro", key, {
|
|
1158
|
+
...(credential.refreshToken ? { refreshToken: credential.refreshToken } : {}),
|
|
1159
|
+
...(credential.expiresAt !== undefined ? { expiresAt: credential.expiresAt } : {}),
|
|
1160
|
+
});
|
|
1161
|
+
notice(services, "info", `added Kiro account ${maskSecret(key)}`);
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
notice(services, "info", "cancelled");
|
|
1165
|
+
}
|
|
1166
|
+
({ keys, activeIndex } = await loadOAuthKeys("kiro"));
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
if (answer.action === "refresh") {
|
|
1170
|
+
const selected = keys.find((key) => key.id === answer.slotId);
|
|
1171
|
+
if (selected) {
|
|
1172
|
+
if (selected.refreshToken || isKiroOAuthToken(selected.value)) {
|
|
1173
|
+
notice(services, "info", "refreshing Kiro account…");
|
|
1174
|
+
const refreshedKey = await maybeRefreshKiroCredential(selected.value, selected.refreshToken);
|
|
1175
|
+
if (refreshedKey) {
|
|
1176
|
+
const decoded = decodeKiroKey(refreshedKey);
|
|
1177
|
+
const replaced = await replaceProviderKey("kiro", selected.value, refreshedKey, {
|
|
1178
|
+
...(decoded?.refreshToken ? { refreshToken: decoded.refreshToken } : {}),
|
|
1179
|
+
...(decoded?.expiresAt !== undefined ? { expiresAt: decoded.expiresAt } : {}),
|
|
1180
|
+
});
|
|
1181
|
+
if (replaced) {
|
|
1182
|
+
notice(services, "info", `refreshed Kiro account ${maskSecret(refreshedKey)}`);
|
|
1183
|
+
({ keys, activeIndex } = await loadOAuthKeys("kiro"));
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
const credential = await runKiroAuthForUI(services);
|
|
1189
|
+
if (credential) {
|
|
1190
|
+
const key = encodeKiroKey(credential);
|
|
1191
|
+
const replaced = await replaceProviderKey("kiro", selected.value, key, {
|
|
1192
|
+
...(credential.refreshToken ? { refreshToken: credential.refreshToken } : {}),
|
|
1193
|
+
...(credential.expiresAt !== undefined ? { expiresAt: credential.expiresAt } : {}),
|
|
1194
|
+
});
|
|
1195
|
+
if (!replaced) {
|
|
1196
|
+
notice(services, "warn", "Kiro account was not found");
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
notice(services, "info", `refreshed Kiro account ${maskSecret(key)}`);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
notice(services, "info", "cancelled");
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
else {
|
|
1207
|
+
notice(services, "warn", "Kiro account was not found");
|
|
1208
|
+
}
|
|
1209
|
+
({ keys, activeIndex } = await loadOAuthKeys("kiro"));
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
await saveOAuthKeys(services, "kiro", answer, keys, activeIndex);
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
798
1216
|
export async function openLlmKeysEditor(services, id) {
|
|
799
1217
|
if (id === "ollama") {
|
|
800
1218
|
const host = await services.overlay.openSecret({
|
|
@@ -822,6 +1240,10 @@ export async function openLlmKeysEditor(services, id) {
|
|
|
822
1240
|
await openCopilotKeysFlow(services);
|
|
823
1241
|
return;
|
|
824
1242
|
}
|
|
1243
|
+
if (id === "kiro") {
|
|
1244
|
+
await openKiroKeysFlow(services);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
825
1247
|
if (providerUsesEndpoints(id)) {
|
|
826
1248
|
await openEndpointsEditor(services, id);
|
|
827
1249
|
}
|