@zhafron/opencode-kiro-auth 1.2.4 → 1.2.6
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/README.md +3 -18
- package/dist/constants.js +0 -3
- package/dist/plugin/cli.d.ts +7 -0
- package/dist/plugin/cli.js +38 -0
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.js +126 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@zhafron/opencode-kiro-auth)
|
|
4
4
|
[](https://www.npmjs.com/package/@zhafron/opencode-kiro-auth)
|
|
5
5
|
|
|
6
|
-
OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to
|
|
6
|
+
OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to Claude Sonnet and Haiku models with substantial trial quotas.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
@@ -26,30 +26,15 @@ Add the plugin to your `opencode.json` or `opencode.jsonc`:
|
|
|
26
26
|
"provider": {
|
|
27
27
|
"kiro": {
|
|
28
28
|
"models": {
|
|
29
|
-
"claude-opus-4-5": {
|
|
30
|
-
"name": "Claude Opus 4.5",
|
|
31
|
-
"limit": { "context": 200000, "output": 64000 },
|
|
32
|
-
"modalities": { "input": ["text", "image"], "output": ["text"] }
|
|
33
|
-
},
|
|
34
|
-
"claude-opus-4-5-thinking": {
|
|
35
|
-
"name": "Claude Opus 4.5 Thinking",
|
|
36
|
-
"limit": { "context": 200000, "output": 64000 },
|
|
37
|
-
"modalities": { "input": ["text", "image"], "output": ["text"] },
|
|
38
|
-
"variants": {
|
|
39
|
-
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
|
40
|
-
"medium": { "thinkingConfig": { "thinkingBudget": 16384 } },
|
|
41
|
-
"max": { "thinkingConfig": { "thinkingBudget": 32768 } }
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
29
|
"claude-sonnet-4-5": {
|
|
45
30
|
"name": "Claude Sonnet 4.5",
|
|
46
31
|
"limit": { "context": 200000, "output": 64000 },
|
|
47
|
-
"modalities": { "input": ["text", "image"], "output": ["text"] }
|
|
32
|
+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] }
|
|
48
33
|
},
|
|
49
34
|
"claude-sonnet-4-5-thinking": {
|
|
50
35
|
"name": "Claude Sonnet 4.5 Thinking",
|
|
51
36
|
"limit": { "context": 200000, "output": 64000 },
|
|
52
|
-
"modalities": { "input": ["text", "image"], "output": ["text"] },
|
|
37
|
+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
|
53
38
|
"variants": {
|
|
54
39
|
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
|
55
40
|
"medium": { "thinkingConfig": { "thinkingBudget": 16384 } },
|
package/dist/constants.js
CHANGED
|
@@ -40,9 +40,6 @@ export const KIRO_CONSTANTS = {
|
|
|
40
40
|
ORIGIN_AI_EDITOR: 'AI_EDITOR'
|
|
41
41
|
};
|
|
42
42
|
export const MODEL_MAPPING = {
|
|
43
|
-
'claude-opus-4-5': 'claude-opus-4.5',
|
|
44
|
-
'claude-opus-4-5-thinking': 'claude-opus-4.5',
|
|
45
|
-
'claude-opus-4-5-20251101': 'claude-opus-4.5',
|
|
46
43
|
'claude-haiku-4-5': 'claude-haiku-4.5',
|
|
47
44
|
'claude-sonnet-4-5': 'CLAUDE_SONNET_4_5_20250929_V1_0',
|
|
48
45
|
'claude-sonnet-4-5-thinking': 'CLAUDE_SONNET_4_5_20250929_V1_0',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function promptAddAnotherAccount(currentCount: number): Promise<boolean>;
|
|
2
|
+
export type LoginMode = 'add' | 'fresh';
|
|
3
|
+
export interface ExistingAccountInfo {
|
|
4
|
+
email?: string;
|
|
5
|
+
index: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function promptLoginMode(existingAccounts: ExistingAccountInfo[]): Promise<LoginMode>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
+
export async function promptAddAnotherAccount(currentCount) {
|
|
4
|
+
const rl = createInterface({ input, output });
|
|
5
|
+
try {
|
|
6
|
+
const answer = await rl.question(`Add another account? (${currentCount} added) (y/n): `);
|
|
7
|
+
const normalized = answer.trim().toLowerCase();
|
|
8
|
+
return normalized === 'y' || normalized === 'yes';
|
|
9
|
+
}
|
|
10
|
+
finally {
|
|
11
|
+
rl.close();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function promptLoginMode(existingAccounts) {
|
|
15
|
+
const rl = createInterface({ input, output });
|
|
16
|
+
try {
|
|
17
|
+
console.log(`\n${existingAccounts.length} account(s) saved:`);
|
|
18
|
+
for (const acc of existingAccounts) {
|
|
19
|
+
const label = acc.email || `Account ${acc.index + 1}`;
|
|
20
|
+
console.log(` ${acc.index + 1}. ${label}`);
|
|
21
|
+
}
|
|
22
|
+
console.log('');
|
|
23
|
+
while (true) {
|
|
24
|
+
const answer = await rl.question('(a)dd new account(s) or (f)resh start? [a/f]: ');
|
|
25
|
+
const normalized = answer.trim().toLowerCase();
|
|
26
|
+
if (normalized === 'a' || normalized === 'add') {
|
|
27
|
+
return 'add';
|
|
28
|
+
}
|
|
29
|
+
if (normalized === 'f' || normalized === 'fresh') {
|
|
30
|
+
return 'fresh';
|
|
31
|
+
}
|
|
32
|
+
console.log("Please enter 'a' to add accounts or 'f' to start fresh.");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
rl.close();
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const createKiroPlugin: (id: string) => ({ client, directory }: a
|
|
|
10
10
|
id: string;
|
|
11
11
|
label: string;
|
|
12
12
|
type: string;
|
|
13
|
-
authorize: () => Promise<unknown>;
|
|
13
|
+
authorize: (inputs?: any) => Promise<unknown>;
|
|
14
14
|
}[];
|
|
15
15
|
};
|
|
16
16
|
}>;
|
|
@@ -26,7 +26,7 @@ export declare const KiroOAuthPlugin: ({ client, directory }: any) => Promise<{
|
|
|
26
26
|
id: string;
|
|
27
27
|
label: string;
|
|
28
28
|
type: string;
|
|
29
|
-
authorize: () => Promise<unknown>;
|
|
29
|
+
authorize: (inputs?: any) => Promise<unknown>;
|
|
30
30
|
}[];
|
|
31
31
|
};
|
|
32
32
|
}>;
|
package/dist/plugin.js
CHANGED
|
@@ -10,6 +10,7 @@ import { fetchUsageLimits, updateAccountQuota } from './plugin/usage';
|
|
|
10
10
|
import { authorizeKiroIDC } from './kiro/oauth-idc';
|
|
11
11
|
import { startIDCAuthServer } from './plugin/server';
|
|
12
12
|
import { KiroTokenRefreshError } from './plugin/errors';
|
|
13
|
+
import { promptAddAnotherAccount, promptLoginMode } from './plugin/cli';
|
|
13
14
|
import { KIRO_CONSTANTS } from './constants';
|
|
14
15
|
import * as logger from './plugin/logger';
|
|
15
16
|
const KIRO_PROVIDER_ID = 'kiro';
|
|
@@ -56,7 +57,7 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
|
|
|
56
57
|
if (!KIRO_API_PATTERN.test(url))
|
|
57
58
|
return fetch(input, init);
|
|
58
59
|
const body = init?.body ? JSON.parse(init.body) : {};
|
|
59
|
-
const model = extractModel(url) || body.model || 'claude-
|
|
60
|
+
const model = extractModel(url) || body.model || 'claude-sonnet-4-5';
|
|
60
61
|
const think = model.endsWith('-thinking') || !!body.providerOptions?.thinkingConfig;
|
|
61
62
|
const budget = body.providerOptions?.thinkingConfig?.thinkingBudget || 20000;
|
|
62
63
|
let retry = 0;
|
|
@@ -292,8 +293,131 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
|
|
|
292
293
|
id: 'idc',
|
|
293
294
|
label: 'AWS Builder ID (IDC)',
|
|
294
295
|
type: 'oauth',
|
|
295
|
-
authorize: async () => new Promise(async (resolve) => {
|
|
296
|
+
authorize: async (inputs) => new Promise(async (resolve) => {
|
|
296
297
|
const region = config.default_region;
|
|
298
|
+
if (inputs) {
|
|
299
|
+
const accounts = [];
|
|
300
|
+
let startFresh = true;
|
|
301
|
+
const existingAm = await AccountManager.loadFromDisk(config.account_selection_strategy);
|
|
302
|
+
if (existingAm.getAccountCount() > 0) {
|
|
303
|
+
const existingAccounts = existingAm.getAccounts().map((acc, idx) => ({
|
|
304
|
+
email: acc.realEmail || acc.email,
|
|
305
|
+
index: idx
|
|
306
|
+
}));
|
|
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');
|
|
312
|
+
}
|
|
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
|
+
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,
|
|
370
|
+
authMethod: 'idc',
|
|
371
|
+
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
|
+
});
|
|
381
|
+
}
|
|
382
|
+
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) {
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
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 { }
|
|
414
|
+
return resolve({
|
|
415
|
+
url: '',
|
|
416
|
+
instructions: `Multi-account setup complete (${actualAccountCount} account(s)).`,
|
|
417
|
+
method: 'auto',
|
|
418
|
+
callback: async () => ({ type: 'success', key: primary.accessToken })
|
|
419
|
+
});
|
|
420
|
+
}
|
|
297
421
|
try {
|
|
298
422
|
const authData = await authorizeKiroIDC(region);
|
|
299
423
|
const { url, waitForAuth } = await startIDCAuthServer(authData, config.auth_server_port_start, config.auth_server_port_range);
|