@zhafron/opencode-kiro-auth 1.6.6 → 1.7.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.
@@ -39,6 +39,8 @@ export const RegionSchema = z.enum([
39
39
  export const KiroConfigSchema = z.object({
40
40
  $schema: z.string().optional(),
41
41
  idc_start_url: z.string().url().optional(),
42
+ idc_region: RegionSchema.optional(),
43
+ idc_profile_arn: z.string().optional(),
42
44
  account_selection_strategy: AccountSelectionStrategySchema.default('lowest-usage'),
43
45
  default_region: RegionSchema.default('us-east-1'),
44
46
  rate_limit_retry_delay_ms: z.number().min(1000).max(60000).default(5000),
@@ -14,7 +14,22 @@ const writeToFile = (level, message, ...args) => {
14
14
  mkdirSync(dir, { recursive: true });
15
15
  const path = join(dir, 'plugin.log');
16
16
  const timestamp = new Date().toISOString();
17
- const content = `[${timestamp}] ${level}: ${message} ${args.map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ')}\n`;
17
+ const content = `[${timestamp}] ${level}: ${message} ${args
18
+ .map((a) => {
19
+ if (a instanceof Error) {
20
+ return `${a.name}: ${a.message}${a.stack ? `\n${a.stack}` : ''}`;
21
+ }
22
+ if (typeof a === 'object') {
23
+ try {
24
+ return JSON.stringify(a);
25
+ }
26
+ catch {
27
+ return '[Unserializable object]';
28
+ }
29
+ }
30
+ return String(a);
31
+ })
32
+ .join(' ')}\n`;
18
33
  appendFileSync(path, content);
19
34
  }
20
35
  catch (e) { }
@@ -135,6 +135,8 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
135
135
  }
136
136
  }
137
137
  };
138
+ if (auth.profileArn)
139
+ request.profileArn = auth.profileArn;
138
140
  const toolUsesInHistory = history.flatMap((h) => h.assistantResponseMessage?.toolUses || []);
139
141
  const allToolUseIdsInHistory = new Set(toolUsesInHistory.map((tu) => tu.toolUseId));
140
142
  const finalCurTrs = [];
@@ -3,6 +3,7 @@ export function runMigrations(db) {
3
3
  migrateRealEmailColumn(db);
4
4
  migrateUsageTable(db);
5
5
  migrateStartUrlColumn(db);
6
+ migrateOidcRegionColumn(db);
6
7
  }
7
8
  function migrateToUniqueRefreshToken(db) {
8
9
  const hasIndex = db
@@ -62,7 +63,8 @@ function migrateRealEmailColumn(db) {
62
63
  db.run(`
63
64
  CREATE TABLE accounts_new (
64
65
  id TEXT PRIMARY KEY, email TEXT NOT NULL, auth_method TEXT NOT NULL,
65
- region TEXT NOT NULL, client_id TEXT, client_secret TEXT, profile_arn TEXT,
66
+ region TEXT NOT NULL, oidc_region TEXT, client_id TEXT, client_secret TEXT, profile_arn TEXT,
67
+ start_url TEXT,
66
68
  refresh_token TEXT NOT NULL, access_token TEXT NOT NULL, expires_at INTEGER NOT NULL,
67
69
  rate_limit_reset INTEGER DEFAULT 0, is_healthy INTEGER DEFAULT 1, unhealthy_reason TEXT,
68
70
  recovery_time INTEGER, fail_count INTEGER DEFAULT 0, last_used INTEGER DEFAULT 0,
@@ -70,8 +72,8 @@ function migrateRealEmailColumn(db) {
70
72
  )
71
73
  `);
72
74
  db.run(`
73
- INSERT INTO accounts_new (id, email, auth_method, region, client_id, client_secret, profile_arn, refresh_token, access_token, expires_at, rate_limit_reset, is_healthy, unhealthy_reason, recovery_time, fail_count, last_used, used_count, limit_count, last_sync)
74
- SELECT id, email, auth_method, region, client_id, client_secret, profile_arn, refresh_token, access_token, expires_at, COALESCE(rate_limit_reset, 0), COALESCE(is_healthy, 1), unhealthy_reason, recovery_time, COALESCE(fail_count, 0), COALESCE(last_used, 0), 0, 0, 0 FROM accounts
75
+ INSERT INTO accounts_new (id, email, auth_method, region, oidc_region, client_id, client_secret, profile_arn, start_url, refresh_token, access_token, expires_at, rate_limit_reset, is_healthy, unhealthy_reason, recovery_time, fail_count, last_used, used_count, limit_count, last_sync)
76
+ SELECT id, email, auth_method, region, NULL, client_id, client_secret, profile_arn, NULL, refresh_token, access_token, expires_at, COALESCE(rate_limit_reset, 0), COALESCE(is_healthy, 1), unhealthy_reason, recovery_time, COALESCE(fail_count, 0), COALESCE(last_used, 0), 0, 0, 0 FROM accounts
75
77
  `);
76
78
  db.run('DROP TABLE accounts');
77
79
  db.run('ALTER TABLE accounts_new RENAME TO accounts');
@@ -115,3 +117,12 @@ function migrateStartUrlColumn(db) {
115
117
  db.run('ALTER TABLE accounts ADD COLUMN start_url TEXT');
116
118
  }
117
119
  }
120
+ function migrateOidcRegionColumn(db) {
121
+ const columns = db.prepare('PRAGMA table_info(accounts)').all();
122
+ const names = new Set(columns.map((c) => c.name));
123
+ if (!names.has('oidc_region')) {
124
+ db.run('ALTER TABLE accounts ADD COLUMN oidc_region TEXT');
125
+ }
126
+ // Backfill: historically `region` was used for both service + OIDC.
127
+ db.run('UPDATE accounts SET oidc_region = region WHERE oidc_region IS NULL OR oidc_region = \"\"');
128
+ }
@@ -28,7 +28,7 @@ export class KiroDatabase {
28
28
  this.db.run(`
29
29
  CREATE TABLE IF NOT EXISTS accounts (
30
30
  id TEXT PRIMARY KEY, email TEXT NOT NULL, auth_method TEXT NOT NULL,
31
- region TEXT NOT NULL, client_id TEXT, client_secret TEXT, profile_arn TEXT,
31
+ region TEXT NOT NULL, oidc_region TEXT, client_id TEXT, client_secret TEXT, profile_arn TEXT,
32
32
  start_url TEXT,
33
33
  refresh_token TEXT NOT NULL, access_token TEXT NOT NULL, expires_at INTEGER NOT NULL,
34
34
  rate_limit_reset INTEGER DEFAULT 0, is_healthy INTEGER DEFAULT 1, unhealthy_reason TEXT,
@@ -45,14 +45,14 @@ export class KiroDatabase {
45
45
  this.db
46
46
  .prepare(`
47
47
  INSERT INTO accounts (
48
- id, email, auth_method, region, client_id, client_secret,
48
+ id, email, auth_method, region, oidc_region, client_id, client_secret,
49
49
  profile_arn, start_url, refresh_token, access_token, expires_at, rate_limit_reset,
50
50
  is_healthy, unhealthy_reason, recovery_time, fail_count, last_used,
51
51
  used_count, limit_count, last_sync
52
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
52
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
53
53
  ON CONFLICT(refresh_token) DO UPDATE SET
54
54
  id=excluded.id, email=excluded.email, auth_method=excluded.auth_method,
55
- region=excluded.region, client_id=excluded.client_id, client_secret=excluded.client_secret,
55
+ region=excluded.region, oidc_region=excluded.oidc_region, client_id=excluded.client_id, client_secret=excluded.client_secret,
56
56
  profile_arn=excluded.profile_arn, start_url=excluded.start_url,
57
57
  access_token=excluded.access_token, expires_at=excluded.expires_at,
58
58
  rate_limit_reset=excluded.rate_limit_reset, is_healthy=excluded.is_healthy,
@@ -60,7 +60,7 @@ export class KiroDatabase {
60
60
  fail_count=excluded.fail_count, last_used=excluded.last_used,
61
61
  used_count=excluded.used_count, limit_count=excluded.limit_count, last_sync=excluded.last_sync
62
62
  `)
63
- .run(acc.id, acc.email, acc.authMethod, acc.region, acc.clientId || null, acc.clientSecret || null, acc.profileArn || null, acc.startUrl || null, acc.refreshToken, acc.accessToken, acc.expiresAt, acc.rateLimitResetTime || 0, acc.isHealthy ? 1 : 0, acc.unhealthyReason || null, acc.recoveryTime || null, acc.failCount || 0, acc.lastUsed || 0, acc.usedCount || 0, acc.limitCount || 0, acc.lastSync || 0);
63
+ .run(acc.id, acc.email, acc.authMethod, acc.region, acc.oidcRegion || null, acc.clientId || null, acc.clientSecret || null, acc.profileArn || null, acc.startUrl || null, acc.refreshToken, acc.accessToken, acc.expiresAt, acc.rateLimitResetTime || 0, acc.isHealthy ? 1 : 0, acc.unhealthyReason || null, acc.recoveryTime || null, acc.failCount || 0, acc.lastUsed || 0, acc.usedCount || 0, acc.limitCount || 0, acc.lastSync || 0);
64
64
  }
65
65
  async upsertAccount(acc) {
66
66
  await withDatabaseLock(this.path, async () => {
@@ -109,6 +109,7 @@ export class KiroDatabase {
109
109
  email: row.email,
110
110
  authMethod: row.auth_method,
111
111
  region: row.region,
112
+ oidcRegion: row.oidc_region || undefined,
112
113
  clientId: row.client_id,
113
114
  clientSecret: row.client_secret,
114
115
  profileArn: row.profile_arn,
@@ -0,0 +1 @@
1
+ export declare function readActiveProfileArnFromKiroCli(): string | undefined;
@@ -0,0 +1,30 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { existsSync } from 'node:fs';
3
+ import { getCliDbPath, safeJsonParse } from './kiro-cli-parser';
4
+ export function readActiveProfileArnFromKiroCli() {
5
+ const dbPath = getCliDbPath();
6
+ if (!existsSync(dbPath))
7
+ return undefined;
8
+ let cliDb;
9
+ try {
10
+ cliDb = new Database(dbPath, { readonly: true });
11
+ cliDb.run('PRAGMA busy_timeout = 5000');
12
+ const row = cliDb
13
+ .prepare('SELECT value FROM state WHERE key = ?')
14
+ .get('api.codewhisperer.profile');
15
+ const parsed = safeJsonParse(row?.value);
16
+ const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
17
+ return typeof arn === 'string' && arn.trim() ? arn.trim() : undefined;
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ finally {
23
+ try {
24
+ cliDb?.close();
25
+ }
26
+ catch {
27
+ // ignore
28
+ }
29
+ }
30
+ }
@@ -1,10 +1,12 @@
1
1
  import { Database } from 'bun:sqlite';
2
2
  import { existsSync } from 'node:fs';
3
+ import { extractRegionFromArn, normalizeRegion } from '../../constants';
3
4
  import { createDeterministicAccountId } from '../accounts';
4
5
  import * as logger from '../logger';
5
6
  import { kiroDb } from '../storage/sqlite';
6
7
  import { fetchUsageLimits } from '../usage';
7
8
  import { findClientCredsRecursive, getCliDbPath, makePlaceholderEmail, normalizeExpiresAt, safeJsonParse } from './kiro-cli-parser';
9
+ import { readActiveProfileArnFromKiroCli } from './kiro-cli-profile';
8
10
  export async function syncFromKiroCli() {
9
11
  const dbPath = getCliDbPath();
10
12
  if (!existsSync(dbPath))
@@ -13,6 +15,19 @@ export async function syncFromKiroCli() {
13
15
  const cliDb = new Database(dbPath, { readonly: true });
14
16
  cliDb.run('PRAGMA busy_timeout = 5000');
15
17
  const rows = cliDb.prepare('SELECT key, value FROM auth_kv').all();
18
+ let activeProfileArn;
19
+ try {
20
+ const stateRow = cliDb
21
+ .prepare('SELECT value FROM state WHERE key = ?')
22
+ .get('api.codewhisperer.profile');
23
+ const parsed = safeJsonParse(stateRow?.value);
24
+ const arn = parsed?.arn || parsed?.profileArn || parsed?.profile_arn;
25
+ if (typeof arn === 'string' && arn.trim())
26
+ activeProfileArn = arn.trim();
27
+ }
28
+ catch {
29
+ // Ignore state read failures; token import can proceed.
30
+ }
16
31
  const deviceRegRow = rows.find((r) => typeof r?.key === 'string' && r.key.includes('device-registration'));
17
32
  const deviceReg = safeJsonParse(deviceRegRow?.value);
18
33
  const regCreds = deviceReg ? findClientCredsRecursive(deviceReg) : {};
@@ -23,8 +38,16 @@ export async function syncFromKiroCli() {
23
38
  continue;
24
39
  const isIdc = row.key.includes('odic');
25
40
  const authMethod = isIdc ? 'idc' : 'desktop';
26
- const region = data.region || 'us-east-1';
27
- const profileArn = data.profile_arn || data.profileArn;
41
+ const oidcRegion = normalizeRegion(data.region);
42
+ let profileArn = data.profile_arn || data.profileArn;
43
+ if (!profileArn && isIdc)
44
+ profileArn = activeProfileArn || readActiveProfileArnFromKiroCli();
45
+ const serviceRegion = extractRegionFromArn(profileArn) || oidcRegion;
46
+ const startUrl = typeof data.start_url === 'string'
47
+ ? data.start_url
48
+ : typeof data.startUrl === 'string'
49
+ ? data.startUrl
50
+ : undefined;
28
51
  const accessToken = data.access_token || data.accessToken || '';
29
52
  const refreshToken = data.refresh_token || data.refreshToken;
30
53
  if (!refreshToken)
@@ -46,7 +69,7 @@ export async function syncFromKiroCli() {
46
69
  access: accessToken,
47
70
  expires: cliExpiresAt,
48
71
  authMethod,
49
- region,
72
+ region: serviceRegion,
50
73
  profileArn,
51
74
  clientId,
52
75
  clientSecret,
@@ -63,7 +86,8 @@ export async function syncFromKiroCli() {
63
86
  catch (e) {
64
87
  logger.warn('Kiro CLI sync: failed to fetch usage/email; falling back', {
65
88
  authMethod,
66
- region
89
+ serviceRegion,
90
+ oidcRegion
67
91
  });
68
92
  logger.debug('Kiro CLI sync: usage fetch error', e);
69
93
  }
@@ -80,10 +104,10 @@ export async function syncFromKiroCli() {
80
104
  email = existing.email;
81
105
  }
82
106
  else {
83
- email = makePlaceholderEmail(authMethod, region, clientId, profileArn);
107
+ email = makePlaceholderEmail(authMethod, serviceRegion, clientId, profileArn);
84
108
  }
85
109
  }
86
- const resolvedEmail = email || makePlaceholderEmail(authMethod, region, clientId, profileArn);
110
+ const resolvedEmail = email || makePlaceholderEmail(authMethod, serviceRegion, clientId, profileArn);
87
111
  const id = createDeterministicAccountId(resolvedEmail, authMethod, clientId, profileArn);
88
112
  const existingById = all.find((a) => a.id === id);
89
113
  if (existingById &&
@@ -91,7 +115,7 @@ export async function syncFromKiroCli() {
91
115
  existingById.expires_at >= cliExpiresAt)
92
116
  continue;
93
117
  if (usageOk) {
94
- const placeholderEmail = makePlaceholderEmail(authMethod, region, clientId, profileArn);
118
+ const placeholderEmail = makePlaceholderEmail(authMethod, serviceRegion, clientId, profileArn);
95
119
  const placeholderId = createDeterministicAccountId(placeholderEmail, authMethod, clientId, profileArn);
96
120
  if (placeholderId !== id) {
97
121
  const placeholderRow = all.find((a) => a.id === placeholderId);
@@ -100,7 +124,8 @@ export async function syncFromKiroCli() {
100
124
  id: placeholderId,
101
125
  email: placeholderRow.email,
102
126
  authMethod,
103
- region: placeholderRow.region || region,
127
+ region: placeholderRow.region || serviceRegion,
128
+ oidcRegion: placeholderRow.oidc_region || oidcRegion,
104
129
  clientId,
105
130
  clientSecret,
106
131
  profileArn,
@@ -123,10 +148,12 @@ export async function syncFromKiroCli() {
123
148
  id,
124
149
  email: resolvedEmail,
125
150
  authMethod,
126
- region,
151
+ region: serviceRegion,
152
+ oidcRegion,
127
153
  clientId,
128
154
  clientSecret,
129
155
  profileArn,
156
+ startUrl,
130
157
  refreshToken,
131
158
  accessToken,
132
159
  expiresAt: cliExpiresAt,
@@ -3,8 +3,9 @@ import { KiroTokenRefreshError } from './errors';
3
3
  export async function refreshAccessToken(auth) {
4
4
  const p = decodeRefreshToken(auth.refresh);
5
5
  const isIdc = auth.authMethod === 'idc';
6
+ const oidcRegion = auth.oidcRegion || auth.region;
6
7
  const url = isIdc
7
- ? `https://oidc.${auth.region}.amazonaws.com/token`
8
+ ? `https://oidc.${oidcRegion}.amazonaws.com/token`
8
9
  : `https://prod.${auth.region}.auth.desktop.kiro.dev/refreshToken`;
9
10
  if (isIdc && (!p.clientId || !p.clientSecret)) {
10
11
  throw new KiroTokenRefreshError('Missing creds', 'MISSING_CREDENTIALS');
@@ -62,6 +63,8 @@ export async function refreshAccessToken(auth) {
62
63
  expires: Date.now() + (d.expires_in || d.expiresIn || 3600) * 1000,
63
64
  authMethod: auth.authMethod,
64
65
  region: auth.region,
66
+ oidcRegion: auth.oidcRegion,
67
+ profileArn: auth.profileArn,
65
68
  clientId: auth.clientId,
66
69
  clientSecret: auth.clientSecret,
67
70
  email: auth.email || d.userInfo?.email
@@ -8,6 +8,7 @@ export interface KiroAuthDetails {
8
8
  expires: number;
9
9
  authMethod: KiroAuthMethod;
10
10
  region: KiroRegion;
11
+ oidcRegion?: KiroRegion;
11
12
  clientId?: string;
12
13
  clientSecret?: string;
13
14
  email?: string;
@@ -25,6 +26,7 @@ export interface ManagedAccount {
25
26
  email: string;
26
27
  authMethod: KiroAuthMethod;
27
28
  region: KiroRegion;
29
+ oidcRegion?: KiroRegion;
28
30
  clientId?: string;
29
31
  clientSecret?: string;
30
32
  profileArn?: string;
@@ -1,7 +1,12 @@
1
1
  export async function fetchUsageLimits(auth) {
2
- const url = `https://q.${auth.region}.amazonaws.com/getUsageLimits?isEmailRequired=true&origin=AI_EDITOR&resourceType=AGENTIC_REQUEST`;
2
+ const url = new URL(`https://q.${auth.region}.amazonaws.com/getUsageLimits`);
3
+ url.searchParams.set('isEmailRequired', 'true');
4
+ url.searchParams.set('origin', 'AI_EDITOR');
5
+ url.searchParams.set('resourceType', 'AGENTIC_REQUEST');
6
+ if (auth.profileArn)
7
+ url.searchParams.set('profileArn', auth.profileArn);
3
8
  try {
4
- const res = await fetch(url, {
9
+ const res = await fetch(url.toString(), {
5
10
  method: 'GET',
6
11
  headers: {
7
12
  Authorization: `Bearer ${auth.access}`,
@@ -10,8 +15,18 @@ export async function fetchUsageLimits(auth) {
10
15
  'amz-sdk-request': 'attempt=1; max=1'
11
16
  }
12
17
  });
13
- if (!res.ok)
14
- throw new Error(`Status: ${res.status}`);
18
+ if (!res.ok) {
19
+ const body = await res.text().catch(() => '');
20
+ const requestId = res.headers.get('x-amzn-requestid') ||
21
+ res.headers.get('x-amzn-request-id') ||
22
+ res.headers.get('x-amz-request-id') ||
23
+ '';
24
+ const errType = res.headers.get('x-amzn-errortype') || res.headers.get('x-amzn-error-type') || '';
25
+ const msg = body && body.length > 0
26
+ ? `${body.slice(0, 2000)}${body.length > 2000 ? '…' : ''}`
27
+ : `HTTP ${res.status}`;
28
+ throw new Error(`Status: ${res.status}${errType ? ` (${errType})` : ''}${requestId ? ` [${requestId}]` : ''}: ${msg}`);
29
+ }
15
30
  const data = await res.json();
16
31
  let usedCount = 0, limitCount = 0;
17
32
  if (Array.isArray(data.usageBreakdownList)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhafron/opencode-kiro-auth",
3
- "version": "1.6.6",
3
+ "version": "1.7.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",
@@ -1,8 +0,0 @@
1
- export declare function promptAddAnotherAccount(currentCount: number): Promise<boolean>;
2
- export declare function promptDeleteAccount(accounts: ExistingAccountInfo[]): Promise<number[] | null>;
3
- export type LoginMode = 'add' | 'fresh' | 'delete';
4
- export interface ExistingAccountInfo {
5
- email?: string;
6
- index: number;
7
- }
8
- export declare function promptLoginMode(existingAccounts: ExistingAccountInfo[]): Promise<LoginMode>;
@@ -1,103 +0,0 @@
1
- import { stdin as input, stdout as output } from 'node:process';
2
- import { createInterface } from 'node:readline/promises';
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 promptDeleteAccount(accounts) {
15
- const rl = createInterface({ input, output });
16
- try {
17
- console.log(`\nSelect account(s) to delete:`);
18
- for (const acc of accounts) {
19
- const label = acc.email || `Account ${acc.index + 1}`;
20
- console.log(` ${acc.index + 1}. ${label}`);
21
- }
22
- console.log(` 0. Cancel`);
23
- console.log('');
24
- while (true) {
25
- const answer = await rl.question('Enter account number(s) (e.g., 1,2,3 or 1): ');
26
- const trimmed = answer.trim();
27
- if (trimmed === '0') {
28
- return null;
29
- }
30
- const parts = trimmed.split(',').map((s) => s.trim());
31
- const numbers = [];
32
- let invalid = false;
33
- for (const part of parts) {
34
- const num = parseInt(part, 10);
35
- if (isNaN(num) || num < 1 || num > accounts.length) {
36
- invalid = true;
37
- break;
38
- }
39
- if (!numbers.includes(num)) {
40
- numbers.push(num);
41
- }
42
- }
43
- if (invalid) {
44
- console.log(`Please enter valid numbers between 1 and ${accounts.length}, separated by commas`);
45
- continue;
46
- }
47
- if (numbers.length === 0) {
48
- console.log(`Please enter at least one account number`);
49
- continue;
50
- }
51
- const indices = numbers.map((n) => n - 1);
52
- const selectedAccounts = indices
53
- .map((i) => accounts[i])
54
- .filter((acc) => acc !== undefined);
55
- if (selectedAccounts.length === 0) {
56
- console.log(`No valid accounts selected`);
57
- continue;
58
- }
59
- console.log(`\nYou are about to delete ${selectedAccounts.length} account(s):`);
60
- for (const acc of selectedAccounts) {
61
- const label = acc.email || `Account ${acc.index + 1}`;
62
- console.log(` - ${label}`);
63
- }
64
- const confirm = await rl.question(`\nConfirm deletion? (y/n): `);
65
- const normalized = confirm.trim().toLowerCase();
66
- if (normalized === 'y' || normalized === 'yes') {
67
- return selectedAccounts.map((acc) => acc.index);
68
- }
69
- return null;
70
- }
71
- }
72
- finally {
73
- rl.close();
74
- }
75
- }
76
- export async function promptLoginMode(existingAccounts) {
77
- const rl = createInterface({ input, output });
78
- try {
79
- console.log(`\n${existingAccounts.length} account(s) saved:`);
80
- for (const acc of existingAccounts) {
81
- const label = acc.email || `Account ${acc.index + 1}`;
82
- console.log(` ${acc.index + 1}. ${label}`);
83
- }
84
- console.log('');
85
- while (true) {
86
- const answer = await rl.question('(a)dd new account(s), (f)resh start, or (d)elete account? [a/f/d]: ');
87
- const normalized = answer.trim().toLowerCase();
88
- if (normalized === 'a' || normalized === 'add') {
89
- return 'add';
90
- }
91
- if (normalized === 'f' || normalized === 'fresh') {
92
- return 'fresh';
93
- }
94
- if (normalized === 'd' || normalized === 'delete') {
95
- return 'delete';
96
- }
97
- console.log("Please enter 'a' to add accounts, 'f' to start fresh, or 'd' to delete account.");
98
- }
99
- }
100
- finally {
101
- rl.close();
102
- }
103
- }
@@ -1,34 +0,0 @@
1
- import type { KiroRegion } from './types';
2
- export interface KiroIDCTokenResult {
3
- email: string;
4
- accessToken: string;
5
- refreshToken: string;
6
- expiresAt: number;
7
- clientId: string;
8
- clientSecret: string;
9
- }
10
- export interface IDCAuthData {
11
- verificationUrl: string;
12
- verificationUriComplete: string;
13
- userCode: string;
14
- deviceCode: string;
15
- clientId: string;
16
- clientSecret: string;
17
- interval: number;
18
- expiresIn: number;
19
- region: KiroRegion;
20
- startUrl: string;
21
- }
22
- export declare function startIDCAuthServer(authData: IDCAuthData, startPort?: number, portRange?: number): Promise<{
23
- url: string;
24
- waitForAuth: () => Promise<KiroIDCTokenResult>;
25
- }>;
26
- /**
27
- * Starts a local auth server that first shows a Start URL input page.
28
- * After the user submits, it calls authorizeKiroIDC internally and transitions
29
- * to the verification code page — no need to call authorizeKiroIDC beforehand.
30
- */
31
- export declare function startIDCAuthServerWithInput(region: KiroRegion, defaultStartUrl: string | undefined, startPort?: number, portRange?: number): Promise<{
32
- url: string;
33
- waitForAuth: () => Promise<KiroIDCTokenResult>;
34
- }>;