@zhafron/opencode-kiro-auth 1.2.8 → 1.3.1

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,115 @@ 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;
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');
86
89
  }
87
90
  if (count > 1 && am.shouldShowToast())
88
91
  showToast(`Using ${acc.realEmail || acc.email} (${am.getAccounts().indexOf(acc) + 1}/${count})`, 'info');
89
92
  if (am.shouldShowUsageToast() &&
90
93
  acc.usedCount !== undefined &&
91
94
  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);
95
+ const p = acc.limitCount > 0 ? (acc.usedCount / acc.limitCount) * 100 : 0;
96
+ showToast(formatUsageMessage(acc.usedCount, acc.limitCount, acc.realEmail || acc.email), p >= 80 ? 'warning' : 'info');
95
97
  }
96
- let auth = am.toAuthDetails(acc);
98
+ const auth = am.toAuthDetails(acc);
97
99
  if (accessTokenExpired(auth, config.token_expiry_buffer_ms)) {
98
100
  try {
99
- logger.log(`Refreshing token for ${acc.realEmail || acc.email}`);
100
- auth = await refreshAccessToken(auth);
101
- am.updateFromAuth(acc, auth);
101
+ const newAuth = await refreshAccessToken(auth);
102
+ am.updateFromAuth(acc, newAuth);
102
103
  await am.saveToDisk();
103
104
  }
104
105
  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);
106
+ if (config.auto_sync_kiro_cli)
107
+ await syncFromKiroCli();
108
+ const refreshedAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
109
+ const stillAcc = refreshedAm.getAccounts().find((a) => a.id === acc.id);
110
+ if (stillAcc &&
111
+ !accessTokenExpired(refreshedAm.toAuthDetails(stillAcc), config.token_expiry_buffer_ms)) {
112
+ showToast('Credentials recovered from Kiro CLI sync.', 'info');
113
+ continue;
114
+ }
115
+ if (e instanceof KiroTokenRefreshError &&
116
+ (e.code === 'ExpiredTokenException' ||
117
+ e.code === 'InvalidTokenException' ||
118
+ e.code === 'HTTP_401' ||
119
+ e.code === 'HTTP_403')) {
120
+ am.markUnhealthy(acc, e.message);
109
121
  await am.saveToDisk();
110
122
  continue;
111
123
  }
112
124
  throw e;
113
125
  }
114
126
  }
115
- const prep = transformToCodeWhisperer(url, init?.body, model, auth, think, budget);
127
+ const prepRequest = (f) => transformToCodeWhisperer(url, init?.body, model, auth, think, budget, f);
128
+ let prep = prepRequest(reductionFactor);
116
129
  const apiTimestamp = config.enable_log_api_request ? logger.getTimestamp() : null;
117
- let parsedBody = null;
118
- if (prep.init.body && typeof prep.init.body === 'string') {
130
+ if (config.enable_log_api_request && apiTimestamp) {
131
+ let parsedBody = null;
119
132
  try {
120
- parsedBody = JSON.parse(prep.init.body);
121
- }
122
- catch (e) {
123
- parsedBody = prep.init.body;
133
+ parsedBody = prep.init.body ? JSON.parse(prep.init.body) : null;
124
134
  }
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);
135
+ catch { }
136
+ logger.logApiRequest({
137
+ url: prep.url,
138
+ method: prep.init.method,
139
+ headers: prep.init.headers,
140
+ body: parsedBody,
141
+ conversationId: prep.conversationId,
142
+ model: prep.effectiveModel,
143
+ email: acc.realEmail || acc.email
144
+ }, apiTimestamp);
137
145
  }
138
146
  try {
139
147
  const res = await fetch(prep.url, prep.init);
140
148
  if (config.enable_log_api_request && apiTimestamp) {
141
- const responseHeaders = {};
142
- res.headers.forEach((value, key) => {
143
- responseHeaders[key] = value;
149
+ const h = {};
150
+ res.headers.forEach((v, k) => {
151
+ h[k] = v;
144
152
  });
145
153
  logger.logApiResponse({
146
154
  status: res.status,
147
155
  statusText: res.statusText,
148
- headers: responseHeaders,
156
+ headers: h,
149
157
  conversationId: prep.conversationId,
150
158
  model: prep.effectiveModel
151
159
  }, apiTimestamp);
152
160
  }
153
161
  if (res.ok) {
154
162
  if (config.usage_tracking_enabled) {
155
- const syncUsage = async (attempt = 0) => {
163
+ const sync = async (att = 0) => {
156
164
  try {
157
165
  const u = await fetchUsageLimits(auth);
158
166
  updateAccountQuota(acc, u, am);
159
167
  await am.saveToDisk();
160
168
  }
161
169
  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);
170
+ if (att < config.usage_sync_max_retries) {
171
+ await sleep(1000 * Math.pow(2, att));
172
+ return sync(att + 1);
166
173
  }
167
- logger.warn(`Usage sync failed for ${acc.realEmail || acc.email} after ${attempt + 1} attempts: ${e.message}`);
168
174
  }
169
175
  };
170
- syncUsage().catch(() => { });
176
+ sync().catch(() => { });
171
177
  }
172
178
  if (prep.streaming) {
173
179
  const s = transformKiroStream(res, model, prep.conversationId);
@@ -184,9 +190,7 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
184
190
  }
185
191
  }), { headers: { 'Content-Type': 'text/event-stream' } });
186
192
  }
187
- const text = await res.text();
188
- const p = parseEventStream(text);
189
- const oai = {
193
+ const text = await res.text(), p = parseEventStream(text), oai = {
190
194
  id: prep.conversationId,
191
195
  object: 'chat.completion',
192
196
  created: Math.floor(Date.now() / 1000),
@@ -217,71 +221,70 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
217
221
  headers: { 'Content-Type': 'application/json' }
218
222
  });
219
223
  }
224
+ if (res.status === 400 && reductionFactor > 0.4) {
225
+ reductionFactor -= 0.2;
226
+ showToast(`Context too long. Retrying with ${Math.round(reductionFactor * 100)}%...`, 'warning');
227
+ prep = prepRequest(reductionFactor);
228
+ continue;
229
+ }
220
230
  if (res.status === 401 && retry < config.rate_limit_max_retries) {
221
231
  retry++;
222
232
  continue;
223
233
  }
224
234
  if (res.status === 429) {
225
- const wait = parseInt(res.headers.get('retry-after') || '60') * 1000;
226
- am.markRateLimited(acc, wait);
235
+ const w = parseInt(res.headers.get('retry-after') || '60') * 1000;
236
+ am.markRateLimited(acc, w);
227
237
  await am.saveToDisk();
228
238
  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);
239
+ showToast(`Rate limited. Switching account...`, 'warning');
235
240
  continue;
236
241
  }
242
+ showToast(`Rate limited. Waiting ${Math.ceil(w / 1000)}s...`, 'warning');
243
+ await sleep(w);
244
+ continue;
237
245
  }
238
246
  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
247
  am.markUnhealthy(acc, res.status === 402 ? 'Quota' : 'Forbidden');
241
248
  await am.saveToDisk();
242
249
  continue;
243
250
  }
244
- const responseHeaders = {};
245
- res.headers.forEach((value, key) => {
246
- responseHeaders[key] = value;
251
+ const h = {};
252
+ res.headers.forEach((v, k) => {
253
+ h[k] = v;
247
254
  });
248
- const responseData = {
255
+ const rData = {
249
256
  status: res.status,
250
257
  statusText: res.statusText,
251
- headers: responseHeaders,
258
+ headers: h,
252
259
  error: `Kiro Error: ${res.status}`,
253
260
  conversationId: prep.conversationId,
254
261
  model: prep.effectiveModel
255
262
  };
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);
263
+ let lastBody = null;
264
+ try {
265
+ lastBody = prep.init.body ? JSON.parse(prep.init.body) : null;
262
266
  }
267
+ catch { }
268
+ if (!config.enable_log_api_request)
269
+ logger.logApiError({
270
+ url: prep.url,
271
+ method: prep.init.method,
272
+ headers: prep.init.headers,
273
+ body: lastBody,
274
+ conversationId: prep.conversationId,
275
+ model: prep.effectiveModel,
276
+ email: acc.realEmail || acc.email
277
+ }, rData, logger.getTimestamp());
263
278
  throw new Error(`Kiro Error: ${res.status}`);
264
279
  }
265
280
  catch (e) {
266
281
  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);
282
+ const d = 5000 * Math.pow(2, retry);
283
+ showToast(`Network error. Retrying in ${Math.ceil(d / 1000)}s...`, 'warning');
284
+ await sleep(d);
270
285
  retry++;
271
286
  continue;
272
287
  }
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
288
  throw e;
286
289
  }
287
290
  }
@@ -299,123 +302,82 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
299
302
  const accounts = [];
300
303
  let startFresh = true;
301
304
  const existingAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
302
- if (existingAm.getAccountCount() > 0) {
303
- const existingAccounts = existingAm.getAccounts().map((acc, idx) => ({
305
+ const idcAccs = existingAm.getAccounts().filter((a) => a.authMethod === 'idc');
306
+ if (idcAccs.length > 0) {
307
+ const existingAccounts = idcAccs.map((acc, idx) => ({
304
308
  email: acc.realEmail || acc.email,
305
309
  index: idx
306
310
  }));
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');
311
+ startFresh = (await promptLoginMode(existingAccounts)) === 'fresh';
312
312
  }
313
313
  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
314
  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,
315
+ const authData = await authorizeKiroIDC(region);
316
+ const { url, waitForAuth } = await startIDCAuthServer(authData, config.auth_server_port_start, config.auth_server_port_range);
317
+ openBrowser(url);
318
+ const res = await waitForAuth();
319
+ accounts.push(res);
320
+ const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
321
+ if (accounts.length === 1 && startFresh)
322
+ am.getAccounts()
323
+ .filter((a) => a.authMethod === 'idc')
324
+ .forEach((a) => am.removeAccount(a));
325
+ const acc = {
326
+ id: generateAccountId(),
327
+ email: res.email,
370
328
  authMethod: 'idc',
371
329
  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
- });
330
+ clientId: res.clientId,
331
+ clientSecret: res.clientSecret,
332
+ refreshToken: res.refreshToken,
333
+ accessToken: res.accessToken,
334
+ expiresAt: res.expiresAt,
335
+ rateLimitResetTime: 0,
336
+ isHealthy: true
337
+ };
338
+ try {
339
+ const u = await fetchUsageLimits({
340
+ refresh: encodeRefreshToken({
341
+ refreshToken: res.refreshToken,
342
+ clientId: res.clientId,
343
+ clientSecret: res.clientSecret,
344
+ authMethod: 'idc'
345
+ }),
346
+ access: res.accessToken,
347
+ expires: res.expiresAt,
348
+ authMethod: 'idc',
349
+ region,
350
+ clientId: res.clientId,
351
+ clientSecret: res.clientSecret,
352
+ email: res.email
353
+ });
354
+ am.updateUsage(acc.id, {
355
+ usedCount: u.usedCount,
356
+ limitCount: u.limitCount,
357
+ realEmail: u.email
358
+ });
359
+ }
360
+ catch { }
361
+ am.addAccount(acc);
362
+ await am.saveToDisk();
363
+ showToast(`Account authenticated (${res.email})`, 'success');
364
+ if (!(await promptAddAnotherAccount(am.getAccountCount())))
365
+ break;
381
366
  }
382
367
  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) {
368
+ showToast(`Failed: ${e.message}`, 'error');
396
369
  break;
397
370
  }
398
371
  }
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 { }
372
+ const finalAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
414
373
  return resolve({
415
374
  url: '',
416
- instructions: `Multi-account setup complete (${actualAccountCount} account(s)).`,
375
+ instructions: `Complete (${finalAm.getAccountCount()} accounts).`,
417
376
  method: 'auto',
418
- callback: async () => ({ type: 'success', key: primary.accessToken })
377
+ callback: async () => ({
378
+ type: 'success',
379
+ key: finalAm.getAccounts()[0]?.accessToken
380
+ })
419
381
  });
420
382
  }
421
383
  try {
@@ -424,12 +386,11 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
424
386
  openBrowser(url);
425
387
  resolve({
426
388
  url,
427
- instructions: `Open this URL to continue: ${url}`,
389
+ instructions: `Open: ${url}`,
428
390
  method: 'auto',
429
391
  callback: async () => {
430
392
  try {
431
- const res = await waitForAuth();
432
- const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
393
+ const res = await waitForAuth(), am = await AccountManager.loadFromDisk(config.account_selection_strategy);
433
394
  const acc = {
434
395
  id: generateAccountId(),
435
396
  email: res.email,
@@ -443,50 +404,20 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
443
404
  rateLimitResetTime: 0,
444
405
  isHealthy: true
445
406
  };
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
407
  am.addAccount(acc);
472
408
  await am.saveToDisk();
473
- showToast(`Successfully logged in as ${res.email}`, 'success');
474
409
  return { type: 'success', key: res.accessToken };
475
410
  }
476
411
  catch (e) {
477
- logger.error(`Login failed: ${e.message}`, e);
478
- showToast(`Login failed: ${e.message}`, 'error');
479
412
  return { type: 'failed' };
480
413
  }
481
414
  }
482
415
  });
483
416
  }
484
417
  catch (e) {
485
- logger.error(`Authorization failed: ${e.message}`, e);
486
- showToast(`Authorization failed: ${e.message}`, 'error');
487
418
  resolve({
488
419
  url: '',
489
- instructions: 'Authorization failed',
420
+ instructions: 'Failed',
490
421
  method: 'auto',
491
422
  callback: async () => ({ type: 'failed' })
492
423
  });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@zhafron/opencode-kiro-auth",
3
- "version": "1.2.8",
3
+ "version": "1.3.1",
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>;