@pentoshi/clai 4.11.23 → 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.
Files changed (152) hide show
  1. package/dist/agent/events.d.ts +4 -1
  2. package/dist/agent/parser/arg-formatting.js +30 -0
  3. package/dist/agent/parser/arg-formatting.js.map +1 -1
  4. package/dist/agent/runner.d.ts +1 -0
  5. package/dist/agent/runner.js +3 -0
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/agent/stream-recovery.d.ts +1 -0
  8. package/dist/agent/stream-recovery.js +9 -0
  9. package/dist/agent/stream-recovery.js.map +1 -1
  10. package/dist/agent/turn/compaction-admission.d.ts +2 -1
  11. package/dist/agent/turn/compaction-admission.js +19 -17
  12. package/dist/agent/turn/compaction-admission.js.map +1 -1
  13. package/dist/agent/turn/compaction-coordinator.d.ts +4 -4
  14. package/dist/agent/turn/compaction-coordinator.js +16 -10
  15. package/dist/agent/turn/compaction-coordinator.js.map +1 -1
  16. package/dist/agent/turn/event-emitter.d.ts +4 -3
  17. package/dist/agent/turn/event-emitter.js +10 -9
  18. package/dist/agent/turn/event-emitter.js.map +1 -1
  19. package/dist/agent/turn/loop/request-assembly.d.ts +1 -0
  20. package/dist/agent/turn/loop/request-assembly.js +4 -2
  21. package/dist/agent/turn/loop/request-assembly.js.map +1 -1
  22. package/dist/agent/turn/loop/round-request.js +1 -0
  23. package/dist/agent/turn/loop/round-request.js.map +1 -1
  24. package/dist/agent/turn/loop/state.d.ts +1 -0
  25. package/dist/agent/turn/loop/state.js +1 -1
  26. package/dist/agent/turn/loop/state.js.map +1 -1
  27. package/dist/agent/turn/loop/stream-failure.js +4 -1
  28. package/dist/agent/turn/loop/stream-failure.js.map +1 -1
  29. package/dist/agent/turn/setup/compaction-services.d.ts +4 -3
  30. package/dist/agent/turn/setup/compaction-services.js.map +1 -1
  31. package/dist/app/adapters/agent-event-adapter.js +6 -1
  32. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  33. package/dist/app/adapters/current-agent-adapter.js +3 -0
  34. package/dist/app/adapters/current-agent-adapter.js.map +1 -1
  35. package/dist/app/commands/catalog.js +31 -0
  36. package/dist/app/commands/catalog.js.map +1 -1
  37. package/dist/app/controllers/session-compact-helper.d.ts +3 -1
  38. package/dist/app/controllers/session-compact-helper.js +17 -2
  39. package/dist/app/controllers/session-compact-helper.js.map +1 -1
  40. package/dist/app/controllers/session-controller.d.ts +3 -1
  41. package/dist/app/controllers/session-controller.js +32 -2
  42. package/dist/app/controllers/session-controller.js.map +1 -1
  43. package/dist/app/controllers/session-turn-request.d.ts +1 -0
  44. package/dist/app/controllers/session-turn-request.js +3 -0
  45. package/dist/app/controllers/session-turn-request.js.map +1 -1
  46. package/dist/app/events/app-event.d.ts +4 -1
  47. package/dist/app/events/app-event.js.map +1 -1
  48. package/dist/app/ports/agent-port.d.ts +1 -0
  49. package/dist/app/ports/transcript-item.d.ts +1 -0
  50. package/dist/commands/providers.d.ts +14 -0
  51. package/dist/commands/providers.js +223 -0
  52. package/dist/commands/providers.js.map +1 -1
  53. package/dist/index.js +13 -3
  54. package/dist/index.js.map +1 -1
  55. package/dist/llm/capabilities.js +10 -3
  56. package/dist/llm/capabilities.js.map +1 -1
  57. package/dist/llm/capability/state.d.ts +1 -1
  58. package/dist/llm/capability/tool-dialect.js +2 -0
  59. package/dist/llm/capability/tool-dialect.js.map +1 -1
  60. package/dist/llm/capability/vision-patterns.js +5 -0
  61. package/dist/llm/capability/vision-patterns.js.map +1 -1
  62. package/dist/llm/cline-auth.d.ts +12 -1
  63. package/dist/llm/cline-auth.js +229 -88
  64. package/dist/llm/cline-auth.js.map +1 -1
  65. package/dist/llm/cline.js +146 -48
  66. package/dist/llm/cline.js.map +1 -1
  67. package/dist/llm/codex-auth.d.ts +3 -1
  68. package/dist/llm/codex-auth.js +42 -8
  69. package/dist/llm/codex-auth.js.map +1 -1
  70. package/dist/llm/codex.js +17 -14
  71. package/dist/llm/codex.js.map +1 -1
  72. package/dist/llm/context-windows.js +6 -0
  73. package/dist/llm/context-windows.js.map +1 -1
  74. package/dist/llm/free.js +30 -9
  75. package/dist/llm/free.js.map +1 -1
  76. package/dist/llm/kiro-auth.d.ts +113 -0
  77. package/dist/llm/kiro-auth.js +1017 -0
  78. package/dist/llm/kiro-auth.js.map +1 -0
  79. package/dist/llm/kiro.d.ts +10 -0
  80. package/dist/llm/kiro.js +846 -0
  81. package/dist/llm/kiro.js.map +1 -0
  82. package/dist/llm/mimo.d.ts +2 -0
  83. package/dist/llm/mimo.js +89 -0
  84. package/dist/llm/mimo.js.map +1 -0
  85. package/dist/llm/model-families.js +9 -0
  86. package/dist/llm/model-families.js.map +1 -1
  87. package/dist/llm/model-layers/model-rules.js +33 -0
  88. package/dist/llm/model-layers/model-rules.js.map +1 -1
  89. package/dist/llm/provider-identity.d.ts +1 -0
  90. package/dist/llm/provider-identity.js +15 -0
  91. package/dist/llm/provider-identity.js.map +1 -1
  92. package/dist/llm/provider-info-text.js +87 -1
  93. package/dist/llm/provider-info-text.js.map +1 -1
  94. package/dist/llm/provider-profile-layers.d.ts +1 -0
  95. package/dist/llm/provider-profile-layers.js +67 -1
  96. package/dist/llm/provider-profile-layers.js.map +1 -1
  97. package/dist/llm/provider-profiles.js +5 -1
  98. package/dist/llm/provider-profiles.js.map +1 -1
  99. package/dist/llm/reasoning-capability.js +2 -0
  100. package/dist/llm/reasoning-capability.js.map +1 -1
  101. package/dist/llm/responses-complete.js +7 -1
  102. package/dist/llm/responses-complete.js.map +1 -1
  103. package/dist/llm/responses-config.d.ts +1 -1
  104. package/dist/llm/responses-config.js.map +1 -1
  105. package/dist/llm/responses-http.d.ts +2 -2
  106. package/dist/llm/responses-http.js +2 -2
  107. package/dist/llm/responses-http.js.map +1 -1
  108. package/dist/llm/responses-stream.js +9 -3
  109. package/dist/llm/responses-stream.js.map +1 -1
  110. package/dist/llm/router.js +14 -12
  111. package/dist/llm/router.js.map +1 -1
  112. package/dist/llm/routing/provider-selection.js +6 -0
  113. package/dist/llm/routing/provider-selection.js.map +1 -1
  114. package/dist/llm/sampling.js +6 -0
  115. package/dist/llm/sampling.js.map +1 -1
  116. package/dist/llm/wire/chat-body.js +47 -12
  117. package/dist/llm/wire/chat-body.js.map +1 -1
  118. package/dist/noninteractive/stream-blocks.js +17 -5
  119. package/dist/noninteractive/stream-blocks.js.map +1 -1
  120. package/dist/store/config.js +2 -0
  121. package/dist/store/config.js.map +1 -1
  122. package/dist/tui-v2/bootstrap/start-tui-v2.js +10 -0
  123. package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
  124. package/dist/tui-v2/components/transcript/transcript-row.js +4 -2
  125. package/dist/tui-v2/components/transcript/transcript-row.js.map +1 -1
  126. package/dist/types.d.ts +1 -1
  127. package/dist/types.js +2 -0
  128. package/dist/types.js.map +1 -1
  129. package/dist/ui-core/bootstrap/composition-root.js +1 -1
  130. package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
  131. package/dist/ui-core/bootstrap/session-resume.d.ts +3 -0
  132. package/dist/ui-core/bootstrap/session-resume.js +52 -0
  133. package/dist/ui-core/bootstrap/session-resume.js.map +1 -1
  134. package/dist/ui-core/commands/key-commands.d.ts +9 -0
  135. package/dist/ui-core/commands/key-commands.js +438 -16
  136. package/dist/ui-core/commands/key-commands.js.map +1 -1
  137. package/dist/ui-core/commands/picker-commands.js +18 -0
  138. package/dist/ui-core/commands/picker-commands.js.map +1 -1
  139. package/dist/ui-core/state/hydrate/classic-transcript.js +5 -0
  140. package/dist/ui-core/state/hydrate/classic-transcript.js.map +1 -1
  141. package/dist/ui-core/state/transcript/apply-app-event.js +29 -0
  142. package/dist/ui-core/state/transcript/apply-app-event.js.map +1 -1
  143. package/dist/ui-core/state/transcript-hydrate.js +165 -79
  144. package/dist/ui-core/state/transcript-hydrate.js.map +1 -1
  145. package/dist/ui-core/state/transcript-store.js +7 -1
  146. package/dist/ui-core/state/transcript-store.js.map +1 -1
  147. package/dist/ui-core/state/transcript-types.d.ts +5 -2
  148. package/dist/ui-core/state/transcript-types.js +17 -3
  149. package/dist/ui-core/state/transcript-types.js.map +1 -1
  150. package/dist/version.generated.d.ts +2 -2
  151. package/dist/version.generated.js +2 -2
  152. package/package.json +1 -1
@@ -0,0 +1,1017 @@
1
+ import { homedir, platform } from "node:os";
2
+ import { join } from "node:path";
3
+ import { readFile, readdir } from "node:fs/promises";
4
+ import { randomBytes, createHash } from "node:crypto";
5
+ import { createServer } from "node:http";
6
+ export const KIRO_DEFAULT_REGION = "us-east-1";
7
+ export const KIRO_AUTH_SERVICE = "https://prod.us-east-1.auth.desktop.kiro.dev";
8
+ export const KIRO_START_URL = "https://view.awsapps.com/start";
9
+ export const KIRO_REDIRECT_URI = "kiro://kiro.kiroAgent/authenticate-success";
10
+ export const KIRO_KEY_PREFIX = "kiro:";
11
+ export const KIRO_CLI_PORTAL_URL = "https://app.kiro.dev/signin";
12
+ export const KIRO_CLI_REDIRECT_URI = "http://localhost:3128";
13
+ export const KIRO_CLI_REDIRECT_FROM = "kirocli";
14
+ export const KIRO_CLI_CALLBACK_PORT = 3128;
15
+ export const KIRO_CLI_CALLBACK_PATH = "/oauth/callback";
16
+ export const KIRO_CLI_SIGNIN_CALLBACK_PATH = "/signin/callback";
17
+ export const KIRO_DESKTOP_UA_VERSION = "0.7.45";
18
+ export const AWS_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\d{1,2}$/;
19
+ export function assertValidAwsRegion(region) {
20
+ const value = (region ?? KIRO_DEFAULT_REGION).trim().toLowerCase();
21
+ if (!AWS_REGION_PATTERN.test(value)) {
22
+ throw new Error(`Invalid AWS region: "${value}"`);
23
+ }
24
+ return value;
25
+ }
26
+ function asRecord(value) {
27
+ return value && typeof value === "object" && !Array.isArray(value)
28
+ ? value
29
+ : undefined;
30
+ }
31
+ function sleep(ms) {
32
+ return new Promise((resolve) => setTimeout(resolve, ms));
33
+ }
34
+ function base64UrlEncode(buffer) {
35
+ return buffer
36
+ .toString("base64")
37
+ .replace(/\+/g, "-")
38
+ .replace(/\//g, "_")
39
+ .replace(/=+$/, "");
40
+ }
41
+ function sha256Base64Url(str) {
42
+ return base64UrlEncode(createHash("sha256").update(str, "utf8").digest());
43
+ }
44
+ export function isKiroOAuthToken(value) {
45
+ return value.startsWith(KIRO_KEY_PREFIX);
46
+ }
47
+ export function isKiroRefreshToken(value) {
48
+ return value.trim().startsWith("aorAAAAAG");
49
+ }
50
+ export function encodeKiroKey(credential) {
51
+ const payload = {
52
+ a: credential.accessToken,
53
+ r: credential.refreshToken ?? "",
54
+ p: credential.profileArn ?? "",
55
+ e: credential.expiresAt ?? 0,
56
+ m: credential.authMethod ?? "builder-id",
57
+ g: credential.region ?? KIRO_DEFAULT_REGION,
58
+ c: credential.clientId ?? "",
59
+ s: credential.clientSecret ?? "",
60
+ u: credential.startUrl ?? "",
61
+ k: credential.apiKey ?? "",
62
+ };
63
+ return (KIRO_KEY_PREFIX +
64
+ Buffer.from(JSON.stringify(payload), "utf8").toString("base64url"));
65
+ }
66
+ export function decodeKiroKey(value) {
67
+ if (!isKiroOAuthToken(value))
68
+ return undefined;
69
+ try {
70
+ const raw = Buffer.from(value.slice(KIRO_KEY_PREFIX.length), "base64url").toString("utf8");
71
+ const json = JSON.parse(raw);
72
+ if (typeof json?.a !== "string" && typeof json?.k !== "string")
73
+ return undefined;
74
+ return {
75
+ accessToken: typeof json?.a === "string" ? json.a : "",
76
+ refreshToken: typeof json?.r === "string" && json.r ? json.r : undefined,
77
+ profileArn: typeof json?.p === "string" && json.p ? json.p : undefined,
78
+ expiresAt: typeof json?.e === "number" && json.e > 0 ? json.e : undefined,
79
+ authMethod: typeof json?.m === "string" ? json.m : undefined,
80
+ region: typeof json?.g === "string" && json.g ? json.g : KIRO_DEFAULT_REGION,
81
+ clientId: typeof json?.c === "string" && json.c ? json.c : undefined,
82
+ clientSecret: typeof json?.s === "string" && json.s ? json.s : undefined,
83
+ startUrl: typeof json?.u === "string" && json.u ? json.u : undefined,
84
+ apiKey: typeof json?.k === "string" && json.k ? json.k : undefined,
85
+ };
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
91
+ export async function registerOidcClient(region = KIRO_DEFAULT_REGION, issuerUrl = KIRO_START_URL) {
92
+ const safeRegion = assertValidAwsRegion(region);
93
+ const endpoint = `https://oidc.${safeRegion}.amazonaws.com/client/register`;
94
+ const response = await fetch(endpoint, {
95
+ method: "POST",
96
+ headers: {
97
+ "Content-Type": "application/json",
98
+ Accept: "application/json",
99
+ },
100
+ body: JSON.stringify({
101
+ clientName: "kiro-cli",
102
+ clientType: "public",
103
+ scopes: [
104
+ "codewhisperer:conversations",
105
+ "codewhisperer:analysis",
106
+ "codewhisperer:completions",
107
+ ],
108
+ grantTypes: [
109
+ "urn:ietf:params:oauth:grant-type:device_code",
110
+ "refresh_token",
111
+ ],
112
+ issuerUrl,
113
+ }),
114
+ signal: AbortSignal.timeout(30_000),
115
+ });
116
+ if (!response.ok) {
117
+ const errorText = await response.text();
118
+ throw new Error(`Failed to register OIDC client (${response.status}): ${errorText}`);
119
+ }
120
+ const json = asRecord(await response.json().catch(() => ({})));
121
+ const clientId = typeof json?.clientId === "string" ? json.clientId : "";
122
+ const clientSecret = typeof json?.clientSecret === "string" ? json.clientSecret : "";
123
+ if (!clientId || !clientSecret) {
124
+ throw new Error("Invalid OIDC client registration response");
125
+ }
126
+ const clientSecretExpiresAt = typeof json?.clientSecretExpiresAt === "number"
127
+ ? json.clientSecretExpiresAt
128
+ : undefined;
129
+ return { clientId, clientSecret, clientSecretExpiresAt };
130
+ }
131
+ export async function startKiroDeviceAuth(options = {}) {
132
+ const region = assertValidAwsRegion(options.region);
133
+ const authMethod = options.authMethod ?? "builder-id";
134
+ const startUrl = options.startUrl?.trim() || KIRO_START_URL;
135
+ const client = await registerOidcClient(region, startUrl);
136
+ const endpoint = `https://oidc.${region}.amazonaws.com/device_authorization`;
137
+ const response = await fetch(endpoint, {
138
+ method: "POST",
139
+ headers: {
140
+ "Content-Type": "application/json",
141
+ Accept: "application/json",
142
+ },
143
+ body: JSON.stringify({
144
+ clientId: client.clientId,
145
+ clientSecret: client.clientSecret,
146
+ startUrl,
147
+ }),
148
+ signal: AbortSignal.timeout(30_000),
149
+ });
150
+ if (!response.ok) {
151
+ const errorText = await response.text();
152
+ throw new Error(`Failed to start Kiro device authorization (${response.status}): ${errorText}`);
153
+ }
154
+ const json = asRecord(await response.json().catch(() => ({})));
155
+ const deviceCode = typeof json?.deviceCode === "string" ? json.deviceCode : "";
156
+ const userCode = typeof json?.userCode === "string" ? json.userCode : "";
157
+ const verificationUri = typeof json?.verificationUri === "string" ? json.verificationUri : "";
158
+ const verificationUriComplete = typeof json?.verificationUriComplete === "string"
159
+ ? json.verificationUriComplete
160
+ : undefined;
161
+ if (!deviceCode || !userCode || !verificationUri) {
162
+ throw new Error("Invalid Kiro device authorization response");
163
+ }
164
+ const expiresInSeconds = typeof json?.expiresIn === "number" ? json.expiresIn : 600;
165
+ const pollIntervalSeconds = typeof json?.interval === "number" ? Math.max(1, json.interval) : 5;
166
+ return {
167
+ deviceCode,
168
+ userCode,
169
+ verificationUri,
170
+ verificationUriComplete,
171
+ expiresInSeconds,
172
+ pollIntervalSeconds,
173
+ clientId: client.clientId,
174
+ clientSecret: client.clientSecret,
175
+ region,
176
+ startUrl,
177
+ authMethod,
178
+ };
179
+ }
180
+ export async function pollKiroDeviceAuth(start, options = {}) {
181
+ const deadline = Date.now() + start.expiresInSeconds * 1000;
182
+ let interval = start.pollIntervalSeconds * 1000;
183
+ const endpoint = `https://oidc.${start.region}.amazonaws.com/token`;
184
+ for (;;) {
185
+ if (options.signal?.aborted) {
186
+ throw new Error("Kiro authentication cancelled");
187
+ }
188
+ const remaining = Math.max(0, Math.ceil((deadline - Date.now()) / 1000));
189
+ if (remaining <= 0) {
190
+ throw new Error("Kiro authentication code expired — start again");
191
+ }
192
+ options.onPending?.(remaining);
193
+ const response = await fetch(endpoint, {
194
+ method: "POST",
195
+ headers: {
196
+ "Content-Type": "application/json",
197
+ Accept: "application/json",
198
+ },
199
+ body: JSON.stringify({
200
+ clientId: start.clientId,
201
+ clientSecret: start.clientSecret,
202
+ deviceCode: start.deviceCode,
203
+ grantType: "urn:ietf:params:oauth:grant-type:device_code",
204
+ }),
205
+ signal: AbortSignal.timeout(30_000),
206
+ });
207
+ const json = asRecord(await response.json().catch(() => ({})));
208
+ if (response.ok && typeof json?.accessToken === "string") {
209
+ const accessToken = json.accessToken;
210
+ const refreshToken = typeof json?.refreshToken === "string" ? json.refreshToken : undefined;
211
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
212
+ const expiresAt = Date.now() + expiresIn * 1000;
213
+ let profileArn;
214
+ try {
215
+ profileArn = await listAvailableProfiles(accessToken, start.region);
216
+ }
217
+ catch {
218
+ profileArn = undefined;
219
+ }
220
+ return {
221
+ accessToken,
222
+ refreshToken,
223
+ profileArn,
224
+ expiresAt,
225
+ authMethod: start.authMethod,
226
+ region: start.region,
227
+ clientId: start.clientId,
228
+ clientSecret: start.clientSecret,
229
+ startUrl: start.startUrl,
230
+ };
231
+ }
232
+ const error = typeof json?.error === "string" ? json.error : String(response.status);
233
+ if (error === "authorization_pending") {
234
+ await sleep(interval);
235
+ continue;
236
+ }
237
+ if (error === "slow_down") {
238
+ interval += 5_000;
239
+ await sleep(interval);
240
+ continue;
241
+ }
242
+ if (error === "expired_token" || error === "access_denied") {
243
+ throw new Error(error === "access_denied"
244
+ ? "Kiro authentication was denied"
245
+ : "Kiro authentication code expired — start again");
246
+ }
247
+ const description = typeof json?.error_description === "string" ? json.error_description : "";
248
+ throw new Error(`Kiro authentication failed (${response.status})${description ? `: ${description}` : ""}`);
249
+ }
250
+ }
251
+ export async function startKiroSocialAuth(provider) {
252
+ const idp = provider === "google" ? "Google" : "Github";
253
+ const codeVerifier = base64UrlEncode(randomBytes(32));
254
+ const codeChallenge = sha256Base64Url(codeVerifier);
255
+ const state = base64UrlEncode(randomBytes(16));
256
+ const params = new URLSearchParams({
257
+ idp,
258
+ redirect_uri: KIRO_REDIRECT_URI,
259
+ code_challenge: codeChallenge,
260
+ code_challenge_method: "S256",
261
+ state,
262
+ prompt: "select_account",
263
+ });
264
+ const url = `${KIRO_AUTH_SERVICE}/login?${params.toString()}`;
265
+ return { url, codeVerifier, state };
266
+ }
267
+ export async function exchangeKiroSocialCode(codeOrUrl, codeVerifier, provider = "google") {
268
+ let code = codeOrUrl.trim().replace(/^['"]|['"]$/g, "");
269
+ if (code.includes("error=")) {
270
+ try {
271
+ const parsedUrl = new URL(code);
272
+ const desc = parsedUrl.searchParams.get("error_description") || parsedUrl.searchParams.get("error");
273
+ if (desc)
274
+ throw new Error(desc);
275
+ }
276
+ catch (e) {
277
+ if (e instanceof Error && !e.message.includes("Invalid URL"))
278
+ throw e;
279
+ }
280
+ const errorMatch = /[?&]error=([^&#\s]+)/.exec(code);
281
+ const descMatch = /[?&]error_description=([^&#\s]+)/.exec(code);
282
+ const err = errorMatch?.[1] ? decodeURIComponent(errorMatch[1].replace(/\+/g, " ")) : "authentication_failed";
283
+ const desc = descMatch?.[1] ? decodeURIComponent(descMatch[1].replace(/\+/g, " ")) : err;
284
+ throw new Error(desc);
285
+ }
286
+ if (code.includes("code=")) {
287
+ try {
288
+ const parsedUrl = new URL(code);
289
+ const c = parsedUrl.searchParams.get("code");
290
+ if (c)
291
+ code = c.trim();
292
+ }
293
+ catch {
294
+ const match = /[?&]code=([^&#\s]+)/.exec(code);
295
+ if (match?.[1]) {
296
+ code = decodeURIComponent(match[1]).trim();
297
+ }
298
+ else {
299
+ const direct = /^code=([^&#\s]+)/.exec(code);
300
+ if (direct?.[1])
301
+ code = decodeURIComponent(direct[1]).trim();
302
+ }
303
+ }
304
+ }
305
+ const endpoint = `${KIRO_AUTH_SERVICE}/oauth/token`;
306
+ const response = await fetch(endpoint, {
307
+ method: "POST",
308
+ headers: {
309
+ "Content-Type": "application/json",
310
+ Accept: "application/json",
311
+ "User-Agent": kiroDesktopUserAgent(),
312
+ },
313
+ body: JSON.stringify({
314
+ code,
315
+ code_verifier: codeVerifier,
316
+ redirect_uri: KIRO_REDIRECT_URI,
317
+ }),
318
+ signal: AbortSignal.timeout(30_000),
319
+ });
320
+ if (!response.ok) {
321
+ const errorText = await response.text();
322
+ throw new Error(`Kiro social token exchange failed (${response.status}): ${errorText}`);
323
+ }
324
+ const json = asRecord(await response.json().catch(() => ({})));
325
+ const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
326
+ if (!accessToken) {
327
+ throw new Error("Invalid token response from Kiro auth service");
328
+ }
329
+ const refreshToken = typeof json?.refreshToken === "string" ? json.refreshToken : undefined;
330
+ const profileArn = typeof json?.profileArn === "string" ? json.profileArn : undefined;
331
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
332
+ return {
333
+ accessToken,
334
+ refreshToken,
335
+ profileArn,
336
+ expiresAt: Date.now() + expiresIn * 1000,
337
+ authMethod: provider,
338
+ region: KIRO_DEFAULT_REGION,
339
+ };
340
+ }
341
+ export async function listenForKiroSocialCallback(options) {
342
+ const { createServer } = await import("node:http");
343
+ const { writeFile, unlink, mkdir } = await import("node:fs/promises");
344
+ const home = homedir();
345
+ const claiDir = join(home, ".clai");
346
+ await mkdir(claiDir, { recursive: true }).catch(() => { });
347
+ const portFile = join(claiDir, "kiro-pending-callback.json");
348
+ let closed = false;
349
+ const server = createServer((req, res) => {
350
+ const host = req.headers.host || "127.0.0.1";
351
+ const parsed = new URL(req.url || "/", `http://${host}`);
352
+ if (parsed.pathname === "/callback" || parsed.pathname === "/kiro-callback") {
353
+ const urlParam = parsed.searchParams.get("url") || parsed.searchParams.get("code");
354
+ if (urlParam) {
355
+ options.onCode(urlParam);
356
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
357
+ res.end(`<!doctype html><html><body style="font-family:system-ui,-apple-system,sans-serif;background:#18181b;color:#f4f4f5;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center;padding:32px;background:#27272a;border-radius:12px;border:1px solid #3f3f46;box-shadow:0 10px 25px rgba(0,0,0,0.5);max-width:400px;"><h2 style="color:#22c55e;margin-top:0;">Kiro AI Authenticated</h2><p style="color:#a1a1aa;line-height:1.5;">You can close this tab and return to clai.</p></div></body></html>`);
358
+ return;
359
+ }
360
+ }
361
+ res.writeHead(404, { "Content-Type": "text/plain" });
362
+ res.end("Not found");
363
+ });
364
+ await new Promise((resolve, reject) => {
365
+ server.listen(0, "127.0.0.1", () => resolve());
366
+ server.once("error", reject);
367
+ });
368
+ const address = server.address();
369
+ const port = typeof address === "object" && address ? address.port : 0;
370
+ await writeFile(portFile, JSON.stringify({ port, pid: process.pid, createdAt: Date.now() }), "utf8").catch(() => { });
371
+ if (platform() === "linux") {
372
+ try {
373
+ await ensureLinuxSchemeHandler();
374
+ }
375
+ catch { }
376
+ }
377
+ const close = () => {
378
+ if (closed)
379
+ return;
380
+ closed = true;
381
+ server.close();
382
+ unlink(portFile).catch(() => { });
383
+ };
384
+ if (options.signal) {
385
+ options.signal.addEventListener("abort", close, { once: true });
386
+ }
387
+ return { port, close };
388
+ }
389
+ async function ensureLinuxSchemeHandler() {
390
+ const { writeFile, mkdir } = await import("node:fs/promises");
391
+ const { execFile } = await import("node:child_process");
392
+ const home = homedir();
393
+ const binDir = join(home, ".local", "bin");
394
+ const appDir = join(home, ".local", "share", "applications");
395
+ await mkdir(binDir, { recursive: true }).catch(() => { });
396
+ await mkdir(appDir, { recursive: true }).catch(() => { });
397
+ const scriptPath = join(binDir, "clai-kiro-auth");
398
+ const desktopPath = join(appDir, "kiro-scheme-handler.desktop");
399
+ const scriptContent = [
400
+ "#!/bin/sh",
401
+ 'FILE="$HOME/.clai/kiro-pending-callback.json"',
402
+ 'if [ -f "$FILE" ]; then',
403
+ ' PORT=$(grep -o \'"port":[0-9]*\' "$FILE" | cut -d: -f2)',
404
+ ' if [ -n "$PORT" ]; then',
405
+ ' ARG="$1"',
406
+ ' node -e \'const http = require("node:http"); const u = new URL("http://127.0.0.1:" + process.argv[1] + "/callback?url=" + encodeURIComponent(process.argv[2])); http.get(u, () => process.exit(0)).on("error", () => process.exit(0));\' "$PORT" "$ARG" 2>/dev/null || curl -s -m 3 "http://127.0.0.1:${PORT}/callback?url=$(node -e \'console.log(encodeURIComponent(process.argv[1]))\' "$ARG" 2>/dev/null || echo "$ARG")" >/dev/null 2>&1',
407
+ " fi",
408
+ "fi",
409
+ ].join("\n") + "\n";
410
+ await writeFile(scriptPath, scriptContent, { mode: 0o755 });
411
+ const desktopContent = [
412
+ "[Desktop Entry]",
413
+ "Type=Application",
414
+ "Name=Kiro Scheme Handler",
415
+ `Exec=${scriptPath} %u`,
416
+ "StartupNotify=false",
417
+ "NoDisplay=true",
418
+ "MimeType=x-scheme-handler/kiro;",
419
+ ].join("\n") + "\n";
420
+ await writeFile(desktopPath, desktopContent, "utf8");
421
+ await new Promise((resolve) => {
422
+ execFile("gio", ["mime", "x-scheme-handler/kiro", "kiro-scheme-handler.desktop"], () => resolve());
423
+ }).catch(() => { });
424
+ }
425
+ function kiroMachineFingerprint() {
426
+ const seed = process.env.USER || process.env.USERNAME || process.env.HOME || "clai";
427
+ return createHash("sha256").update(seed, "utf8").digest("hex").slice(0, 16);
428
+ }
429
+ export function kiroDesktopUserAgent() {
430
+ return `KiroIDE-${KIRO_DESKTOP_UA_VERSION}-${kiroMachineFingerprint()}`;
431
+ }
432
+ export function isHeadlessEnvironment() {
433
+ if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY) {
434
+ return true;
435
+ }
436
+ if (platform() === "linux") {
437
+ return !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
438
+ }
439
+ return false;
440
+ }
441
+ export function createKiroCliAuthorizationFlow() {
442
+ const codeVerifier = base64UrlEncode(randomBytes(32));
443
+ const codeChallenge = sha256Base64Url(codeVerifier);
444
+ const state = randomBytes(16).toString("hex");
445
+ const params = new URLSearchParams({
446
+ state,
447
+ code_challenge: codeChallenge,
448
+ code_challenge_method: "S256",
449
+ redirect_uri: KIRO_CLI_REDIRECT_URI,
450
+ redirect_from: KIRO_CLI_REDIRECT_FROM,
451
+ });
452
+ return {
453
+ authorizeUrl: `${KIRO_CLI_PORTAL_URL}?${params.toString()}`,
454
+ codeVerifier,
455
+ state,
456
+ };
457
+ }
458
+ export function parseKiroCliCallback(input) {
459
+ const value = input.trim();
460
+ if (!value) {
461
+ throw new Error("missing Kiro callback URL");
462
+ }
463
+ let path = KIRO_CLI_CALLBACK_PATH;
464
+ let query = value;
465
+ const urlMatch = /^https?:\/\/[^/]+(\/[^?]*)?(\?.*)?$/i.exec(value);
466
+ const bareHostMatch = /^(?:localhost|127\.0\.0\.1)(?::\d+)?(\/[^?]*)?(\?.*)?$/i.exec(value);
467
+ if (urlMatch) {
468
+ path = urlMatch[1] || KIRO_CLI_CALLBACK_PATH;
469
+ query = (urlMatch[2] || "").replace(/^\?/, "");
470
+ }
471
+ else if (bareHostMatch) {
472
+ path = bareHostMatch[1] || KIRO_CLI_CALLBACK_PATH;
473
+ query = (bareHostMatch[2] || "").replace(/^\?/, "");
474
+ }
475
+ else if (value.includes("?")) {
476
+ const idx = value.indexOf("?");
477
+ path = value.slice(0, idx) || KIRO_CLI_CALLBACK_PATH;
478
+ query = value.slice(idx + 1);
479
+ }
480
+ const queryNoHash = query.split("#")[0] ?? "";
481
+ const pairs = new URLSearchParams(queryNoHash);
482
+ const error = pairs.get("error");
483
+ if (error) {
484
+ const desc = pairs.get("error_description") || error;
485
+ throw new Error(`Kiro login failed: ${desc}`);
486
+ }
487
+ const code = pairs.get("code");
488
+ if (!code) {
489
+ throw new Error("missing Kiro authorization code");
490
+ }
491
+ const loginOption = pairs.get("login_option");
492
+ if (!loginOption) {
493
+ throw new Error("missing Kiro login_option");
494
+ }
495
+ const state = pairs.get("state") || undefined;
496
+ return { code, loginOption, state, path };
497
+ }
498
+ export async function exchangeKiroPortalCode(callback, codeVerifier) {
499
+ const redirectUri = `${KIRO_CLI_REDIRECT_URI}${callback.path}?login_option=${callback.loginOption}`;
500
+ const endpoint = `${KIRO_AUTH_SERVICE}/oauth/token`;
501
+ const response = await fetch(endpoint, {
502
+ method: "POST",
503
+ headers: {
504
+ "Content-Type": "application/json",
505
+ Accept: "application/json",
506
+ "User-Agent": kiroDesktopUserAgent(),
507
+ },
508
+ body: JSON.stringify({
509
+ code: callback.code,
510
+ code_verifier: codeVerifier,
511
+ redirect_uri: redirectUri,
512
+ invitation_code: null,
513
+ }),
514
+ signal: AbortSignal.timeout(30_000),
515
+ });
516
+ if (!response.ok) {
517
+ const errorText = await response.text();
518
+ throw new Error(`Kiro portal token exchange failed (${response.status}): ${errorText}`);
519
+ }
520
+ const json = asRecord(await response.json().catch(() => ({})));
521
+ const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
522
+ if (!accessToken) {
523
+ throw new Error("Invalid token response from Kiro auth service");
524
+ }
525
+ const refreshToken = typeof json?.refreshToken === "string" ? json.refreshToken : undefined;
526
+ const profileArn = typeof json?.profileArn === "string" ? json.profileArn : undefined;
527
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
528
+ return {
529
+ accessToken,
530
+ refreshToken,
531
+ profileArn,
532
+ expiresAt: Date.now() + expiresIn * 1000,
533
+ authMethod: callback.loginOption === "google" || callback.loginOption === "github"
534
+ ? callback.loginOption
535
+ : "imported",
536
+ region: KIRO_DEFAULT_REGION,
537
+ };
538
+ }
539
+ export function listenForKiroCliCallback(options) {
540
+ const timeoutMs = options.timeoutMs ?? 300_000;
541
+ let resolvePromise;
542
+ let rejectPromise;
543
+ const promise = new Promise((res, rej) => {
544
+ resolvePromise = res;
545
+ rejectPromise = rej;
546
+ });
547
+ let settled = false;
548
+ let server;
549
+ const finish = (err, cb) => {
550
+ if (settled)
551
+ return;
552
+ settled = true;
553
+ clearTimeout(timer);
554
+ server?.close();
555
+ if (err)
556
+ rejectPromise(err);
557
+ else if (cb)
558
+ resolvePromise(cb);
559
+ };
560
+ const timer = setTimeout(() => {
561
+ finish(new Error("Kiro login timed out waiting for browser callback"));
562
+ }, timeoutMs);
563
+ if (typeof timer.unref === "function")
564
+ timer.unref();
565
+ server = createServer((req, res) => {
566
+ const host = req.headers.host || `localhost:${KIRO_CLI_CALLBACK_PORT}`;
567
+ let parsed;
568
+ try {
569
+ parsed = new URL(req.url || "/", `http://${host}`);
570
+ }
571
+ catch {
572
+ res.writeHead(400, { "Content-Type": "text/plain" });
573
+ res.end("Bad request");
574
+ return;
575
+ }
576
+ const pathname = parsed.pathname;
577
+ if (pathname !== KIRO_CLI_CALLBACK_PATH &&
578
+ pathname !== KIRO_CLI_SIGNIN_CALLBACK_PATH &&
579
+ pathname !== "/") {
580
+ res.writeHead(404, { "Content-Type": "text/plain" });
581
+ res.end("Not found");
582
+ return;
583
+ }
584
+ const fullInput = `${pathname}${parsed.search}`;
585
+ let callback;
586
+ try {
587
+ callback = parseKiroCliCallback(fullInput);
588
+ }
589
+ catch (err) {
590
+ const msg = err instanceof Error ? err.message : String(err);
591
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
592
+ res.end(`<!doctype html><html><body style="font-family:system-ui,-apple-system,sans-serif;background:#18181b;color:#f4f4f5;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center;padding:32px;background:#27272a;border-radius:12px;border:1px solid #3f3f46;max-width:420px;"><h2 style="color:#ef4444;margin-top:0;">Kiro login failed</h2><p style="color:#a1a1aa;line-height:1.5;">${msg.replace(/</g, "&lt;")}</p><p style="color:#71717a;font-size:13px;">Return to clai and try again.</p></div></body></html>`);
593
+ finish(err instanceof Error ? err : new Error(msg));
594
+ return;
595
+ }
596
+ if (options.expectedState && callback.state && callback.state !== options.expectedState) {
597
+ const err = new Error("Kiro login state mismatch (possible CSRF). Please try again.");
598
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
599
+ res.end(`<!doctype html><html><body style="font-family:system-ui,-apple-system,sans-serif;background:#18181b;color:#f4f4f5;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center;padding:32px;background:#27272a;border-radius:12px;border:1px solid #3f3f46;max-width:420px;"><h2 style="color:#ef4444;margin-top:0;">Kiro login failed</h2><p style="color:#a1a1aa;line-height:1.5;">State mismatch. Return to clai and try again.</p></div></body></html>`);
600
+ finish(err);
601
+ return;
602
+ }
603
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
604
+ res.end(`<!doctype html><html><body style="font-family:system-ui,-apple-system,sans-serif;background:#18181b;color:#f4f4f5;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center;padding:32px;background:#27272a;border-radius:12px;border:1px solid #3f3f46;max-width:420px;"><h2 style="color:#22c55e;margin-top:0;">Kiro AI Authenticated</h2><p style="color:#a1a1aa;line-height:1.5;">You can close this tab and return to clai.</p></div></body></html>`);
605
+ finish(undefined, callback);
606
+ });
607
+ server.once("error", (err) => {
608
+ if (err.code === "EADDRINUSE") {
609
+ finish(new Error(`Port ${KIRO_CLI_CALLBACK_PORT} is in use — close the other app or use device-code sign-in instead.`));
610
+ }
611
+ else {
612
+ finish(err);
613
+ }
614
+ });
615
+ server.listen(KIRO_CLI_CALLBACK_PORT, "127.0.0.1");
616
+ if (options.signal) {
617
+ options.signal.addEventListener("abort", () => finish(new Error("Kiro authentication cancelled")), { once: true });
618
+ }
619
+ return {
620
+ port: KIRO_CLI_CALLBACK_PORT,
621
+ promise,
622
+ close() {
623
+ finish(new Error("Kiro authentication cancelled"));
624
+ },
625
+ };
626
+ }
627
+ export async function refreshKiroToken(refreshToken, options = {}) {
628
+ const { clientId, clientSecret, region } = options;
629
+ if (clientId && clientSecret) {
630
+ const safeRegion = assertValidAwsRegion(region);
631
+ const endpoint = `https://oidc.${safeRegion}.amazonaws.com/token`;
632
+ const response = await fetch(endpoint, {
633
+ method: "POST",
634
+ headers: {
635
+ "Content-Type": "application/json",
636
+ Accept: "application/json",
637
+ },
638
+ body: JSON.stringify({
639
+ clientId,
640
+ clientSecret,
641
+ refreshToken,
642
+ grantType: "refresh_token",
643
+ }),
644
+ signal: AbortSignal.timeout(30_000),
645
+ });
646
+ if (!response.ok) {
647
+ const errorText = await response.text();
648
+ throw new Error(`Kiro token refresh failed (${response.status}): ${errorText}`);
649
+ }
650
+ const json = asRecord(await response.json().catch(() => ({})));
651
+ const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
652
+ if (!accessToken) {
653
+ throw new Error("Invalid token refresh response from AWS OIDC");
654
+ }
655
+ const newRefreshToken = typeof json?.refreshToken === "string"
656
+ ? json.refreshToken
657
+ : refreshToken;
658
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
659
+ let profileArn = options.profileArn;
660
+ if (!profileArn) {
661
+ try {
662
+ profileArn = await listAvailableProfiles(accessToken, safeRegion);
663
+ }
664
+ catch {
665
+ profileArn = undefined;
666
+ }
667
+ }
668
+ return {
669
+ accessToken,
670
+ refreshToken: newRefreshToken,
671
+ profileArn,
672
+ expiresAt: Date.now() + expiresIn * 1000,
673
+ authMethod: options.authMethod ?? "builder-id",
674
+ region: safeRegion,
675
+ clientId,
676
+ clientSecret,
677
+ startUrl: options.startUrl,
678
+ };
679
+ }
680
+ const endpoint = `${KIRO_AUTH_SERVICE}/refreshToken`;
681
+ const response = await fetch(endpoint, {
682
+ method: "POST",
683
+ headers: {
684
+ "Content-Type": "application/json",
685
+ Accept: "application/json",
686
+ "User-Agent": kiroDesktopUserAgent(),
687
+ },
688
+ body: JSON.stringify({ refreshToken }),
689
+ signal: AbortSignal.timeout(30_000),
690
+ });
691
+ if (!response.ok) {
692
+ const errorText = await response.text();
693
+ throw new Error(`Kiro token refresh failed (${response.status}): ${errorText}`);
694
+ }
695
+ const json = asRecord(await response.json().catch(() => ({})));
696
+ const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
697
+ if (!accessToken) {
698
+ throw new Error("Invalid token refresh response from Kiro service");
699
+ }
700
+ const newRefreshToken = typeof json?.refreshToken === "string" ? json.refreshToken : refreshToken;
701
+ const profileArn = typeof json?.profileArn === "string"
702
+ ? json.profileArn
703
+ : options.profileArn;
704
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
705
+ return {
706
+ accessToken,
707
+ refreshToken: newRefreshToken,
708
+ profileArn,
709
+ expiresAt: Date.now() + expiresIn * 1000,
710
+ authMethod: options.authMethod ?? "imported",
711
+ region: options.region ?? KIRO_DEFAULT_REGION,
712
+ };
713
+ }
714
+ export async function listAvailableProfiles(accessToken, region = KIRO_DEFAULT_REGION) {
715
+ const safeRegion = assertValidAwsRegion(region);
716
+ const endpoint = `https://codewhisperer.${safeRegion}.amazonaws.com`;
717
+ const response = await fetch(endpoint, {
718
+ method: "POST",
719
+ headers: {
720
+ "Content-Type": "application/x-amz-json-1.0",
721
+ "x-amz-target": "AmazonCodeWhispererService.ListAvailableProfiles",
722
+ Authorization: `Bearer ${accessToken}`,
723
+ Accept: "application/json",
724
+ },
725
+ body: JSON.stringify({ maxResults: 10 }),
726
+ signal: AbortSignal.timeout(15_000),
727
+ });
728
+ if (!response.ok)
729
+ return undefined;
730
+ const json = asRecord(await response.json().catch(() => ({})));
731
+ const profiles = Array.isArray(json?.profiles) ? json.profiles : [];
732
+ const arnOf = (p) => {
733
+ const rec = asRecord(p);
734
+ return typeof rec?.arn === "string"
735
+ ? rec.arn
736
+ : typeof rec?.profileArn === "string"
737
+ ? rec.profileArn
738
+ : null;
739
+ };
740
+ const match = profiles.find((p) => arnOf(p)?.split(":")[3] === safeRegion) ||
741
+ profiles[0];
742
+ return arnOf(match) ?? undefined;
743
+ }
744
+ export async function listAvailableApiKeyModels(apiKey, region = KIRO_DEFAULT_REGION) {
745
+ const safeRegion = assertValidAwsRegion(region);
746
+ const params = new URLSearchParams({ origin: "AI_EDITOR" });
747
+ const endpoint = `https://q.${safeRegion}.amazonaws.com/ListAvailableModels?${params.toString()}`;
748
+ const response = await fetch(endpoint, {
749
+ method: "GET",
750
+ headers: {
751
+ Authorization: `Bearer ${apiKey}`,
752
+ TokenType: "API_KEY",
753
+ Accept: "application/json",
754
+ "User-Agent": "AWS-SDK-JS/3.0.0 kiro-ide/1.0.0",
755
+ "X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
756
+ },
757
+ signal: AbortSignal.timeout(20_000),
758
+ });
759
+ if (!response.ok) {
760
+ const errorText = await response.text();
761
+ throw new Error(`Failed to list API key models (${response.status}): ${errorText}`);
762
+ }
763
+ const json = asRecord(await response.json().catch(() => ({})));
764
+ const models = Array.isArray(json?.models) ? json.models : [];
765
+ return models
766
+ .map((m) => {
767
+ const rec = asRecord(m);
768
+ return typeof rec?.modelId === "string"
769
+ ? rec.modelId
770
+ : typeof rec?.id === "string"
771
+ ? rec.id
772
+ : "";
773
+ })
774
+ .filter(Boolean);
775
+ }
776
+ export async function validateKiroApiKey(apiKey, region = KIRO_DEFAULT_REGION) {
777
+ try {
778
+ const models = await listAvailableApiKeyModels(apiKey, region);
779
+ return models.length > 0;
780
+ }
781
+ catch {
782
+ return false;
783
+ }
784
+ }
785
+ export async function candidateKiroCredentialPaths() {
786
+ const home = homedir();
787
+ const os = platform();
788
+ const paths = [
789
+ join(home, ".kiro", "auth.json"),
790
+ join(home, ".kiro", "credentials.json"),
791
+ ];
792
+ if (os === "darwin") {
793
+ paths.push(join(home, "Library", "Application Support", "Kiro", "auth.json"), join(home, "Library", "Application Support", "kiro-desktop", "auth.json"));
794
+ }
795
+ else if (os === "linux") {
796
+ paths.push(join(home, ".config", "kiro", "auth.json"), join(home, ".config", "kiro-desktop", "auth.json"));
797
+ }
798
+ else if (os === "win32") {
799
+ const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
800
+ paths.push(join(appData, "Kiro", "auth.json"), join(appData, "kiro-desktop", "auth.json"));
801
+ }
802
+ const ssoCacheDir = join(home, ".aws", "sso", "cache");
803
+ try {
804
+ const files = await readdir(ssoCacheDir);
805
+ for (const file of files) {
806
+ if (file.endsWith(".json")) {
807
+ paths.push(join(ssoCacheDir, file));
808
+ }
809
+ }
810
+ }
811
+ catch { }
812
+ return paths;
813
+ }
814
+ export async function readKiroStoredAuth() {
815
+ const paths = await candidateKiroCredentialPaths();
816
+ for (const path of paths) {
817
+ let parsed;
818
+ try {
819
+ parsed = JSON.parse(await readFile(path, "utf8"));
820
+ }
821
+ catch {
822
+ continue;
823
+ }
824
+ const rec = asRecord(parsed);
825
+ if (!rec)
826
+ continue;
827
+ const accessToken = typeof rec.accessToken === "string"
828
+ ? rec.accessToken
829
+ : typeof rec.access_token === "string"
830
+ ? rec.access_token
831
+ : "";
832
+ const refreshToken = typeof rec.refreshToken === "string"
833
+ ? rec.refreshToken
834
+ : typeof rec.refresh_token === "string"
835
+ ? rec.refresh_token
836
+ : undefined;
837
+ if (!accessToken && !refreshToken)
838
+ continue;
839
+ const clientId = typeof rec.clientId === "string"
840
+ ? rec.clientId
841
+ : typeof rec.client_id === "string"
842
+ ? rec.client_id
843
+ : undefined;
844
+ const clientSecret = typeof rec.clientSecret === "string"
845
+ ? rec.clientSecret
846
+ : typeof rec.client_secret === "string"
847
+ ? rec.client_secret
848
+ : undefined;
849
+ const region = typeof rec.region === "string" && rec.region
850
+ ? rec.region
851
+ : KIRO_DEFAULT_REGION;
852
+ const profileArn = typeof rec.profileArn === "string"
853
+ ? rec.profileArn
854
+ : typeof rec.profile_arn === "string"
855
+ ? rec.profile_arn
856
+ : undefined;
857
+ const startUrl = typeof rec.startUrl === "string"
858
+ ? rec.startUrl
859
+ : typeof rec.start_url === "string"
860
+ ? rec.start_url
861
+ : undefined;
862
+ let expiresAt;
863
+ if (typeof rec.expiresAt === "number") {
864
+ expiresAt = rec.expiresAt;
865
+ }
866
+ else if (typeof rec.expires_at === "string") {
867
+ expiresAt = Date.parse(rec.expires_at);
868
+ }
869
+ return {
870
+ accessToken,
871
+ refreshToken,
872
+ profileArn,
873
+ expiresAt,
874
+ region,
875
+ clientId,
876
+ clientSecret,
877
+ startUrl,
878
+ authMethod: clientId && clientSecret ? "builder-id" : "imported",
879
+ };
880
+ }
881
+ const home = homedir();
882
+ const os = platform();
883
+ const sqlitePaths = [
884
+ join(home, ".local", "share", "kiro-cli", "data.sqlite3"),
885
+ join(home, ".config", "kiro-cli", "data.sqlite3"),
886
+ join(home, ".config", "kiro", "data.sqlite3"),
887
+ join(home, ".kiro", "data.sqlite3"),
888
+ ];
889
+ if (os === "darwin") {
890
+ sqlitePaths.push(join(home, "Library", "Application Support", "kiro-cli", "data.sqlite3"), join(home, "Library", "Application Support", "Kiro", "data.sqlite3"));
891
+ }
892
+ else if (os === "win32") {
893
+ const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
894
+ sqlitePaths.push(join(appData, "kiro-cli", "data.sqlite3"), join(appData, "Kiro", "data.sqlite3"));
895
+ }
896
+ for (const dbPath of sqlitePaths) {
897
+ try {
898
+ const { DatabaseSync } = await import("node:sqlite");
899
+ const db = new DatabaseSync(dbPath, { readOnly: true });
900
+ try {
901
+ const rows = db.prepare("SELECT key, value FROM auth_kv").all();
902
+ const byKey = new Map(rows.map((r) => [r.key, r.value]));
903
+ const tokenKeys = [
904
+ "kirocli:social:token",
905
+ "kirocli:oidc:token",
906
+ "kirocli:odic:token",
907
+ "codewhisperer:oidc:token",
908
+ "codewhisperer:odic:token",
909
+ ];
910
+ const deviceKeys = [
911
+ "kirocli:oidc:device-registration",
912
+ "kirocli:odic:device-registration",
913
+ "codewhisperer:oidc:device-registration",
914
+ "codewhisperer:odic:device-registration",
915
+ ];
916
+ const tokenRaw = tokenKeys.map((k) => byKey.get(k)).find((v) => v);
917
+ if (!tokenRaw)
918
+ continue;
919
+ const deviceRaw = deviceKeys.map((k) => byKey.get(k)).find((v) => v);
920
+ try {
921
+ const parsed = JSON.parse(tokenRaw);
922
+ const device = deviceRaw
923
+ ? JSON.parse(deviceRaw)
924
+ : undefined;
925
+ const accessToken = typeof parsed.access_token === "string"
926
+ ? parsed.access_token
927
+ : typeof parsed.accessToken === "string"
928
+ ? parsed.accessToken
929
+ : "";
930
+ const refreshToken = typeof parsed.refresh_token === "string"
931
+ ? parsed.refresh_token
932
+ : typeof parsed.refreshToken === "string"
933
+ ? parsed.refreshToken
934
+ : undefined;
935
+ if (!accessToken && !refreshToken)
936
+ continue;
937
+ let expiresAt;
938
+ if (typeof parsed.expires_at === "number")
939
+ expiresAt = parsed.expires_at;
940
+ else if (typeof parsed.expiresAt === "number")
941
+ expiresAt = parsed.expiresAt;
942
+ else if (typeof parsed.expires_at === "string")
943
+ expiresAt = Date.parse(parsed.expires_at);
944
+ const profileArn = typeof parsed.profile_arn === "string"
945
+ ? parsed.profile_arn
946
+ : typeof parsed.profileArn === "string"
947
+ ? parsed.profileArn
948
+ : undefined;
949
+ const clientId = (typeof parsed.clientId === "string" && parsed.clientId) ||
950
+ (typeof parsed.client_id === "string" && parsed.client_id) ||
951
+ (typeof device?.clientId === "string" && device.clientId) ||
952
+ (typeof device?.client_id === "string" && device.client_id) ||
953
+ undefined;
954
+ const clientSecret = (typeof parsed.clientSecret === "string" && parsed.clientSecret) ||
955
+ (typeof parsed.client_secret === "string" && parsed.client_secret) ||
956
+ (typeof device?.clientSecret === "string" && device.clientSecret) ||
957
+ (typeof device?.client_secret === "string" && device.client_secret) ||
958
+ undefined;
959
+ const provider = typeof parsed.provider === "string"
960
+ ? parsed.provider.toLowerCase()
961
+ : undefined;
962
+ const authMethod = provider === "google" || provider === "github"
963
+ ? provider
964
+ : clientId && clientSecret
965
+ ? "builder-id"
966
+ : "imported";
967
+ return {
968
+ accessToken,
969
+ refreshToken,
970
+ expiresAt,
971
+ profileArn,
972
+ authMethod,
973
+ region: KIRO_DEFAULT_REGION,
974
+ clientId,
975
+ clientSecret,
976
+ };
977
+ }
978
+ catch { }
979
+ }
980
+ finally {
981
+ db.close();
982
+ }
983
+ }
984
+ catch { }
985
+ }
986
+ return undefined;
987
+ }
988
+ export async function importExistingKiroAuth() {
989
+ const stored = await readKiroStoredAuth();
990
+ if (!stored)
991
+ return undefined;
992
+ if (stored.refreshToken &&
993
+ stored.expiresAt !== undefined &&
994
+ stored.expiresAt < Date.now()) {
995
+ try {
996
+ return await refreshKiroToken(stored.refreshToken, stored);
997
+ }
998
+ catch {
999
+ return stored;
1000
+ }
1001
+ }
1002
+ return stored;
1003
+ }
1004
+ export async function maybeRefreshKiroCredential(key, storedRefreshToken) {
1005
+ const decoded = decodeKiroKey(key);
1006
+ const refreshToken = decoded?.refreshToken || storedRefreshToken;
1007
+ if (!refreshToken)
1008
+ return undefined;
1009
+ try {
1010
+ const refreshed = await refreshKiroToken(refreshToken, decoded);
1011
+ return encodeKiroKey(refreshed);
1012
+ }
1013
+ catch {
1014
+ return undefined;
1015
+ }
1016
+ }
1017
+ //# sourceMappingURL=kiro-auth.js.map