@zhafron/opencode-kiro-auth 1.2.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -1,18 +1,20 @@
1
- import { loadConfig } from './plugin/config';
2
1
  import { exec } from 'node:child_process';
3
- import { AccountManager, generateAccountId } from './plugin/accounts';
2
+ import { KIRO_CONSTANTS } from './constants';
4
3
  import { accessTokenExpired, encodeRefreshToken } from './kiro/auth';
5
- import { refreshAccessToken } from './plugin/token';
4
+ import { authorizeKiroIDC } from './kiro/oauth-idc';
5
+ import { AccountManager, generateAccountId } from './plugin/accounts';
6
+ import { promptAddAnotherAccount, promptLoginMode } from './plugin/cli';
7
+ import { loadConfig } from './plugin/config';
8
+ import { KiroTokenRefreshError } from './plugin/errors';
9
+ import * as logger from './plugin/logger';
6
10
  import { transformToCodeWhisperer } from './plugin/request';
7
11
  import { parseEventStream } from './plugin/response';
12
+ import { startIDCAuthServer } from './plugin/server';
13
+ import { migrateJsonToSqlite } from './plugin/storage/migration';
8
14
  import { transformKiroStream } from './plugin/streaming';
15
+ import { syncFromKiroCli } from './plugin/sync/kiro-cli';
16
+ import { refreshAccessToken } from './plugin/token';
9
17
  import { fetchUsageLimits, updateAccountQuota } from './plugin/usage';
10
- import { authorizeKiroIDC } from './kiro/oauth-idc';
11
- import { startIDCAuthServer } from './plugin/server';
12
- import { KiroTokenRefreshError } from './plugin/errors';
13
- import { promptAddAnotherAccount, promptLoginMode } from './plugin/cli';
14
- import { KIRO_CONSTANTS } from './constants';
15
- import * as logger from './plugin/logger';
16
18
  const KIRO_PROVIDER_ID = 'kiro';
17
19
  const KIRO_API_PATTERN = /^(https?:\/\/)?q\.[a-z0-9-]+\.amazonaws\.com/;
18
20
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -28,14 +30,14 @@ const formatUsageMessage = (usedCount, limitCount, email) => {
28
30
  const openBrowser = (url) => {
29
31
  const escapedUrl = url.replace(/"/g, '\\"');
30
32
  const platform = process.platform;
31
- const command = platform === 'win32'
33
+ const cmd = platform === 'win32'
32
34
  ? `cmd /c start "" "${escapedUrl}"`
33
35
  : platform === 'darwin'
34
36
  ? `open "${escapedUrl}"`
35
37
  : `xdg-open "${escapedUrl}"`;
36
- exec(command, (error) => {
38
+ exec(cmd, (error) => {
37
39
  if (error)
38
- logger.warn(`Failed to open browser automatically: ${error.message}`, error);
40
+ logger.warn(`Browser error: ${error.message}`);
39
41
  });
40
42
  };
41
43
  export const createKiroPlugin = (id) => async ({ client, directory }) => {
@@ -48,6 +50,9 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
48
50
  provider: id,
49
51
  loader: async (getAuth) => {
50
52
  await getAuth();
53
+ await migrateJsonToSqlite();
54
+ if (config.auto_sync_kiro_cli)
55
+ await syncFromKiroCli();
51
56
  const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
52
57
  return {
53
58
  apiKey: '',
@@ -60,114 +65,107 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
60
65
  const model = extractModel(url) || body.model || 'claude-sonnet-4-5';
61
66
  const think = model.endsWith('-thinking') || !!body.providerOptions?.thinkingConfig;
62
67
  const budget = body.providerOptions?.thinkingConfig?.thinkingBudget || 20000;
63
- let retry = 0;
64
- let iterations = 0;
65
- const startTime = Date.now();
66
- const maxIterations = config.max_request_iterations;
67
- const timeoutMs = config.request_timeout_ms;
68
+ let retry = 0, iterations = 0, reductionFactor = 1.0;
69
+ const startTime = Date.now(), maxIterations = config.max_request_iterations, timeoutMs = config.request_timeout_ms;
68
70
  while (true) {
69
71
  iterations++;
70
- const elapsed = Date.now() - startTime;
71
- if (iterations > maxIterations) {
72
- throw new Error(`Request exceeded max iterations (${maxIterations}). All accounts may be unhealthy or rate-limited.`);
73
- }
74
- if (elapsed > timeoutMs) {
75
- throw new Error(`Request timeout after ${Math.ceil(elapsed / 1000)}s. Max timeout: ${Math.ceil(timeoutMs / 1000)}s.`);
76
- }
72
+ if (iterations > maxIterations)
73
+ throw new Error(`Exceeded max iterations (${maxIterations})`);
74
+ if (Date.now() - startTime > timeoutMs)
75
+ throw new Error('Request timeout');
77
76
  const count = am.getAccountCount();
78
77
  if (count === 0)
79
- throw new Error('No accounts. Login first.');
78
+ throw new Error('No accounts');
80
79
  const acc = am.getCurrentOrNext();
81
80
  if (!acc) {
82
- const w = am.getMinWaitTime() || 60000;
83
- showToast(`All accounts rate-limited. Waiting ${Math.ceil(w / 1000)}s...`, 'warning');
84
- await sleep(w);
85
- continue;
86
- }
87
- if (count > 1 && am.shouldShowToast())
88
- showToast(`Using ${acc.realEmail || acc.email} (${am.getAccounts().indexOf(acc) + 1}/${count})`, 'info');
89
- if (am.shouldShowUsageToast() &&
90
- acc.usedCount !== undefined &&
91
- acc.limitCount !== undefined) {
92
- const percentage = acc.limitCount > 0 ? (acc.usedCount / acc.limitCount) * 100 : 0;
93
- const variant = percentage >= 80 ? 'warning' : 'info';
94
- showToast(formatUsageMessage(acc.usedCount, acc.limitCount, acc.realEmail || acc.email), variant);
81
+ const wait = am.getMinWaitTime();
82
+ if (wait > 0 && wait < 30000) {
83
+ if (am.shouldShowToast())
84
+ showToast(`All accounts rate-limited. Waiting ${Math.ceil(wait / 1000)}s...`, 'warning');
85
+ await sleep(wait);
86
+ continue;
87
+ }
88
+ throw new Error('All accounts are unhealthy or rate-limited');
95
89
  }
96
- let auth = am.toAuthDetails(acc);
90
+ const auth = am.toAuthDetails(acc);
97
91
  if (accessTokenExpired(auth, config.token_expiry_buffer_ms)) {
98
92
  try {
99
- logger.log(`Refreshing token for ${acc.realEmail || acc.email}`);
100
- auth = await refreshAccessToken(auth);
101
- am.updateFromAuth(acc, auth);
93
+ const newAuth = await refreshAccessToken(auth);
94
+ am.updateFromAuth(acc, newAuth);
102
95
  await am.saveToDisk();
103
96
  }
104
97
  catch (e) {
105
- const msg = e instanceof KiroTokenRefreshError ? e.message : String(e);
106
- showToast(`Refresh failed for ${acc.realEmail || acc.email}: ${msg}`, 'error');
107
- if (e instanceof KiroTokenRefreshError && e.code === 'invalid_grant') {
108
- am.removeAccount(acc);
98
+ if (config.auto_sync_kiro_cli)
99
+ await syncFromKiroCli();
100
+ const refreshedAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
101
+ const stillAcc = refreshedAm.getAccounts().find((a) => a.id === acc.id);
102
+ if (stillAcc &&
103
+ !accessTokenExpired(refreshedAm.toAuthDetails(stillAcc), config.token_expiry_buffer_ms)) {
104
+ showToast('Credentials recovered from Kiro CLI sync.', 'info');
105
+ continue;
106
+ }
107
+ if (e instanceof KiroTokenRefreshError &&
108
+ (e.code === 'ExpiredTokenException' ||
109
+ e.code === 'InvalidTokenException' ||
110
+ e.code === 'HTTP_401' ||
111
+ e.code === 'HTTP_403')) {
112
+ am.markUnhealthy(acc, e.message);
109
113
  await am.saveToDisk();
110
114
  continue;
111
115
  }
112
116
  throw e;
113
117
  }
114
118
  }
115
- const prep = transformToCodeWhisperer(url, init?.body, model, auth, think, budget);
119
+ const prepRequest = (f) => transformToCodeWhisperer(url, init?.body, model, auth, think, budget, f);
120
+ let prep = prepRequest(reductionFactor);
116
121
  const apiTimestamp = config.enable_log_api_request ? logger.getTimestamp() : null;
117
- let parsedBody = null;
118
- if (prep.init.body && typeof prep.init.body === 'string') {
122
+ if (config.enable_log_api_request && apiTimestamp) {
123
+ let parsedBody = null;
119
124
  try {
120
- parsedBody = JSON.parse(prep.init.body);
125
+ parsedBody = prep.init.body ? JSON.parse(prep.init.body) : null;
121
126
  }
122
- catch (e) {
123
- parsedBody = prep.init.body;
124
- }
125
- }
126
- const requestData = {
127
- url: prep.url,
128
- method: prep.init.method,
129
- headers: prep.init.headers,
130
- body: parsedBody,
131
- conversationId: prep.conversationId,
132
- model: prep.effectiveModel,
133
- email: acc.realEmail || acc.email
134
- };
135
- if (config.enable_log_api_request && apiTimestamp) {
136
- logger.logApiRequest(requestData, apiTimestamp);
127
+ catch { }
128
+ logger.logApiRequest({
129
+ url: prep.url,
130
+ method: prep.init.method,
131
+ headers: prep.init.headers,
132
+ body: parsedBody,
133
+ conversationId: prep.conversationId,
134
+ model: prep.effectiveModel,
135
+ email: acc.realEmail || acc.email
136
+ }, apiTimestamp);
137
137
  }
138
138
  try {
139
139
  const res = await fetch(prep.url, prep.init);
140
140
  if (config.enable_log_api_request && apiTimestamp) {
141
- const responseHeaders = {};
142
- res.headers.forEach((value, key) => {
143
- responseHeaders[key] = value;
141
+ const h = {};
142
+ res.headers.forEach((v, k) => {
143
+ h[k] = v;
144
144
  });
145
145
  logger.logApiResponse({
146
146
  status: res.status,
147
147
  statusText: res.statusText,
148
- headers: responseHeaders,
148
+ headers: h,
149
149
  conversationId: prep.conversationId,
150
150
  model: prep.effectiveModel
151
151
  }, apiTimestamp);
152
152
  }
153
153
  if (res.ok) {
154
154
  if (config.usage_tracking_enabled) {
155
- const syncUsage = async (attempt = 0) => {
155
+ const sync = async (att = 0) => {
156
156
  try {
157
157
  const u = await fetchUsageLimits(auth);
158
158
  updateAccountQuota(acc, u, am);
159
159
  await am.saveToDisk();
160
160
  }
161
161
  catch (e) {
162
- if (attempt < config.usage_sync_max_retries) {
163
- const delay = 1000 * Math.pow(2, attempt);
164
- await sleep(delay);
165
- return syncUsage(attempt + 1);
162
+ if (att < config.usage_sync_max_retries) {
163
+ await sleep(1000 * Math.pow(2, att));
164
+ return sync(att + 1);
166
165
  }
167
- logger.warn(`Usage sync failed for ${acc.realEmail || acc.email} after ${attempt + 1} attempts: ${e.message}`);
168
166
  }
169
167
  };
170
- syncUsage().catch(() => { });
168
+ sync().catch(() => { });
171
169
  }
172
170
  if (prep.streaming) {
173
171
  const s = transformKiroStream(res, model, prep.conversationId);
@@ -184,9 +182,7 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
184
182
  }
185
183
  }), { headers: { 'Content-Type': 'text/event-stream' } });
186
184
  }
187
- const text = await res.text();
188
- const p = parseEventStream(text);
189
- const oai = {
185
+ const text = await res.text(), p = parseEventStream(text), oai = {
190
186
  id: prep.conversationId,
191
187
  object: 'chat.completion',
192
188
  created: Math.floor(Date.now() / 1000),
@@ -217,71 +213,70 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
217
213
  headers: { 'Content-Type': 'application/json' }
218
214
  });
219
215
  }
216
+ if (res.status === 400 && reductionFactor > 0.4) {
217
+ reductionFactor -= 0.2;
218
+ showToast(`Context too long. Retrying with ${Math.round(reductionFactor * 100)}%...`, 'warning');
219
+ prep = prepRequest(reductionFactor);
220
+ continue;
221
+ }
220
222
  if (res.status === 401 && retry < config.rate_limit_max_retries) {
221
223
  retry++;
222
224
  continue;
223
225
  }
224
226
  if (res.status === 429) {
225
- const wait = parseInt(res.headers.get('retry-after') || '60') * 1000;
226
- am.markRateLimited(acc, wait);
227
+ const w = parseInt(res.headers.get('retry-after') || '60') * 1000;
228
+ am.markRateLimited(acc, w);
227
229
  await am.saveToDisk();
228
230
  if (count > 1) {
229
- showToast(`Rate limited on ${acc.realEmail || acc.email}. Switching account...`, 'warning');
230
- continue;
231
- }
232
- else {
233
- showToast(`Rate limited. Retrying in ${Math.ceil(wait / 1000)}s...`, 'warning');
234
- await sleep(wait);
231
+ showToast(`Rate limited. Switching account...`, 'warning');
235
232
  continue;
236
233
  }
234
+ showToast(`Rate limited. Waiting ${Math.ceil(w / 1000)}s...`, 'warning');
235
+ await sleep(w);
236
+ continue;
237
237
  }
238
238
  if ((res.status === 402 || res.status === 403) && count > 1) {
239
- showToast(`${res.status === 402 ? 'Quota exhausted' : 'Forbidden'} on ${acc.realEmail || acc.email}. Switching...`, 'warning');
240
239
  am.markUnhealthy(acc, res.status === 402 ? 'Quota' : 'Forbidden');
241
240
  await am.saveToDisk();
242
241
  continue;
243
242
  }
244
- const responseHeaders = {};
245
- res.headers.forEach((value, key) => {
246
- responseHeaders[key] = value;
243
+ const h = {};
244
+ res.headers.forEach((v, k) => {
245
+ h[k] = v;
247
246
  });
248
- const responseData = {
247
+ const rData = {
249
248
  status: res.status,
250
249
  statusText: res.statusText,
251
- headers: responseHeaders,
250
+ headers: h,
252
251
  error: `Kiro Error: ${res.status}`,
253
252
  conversationId: prep.conversationId,
254
253
  model: prep.effectiveModel
255
254
  };
256
- if (config.enable_log_api_request && apiTimestamp) {
257
- logger.logApiResponse(responseData, apiTimestamp);
258
- }
259
- else {
260
- const errorTimestamp = logger.getTimestamp();
261
- logger.logApiError(requestData, responseData, errorTimestamp);
255
+ let lastBody = null;
256
+ try {
257
+ lastBody = prep.init.body ? JSON.parse(prep.init.body) : null;
262
258
  }
259
+ catch { }
260
+ if (!config.enable_log_api_request)
261
+ logger.logApiError({
262
+ url: prep.url,
263
+ method: prep.init.method,
264
+ headers: prep.init.headers,
265
+ body: lastBody,
266
+ conversationId: prep.conversationId,
267
+ model: prep.effectiveModel,
268
+ email: acc.realEmail || acc.email
269
+ }, rData, logger.getTimestamp());
263
270
  throw new Error(`Kiro Error: ${res.status}`);
264
271
  }
265
272
  catch (e) {
266
273
  if (isNetworkError(e) && retry < config.rate_limit_max_retries) {
267
- const delay = 5000 * Math.pow(2, retry);
268
- showToast(`Network error. Retrying in ${Math.ceil(delay / 1000)}s...`, 'warning');
269
- await sleep(delay);
274
+ const d = 5000 * Math.pow(2, retry);
275
+ showToast(`Network error. Retrying in ${Math.ceil(d / 1000)}s...`, 'warning');
276
+ await sleep(d);
270
277
  retry++;
271
278
  continue;
272
279
  }
273
- const networkErrorData = {
274
- error: String(e),
275
- conversationId: prep.conversationId,
276
- model: prep.effectiveModel
277
- };
278
- if (config.enable_log_api_request && apiTimestamp) {
279
- logger.logApiResponse(networkErrorData, apiTimestamp);
280
- }
281
- else {
282
- const errorTimestamp = logger.getTimestamp();
283
- logger.logApiError(requestData, networkErrorData, errorTimestamp);
284
- }
285
280
  throw e;
286
281
  }
287
282
  }
@@ -299,123 +294,83 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
299
294
  const accounts = [];
300
295
  let startFresh = true;
301
296
  const existingAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
302
- if (existingAm.getAccountCount() > 0) {
303
- const existingAccounts = existingAm.getAccounts().map((acc, idx) => ({
297
+ const allAccounts = existingAm.getAccounts();
298
+ const idcAccounts = allAccounts.filter((a) => a.authMethod === 'idc');
299
+ if (idcAccounts.length > 0) {
300
+ const existingAccounts = idcAccounts.map((acc, idx) => ({
304
301
  email: acc.realEmail || acc.email,
305
302
  index: idx
306
303
  }));
307
- const loginMode = await promptLoginMode(existingAccounts);
308
- startFresh = loginMode === 'fresh';
309
- console.log(startFresh
310
- ? '\nStarting fresh - existing accounts will be replaced.\n'
311
- : '\nAdding to existing accounts.\n');
304
+ startFresh = (await promptLoginMode(existingAccounts)) === 'fresh';
312
305
  }
313
306
  while (true) {
314
- console.log(`\n=== Kiro IDC Auth (Account ${accounts.length + 1}) ===\n`);
315
- const result = await (async () => {
316
- try {
317
- const authData = await authorizeKiroIDC(region);
318
- const { url, waitForAuth } = await startIDCAuthServer(authData, config.auth_server_port_start, config.auth_server_port_range);
319
- console.log('OAuth URL:\n' + url + '\n');
320
- openBrowser(url);
321
- const res = await waitForAuth();
322
- return res;
323
- }
324
- catch (e) {
325
- return { type: 'failed', error: e.message };
326
- }
327
- })();
328
- if ('type' in result && result.type === 'failed') {
329
- if (accounts.length === 0) {
330
- return resolve({
331
- url: '',
332
- instructions: `Authentication failed: ${result.error}`,
333
- method: 'auto',
334
- callback: async () => ({ type: 'failed' })
335
- });
336
- }
337
- console.warn(`[opencode-kiro-auth] Skipping failed account ${accounts.length + 1}: ${result.error}`);
338
- break;
339
- }
340
- const successResult = result;
341
- accounts.push(successResult);
342
- const isFirstAccount = accounts.length === 1;
343
- const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
344
- if (isFirstAccount && startFresh) {
345
- am.getAccounts().forEach((acc) => am.removeAccount(acc));
346
- }
347
- const acc = {
348
- id: generateAccountId(),
349
- email: successResult.email,
350
- authMethod: 'idc',
351
- region,
352
- clientId: successResult.clientId,
353
- clientSecret: successResult.clientSecret,
354
- refreshToken: successResult.refreshToken,
355
- accessToken: successResult.accessToken,
356
- expiresAt: successResult.expiresAt,
357
- rateLimitResetTime: 0,
358
- isHealthy: true
359
- };
360
307
  try {
361
- const u = await fetchUsageLimits({
362
- refresh: encodeRefreshToken({
363
- refreshToken: successResult.refreshToken,
364
- clientId: successResult.clientId,
365
- clientSecret: successResult.clientSecret,
366
- authMethod: 'idc'
367
- }),
368
- access: successResult.accessToken,
369
- expires: successResult.expiresAt,
308
+ const authData = await authorizeKiroIDC(region);
309
+ const { url, waitForAuth } = await startIDCAuthServer(authData, config.auth_server_port_start, config.auth_server_port_range);
310
+ openBrowser(url);
311
+ const res = await waitForAuth();
312
+ accounts.push(res);
313
+ const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
314
+ if (accounts.length === 1 && startFresh)
315
+ am.getAccounts()
316
+ .filter((a) => a.authMethod === 'idc')
317
+ .forEach((a) => am.removeAccount(a));
318
+ const acc = {
319
+ id: generateAccountId(),
320
+ email: res.email,
370
321
  authMethod: 'idc',
371
322
  region,
372
- clientId: successResult.clientId,
373
- clientSecret: successResult.clientSecret,
374
- email: successResult.email
375
- });
376
- am.updateUsage(acc.id, {
377
- usedCount: u.usedCount,
378
- limitCount: u.limitCount,
379
- realEmail: u.email
380
- });
323
+ clientId: res.clientId,
324
+ clientSecret: res.clientSecret,
325
+ refreshToken: res.refreshToken,
326
+ accessToken: res.accessToken,
327
+ expiresAt: res.expiresAt,
328
+ rateLimitResetTime: 0,
329
+ isHealthy: true
330
+ };
331
+ try {
332
+ const u = await fetchUsageLimits({
333
+ refresh: encodeRefreshToken({
334
+ refreshToken: res.refreshToken,
335
+ clientId: res.clientId,
336
+ clientSecret: res.clientSecret,
337
+ authMethod: 'idc'
338
+ }),
339
+ access: res.accessToken,
340
+ expires: res.expiresAt,
341
+ authMethod: 'idc',
342
+ region,
343
+ clientId: res.clientId,
344
+ clientSecret: res.clientSecret,
345
+ email: res.email
346
+ });
347
+ am.updateUsage(acc.id, {
348
+ usedCount: u.usedCount,
349
+ limitCount: u.limitCount,
350
+ realEmail: u.email
351
+ });
352
+ }
353
+ catch { }
354
+ am.addAccount(acc);
355
+ await am.saveToDisk();
356
+ showToast(`Account authenticated (${res.email})`, 'success');
357
+ if (!(await promptAddAnotherAccount(am.getAccountCount())))
358
+ break;
381
359
  }
382
360
  catch (e) {
383
- logger.warn(`Initial usage fetch failed: ${e.message}`, e);
384
- }
385
- am.addAccount(acc);
386
- await am.saveToDisk();
387
- showToast(`Account ${accounts.length} authenticated${successResult.email ? ` (${successResult.email})` : ''}`, 'success');
388
- let currentAccountCount = accounts.length;
389
- try {
390
- const currentStorage = await AccountManager.loadFromDisk(config.account_selection_strategy);
391
- currentAccountCount = currentStorage.getAccountCount();
392
- }
393
- catch { }
394
- const addAnother = await promptAddAnotherAccount(currentAccountCount);
395
- if (!addAnother) {
361
+ showToast(`Failed: ${e.message}`, 'error');
396
362
  break;
397
363
  }
398
364
  }
399
- const primary = accounts[0];
400
- if (!primary) {
401
- return resolve({
402
- url: '',
403
- instructions: 'Authentication cancelled',
404
- method: 'auto',
405
- callback: async () => ({ type: 'failed' })
406
- });
407
- }
408
- let actualAccountCount = accounts.length;
409
- try {
410
- const finalStorage = await AccountManager.loadFromDisk(config.account_selection_strategy);
411
- actualAccountCount = finalStorage.getAccountCount();
412
- }
413
- catch { }
365
+ const finalAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
414
366
  return resolve({
415
367
  url: '',
416
- instructions: `Multi-account setup complete (${actualAccountCount} account(s)).`,
368
+ instructions: `Complete (${finalAm.getAccountCount()} accounts).`,
417
369
  method: 'auto',
418
- callback: async () => ({ type: 'success', key: primary.accessToken })
370
+ callback: async () => ({
371
+ type: 'success',
372
+ key: finalAm.getAccounts()[0]?.accessToken
373
+ })
419
374
  });
420
375
  }
421
376
  try {
@@ -424,12 +379,11 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
424
379
  openBrowser(url);
425
380
  resolve({
426
381
  url,
427
- instructions: `Open this URL to continue: ${url}`,
382
+ instructions: `Open: ${url}`,
428
383
  method: 'auto',
429
384
  callback: async () => {
430
385
  try {
431
- const res = await waitForAuth();
432
- const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
386
+ const res = await waitForAuth(), am = await AccountManager.loadFromDisk(config.account_selection_strategy);
433
387
  const acc = {
434
388
  id: generateAccountId(),
435
389
  email: res.email,
@@ -443,50 +397,20 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
443
397
  rateLimitResetTime: 0,
444
398
  isHealthy: true
445
399
  };
446
- try {
447
- const u = await fetchUsageLimits({
448
- refresh: encodeRefreshToken({
449
- refreshToken: res.refreshToken,
450
- clientId: res.clientId,
451
- clientSecret: res.clientSecret,
452
- authMethod: 'idc'
453
- }),
454
- access: res.accessToken,
455
- expires: res.expiresAt,
456
- authMethod: 'idc',
457
- region,
458
- clientId: res.clientId,
459
- clientSecret: res.clientSecret,
460
- email: res.email
461
- });
462
- am.updateUsage(acc.id, {
463
- usedCount: u.usedCount,
464
- limitCount: u.limitCount,
465
- realEmail: u.email
466
- });
467
- }
468
- catch (e) {
469
- logger.warn(`Initial usage fetch failed: ${e.message}`, e);
470
- }
471
400
  am.addAccount(acc);
472
401
  await am.saveToDisk();
473
- showToast(`Successfully logged in as ${res.email}`, 'success');
474
402
  return { type: 'success', key: res.accessToken };
475
403
  }
476
404
  catch (e) {
477
- logger.error(`Login failed: ${e.message}`, e);
478
- showToast(`Login failed: ${e.message}`, 'error');
479
405
  return { type: 'failed' };
480
406
  }
481
407
  }
482
408
  });
483
409
  }
484
410
  catch (e) {
485
- logger.error(`Authorization failed: ${e.message}`, e);
486
- showToast(`Authorization failed: ${e.message}`, 'error');
487
411
  resolve({
488
412
  url: '',
489
- instructions: 'Authorization failed',
413
+ instructions: 'Failed',
490
414
  method: 'auto',
491
415
  callback: async () => ({ type: 'failed' })
492
416
  });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@zhafron/opencode-kiro-auth",
3
- "version": "1.2.7",
3
+ "version": "1.3.0",
4
4
  "description": "OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to Claude models",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "scripts": {
9
9
  "build": "tsc -p tsconfig.build.json",
10
- "format": "prettier --write --no-config --no-semi --single-quote --trailing-comma none --print-width 100 'src/**/*.ts'",
10
+ "format": "prettier --write 'src/**/*.ts'",
11
11
  "typecheck": "tsc --noEmit"
12
12
  },
13
13
  "keywords": [
@@ -37,6 +37,7 @@
37
37
  "devDependencies": {
38
38
  "@types/node": "^20.0.0",
39
39
  "@types/proper-lockfile": "^4.1.4",
40
+ "bun-types": "^1.3.6",
40
41
  "prettier": "^3.4.2",
41
42
  "prettier-plugin-organize-imports": "^4.1.0",
42
43
  "typescript": "^5.7.3"
@@ -1,7 +0,0 @@
1
- import type { AccountStorage, UsageStorage } from './types';
2
- export declare function getStoragePath(): string;
3
- export declare function getUsagePath(): string;
4
- export declare function loadAccounts(): Promise<AccountStorage>;
5
- export declare function saveAccounts(storage: AccountStorage): Promise<void>;
6
- export declare function loadUsage(): Promise<UsageStorage>;
7
- export declare function saveUsage(storage: UsageStorage): Promise<void>;