@zhafron/opencode-kiro-auth 1.0.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.
Files changed (110) hide show
  1. package/README.md +85 -0
  2. package/dist/constants.d.ts +26 -0
  3. package/dist/constants.js +60 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.js +1 -0
  6. package/dist/kiro/auth.d.ts +5 -0
  7. package/dist/kiro/auth.js +24 -0
  8. package/dist/kiro/oauth-idc.d.ts +24 -0
  9. package/dist/kiro/oauth-idc.js +132 -0
  10. package/dist/kiro/oauth-social.d.ts +17 -0
  11. package/dist/kiro/oauth-social.js +51 -0
  12. package/dist/plugin/accounts.d.ts +28 -0
  13. package/dist/plugin/accounts.js +156 -0
  14. package/dist/plugin/auth-page.d.ts +3 -0
  15. package/dist/plugin/auth-page.js +568 -0
  16. package/dist/plugin/cli.d.ts +6 -0
  17. package/dist/plugin/cli.js +98 -0
  18. package/dist/plugin/config/index.d.ts +3 -0
  19. package/dist/plugin/config/index.js +2 -0
  20. package/dist/plugin/config/loader.d.ts +6 -0
  21. package/dist/plugin/config/loader.js +125 -0
  22. package/dist/plugin/config/schema.d.ts +44 -0
  23. package/dist/plugin/config/schema.js +28 -0
  24. package/dist/plugin/debug.d.ts +2 -0
  25. package/dist/plugin/debug.js +9 -0
  26. package/dist/plugin/errors.d.ts +17 -0
  27. package/dist/plugin/errors.js +34 -0
  28. package/dist/plugin/logger.d.ts +4 -0
  29. package/dist/plugin/logger.js +37 -0
  30. package/dist/plugin/models.d.ts +3 -0
  31. package/dist/plugin/models.js +14 -0
  32. package/dist/plugin/oauth-parser.d.ts +5 -0
  33. package/dist/plugin/oauth-parser.js +23 -0
  34. package/dist/plugin/quota.d.ts +15 -0
  35. package/dist/plugin/quota.js +68 -0
  36. package/dist/plugin/recovery.d.ts +19 -0
  37. package/dist/plugin/recovery.js +302 -0
  38. package/dist/plugin/refresh-queue.d.ts +14 -0
  39. package/dist/plugin/refresh-queue.js +69 -0
  40. package/dist/plugin/request.d.ts +4 -0
  41. package/dist/plugin/request.js +240 -0
  42. package/dist/plugin/response.d.ts +6 -0
  43. package/dist/plugin/response.js +246 -0
  44. package/dist/plugin/server.d.ts +24 -0
  45. package/dist/plugin/server.js +96 -0
  46. package/dist/plugin/storage.d.ts +7 -0
  47. package/dist/plugin/storage.js +75 -0
  48. package/dist/plugin/streaming.d.ts +3 -0
  49. package/dist/plugin/streaming.js +503 -0
  50. package/dist/plugin/token.d.ts +2 -0
  51. package/dist/plugin/token.js +56 -0
  52. package/dist/plugin/types.d.ts +148 -0
  53. package/dist/plugin/types.js +0 -0
  54. package/dist/plugin/usage.d.ts +3 -0
  55. package/dist/plugin/usage.js +36 -0
  56. package/dist/plugin.d.ts +32 -0
  57. package/dist/plugin.js +222 -0
  58. package/dist/src/constants.d.ts +22 -0
  59. package/dist/src/constants.js +35 -0
  60. package/dist/src/kiro/auth.d.ts +5 -0
  61. package/dist/src/kiro/auth.js +69 -0
  62. package/dist/src/kiro/oauth-idc.d.ts +22 -0
  63. package/dist/src/kiro/oauth-idc.js +99 -0
  64. package/dist/src/kiro/oauth-social.d.ts +17 -0
  65. package/dist/src/kiro/oauth-social.js +69 -0
  66. package/dist/src/plugin/accounts.d.ts +23 -0
  67. package/dist/src/plugin/accounts.js +265 -0
  68. package/dist/src/plugin/cli.d.ts +6 -0
  69. package/dist/src/plugin/cli.js +98 -0
  70. package/dist/src/plugin/config/index.d.ts +3 -0
  71. package/dist/src/plugin/config/index.js +2 -0
  72. package/dist/src/plugin/config/loader.d.ts +7 -0
  73. package/dist/src/plugin/config/loader.js +143 -0
  74. package/dist/src/plugin/config/schema.d.ts +68 -0
  75. package/dist/src/plugin/config/schema.js +44 -0
  76. package/dist/src/plugin/debug.d.ts +2 -0
  77. package/dist/src/plugin/debug.js +9 -0
  78. package/dist/src/plugin/errors.d.ts +17 -0
  79. package/dist/src/plugin/errors.js +34 -0
  80. package/dist/src/plugin/logger.d.ts +4 -0
  81. package/dist/src/plugin/logger.js +17 -0
  82. package/dist/src/plugin/models.d.ts +3 -0
  83. package/dist/src/plugin/models.js +14 -0
  84. package/dist/src/plugin/oauth-parser.d.ts +5 -0
  85. package/dist/src/plugin/oauth-parser.js +23 -0
  86. package/dist/src/plugin/quota.d.ts +25 -0
  87. package/dist/src/plugin/quota.js +175 -0
  88. package/dist/src/plugin/recovery.d.ts +19 -0
  89. package/dist/src/plugin/recovery.js +302 -0
  90. package/dist/src/plugin/refresh-queue.d.ts +14 -0
  91. package/dist/src/plugin/refresh-queue.js +69 -0
  92. package/dist/src/plugin/request.d.ts +35 -0
  93. package/dist/src/plugin/request.js +411 -0
  94. package/dist/src/plugin/response.d.ts +6 -0
  95. package/dist/src/plugin/response.js +246 -0
  96. package/dist/src/plugin/server.d.ts +10 -0
  97. package/dist/src/plugin/server.js +203 -0
  98. package/dist/src/plugin/storage.d.ts +5 -0
  99. package/dist/src/plugin/storage.js +106 -0
  100. package/dist/src/plugin/streaming.d.ts +12 -0
  101. package/dist/src/plugin/streaming.js +444 -0
  102. package/dist/src/plugin/token.d.ts +8 -0
  103. package/dist/src/plugin/token.js +130 -0
  104. package/dist/src/plugin/types.d.ts +144 -0
  105. package/dist/src/plugin/types.js +0 -0
  106. package/dist/src/plugin/usage.d.ts +28 -0
  107. package/dist/src/plugin/usage.js +159 -0
  108. package/dist/src/plugin.d.ts +2 -0
  109. package/dist/src/plugin.js +341 -0
  110. package/package.json +57 -0
@@ -0,0 +1,148 @@
1
+ export type KiroAuthMethod = 'idc';
2
+ export type KiroRegion = 'us-east-1' | 'us-west-2';
3
+ export interface KiroAuthDetails {
4
+ refresh: string;
5
+ access: string;
6
+ expires: number;
7
+ authMethod: KiroAuthMethod;
8
+ region: KiroRegion;
9
+ clientId?: string;
10
+ clientSecret?: string;
11
+ email?: string;
12
+ profileArn?: string;
13
+ }
14
+ export interface RefreshParts {
15
+ refreshToken: string;
16
+ clientId?: string;
17
+ clientSecret?: string;
18
+ profileArn?: string;
19
+ authMethod?: KiroAuthMethod;
20
+ }
21
+ export interface ManagedAccount {
22
+ id: string;
23
+ email: string;
24
+ realEmail?: string;
25
+ authMethod: KiroAuthMethod;
26
+ region: KiroRegion;
27
+ clientId?: string;
28
+ clientSecret?: string;
29
+ profileArn?: string;
30
+ refreshToken: string;
31
+ accessToken: string;
32
+ expiresAt: number;
33
+ rateLimitResetTime: number;
34
+ isHealthy: boolean;
35
+ unhealthyReason?: string;
36
+ recoveryTime?: number;
37
+ usedCount?: number;
38
+ limitCount?: number;
39
+ lastUsed?: number;
40
+ }
41
+ export interface AccountMetadata {
42
+ id: string;
43
+ email: string;
44
+ realEmail?: string;
45
+ authMethod: KiroAuthMethod;
46
+ region: KiroRegion;
47
+ clientId?: string;
48
+ clientSecret?: string;
49
+ profileArn?: string;
50
+ refreshToken: string;
51
+ accessToken: string;
52
+ expiresAt: number;
53
+ rateLimitResetTime: number;
54
+ isHealthy: boolean;
55
+ unhealthyReason?: string;
56
+ recoveryTime?: number;
57
+ }
58
+ export interface AccountStorage {
59
+ version: 1;
60
+ accounts: AccountMetadata[];
61
+ activeIndex: number;
62
+ }
63
+ export interface UsageMetadata {
64
+ usedCount: number;
65
+ limitCount: number;
66
+ realEmail?: string;
67
+ lastSync: number;
68
+ }
69
+ export interface UsageStorage {
70
+ version: 1;
71
+ usage: Record<string, UsageMetadata>;
72
+ }
73
+ export interface CodeWhispererMessage {
74
+ userInputMessage?: {
75
+ content: string;
76
+ modelId: string;
77
+ origin: string;
78
+ images?: Array<{
79
+ format: string;
80
+ source: {
81
+ bytes: string;
82
+ };
83
+ }>;
84
+ userInputMessageContext?: {
85
+ toolResults?: Array<{
86
+ toolUseId: string;
87
+ content: Array<{
88
+ text?: string;
89
+ }>;
90
+ status?: string;
91
+ }>;
92
+ tools?: Array<{
93
+ toolSpecification: {
94
+ name: string;
95
+ description: string;
96
+ inputSchema: {
97
+ json: Record<string, unknown>;
98
+ };
99
+ };
100
+ }>;
101
+ };
102
+ };
103
+ assistantResponseMessage?: {
104
+ content: string;
105
+ toolUses?: Array<{
106
+ input: any;
107
+ name: string;
108
+ toolUseId: string;
109
+ }>;
110
+ };
111
+ }
112
+ export interface CodeWhispererRequest {
113
+ conversationState: {
114
+ chatTriggerType: string;
115
+ conversationId: string;
116
+ history: CodeWhispererMessage[];
117
+ currentMessage: CodeWhispererMessage;
118
+ };
119
+ profileArn?: string;
120
+ }
121
+ export interface ToolCall {
122
+ toolUseId: string;
123
+ name: string;
124
+ input: string | Record<string, unknown>;
125
+ }
126
+ export interface ParsedResponse {
127
+ content: string;
128
+ toolCalls: ToolCall[];
129
+ stopReason?: string;
130
+ inputTokens?: number;
131
+ outputTokens?: number;
132
+ }
133
+ export interface PreparedRequest {
134
+ url: string;
135
+ init: RequestInit;
136
+ streaming: boolean;
137
+ effectiveModel: string;
138
+ conversationId: string;
139
+ }
140
+ export type AccountSelectionStrategy = 'sticky' | 'round-robin' | 'lowest-usage';
141
+ export interface StreamEvent {
142
+ type: string;
143
+ message?: any;
144
+ content_block?: any;
145
+ delta?: any;
146
+ index?: number;
147
+ usage?: any;
148
+ }
File without changes
@@ -0,0 +1,3 @@
1
+ import { ManagedAccount, KiroAuthDetails } from './types';
2
+ export declare function fetchUsageLimits(auth: KiroAuthDetails): Promise<any>;
3
+ export declare function updateAccountQuota(account: ManagedAccount, usage: any, accountManager?: any): void;
@@ -0,0 +1,36 @@
1
+ export async function fetchUsageLimits(auth) {
2
+ const url = `https://q.${auth.region}.amazonaws.com/getUsageLimits?isEmailRequired=true&origin=AI_EDITOR&resourceType=AGENTIC_REQUEST`;
3
+ try {
4
+ const res = await fetch(url, {
5
+ method: 'GET',
6
+ headers: { Authorization: `Bearer ${auth.access}`, 'Content-Type': 'application/json', 'x-amzn-kiro-agent-mode': 'vibe', 'amz-sdk-request': 'attempt=1; max=1' }
7
+ });
8
+ if (!res.ok)
9
+ throw new Error(`Status: ${res.status}`);
10
+ const data = await res.json();
11
+ let usedCount = 0, limitCount = 0;
12
+ if (Array.isArray(data.usageBreakdownList)) {
13
+ for (const s of data.usageBreakdownList) {
14
+ if (s.freeTrialInfo) {
15
+ usedCount += s.freeTrialInfo.currentUsage || 0;
16
+ limitCount += s.freeTrialInfo.usageLimit || 0;
17
+ }
18
+ usedCount += s.currentUsage || 0;
19
+ limitCount += s.usageLimit || 0;
20
+ }
21
+ }
22
+ return { usedCount, limitCount, email: data.userInfo?.email };
23
+ }
24
+ catch (e) {
25
+ throw e;
26
+ }
27
+ }
28
+ export function updateAccountQuota(account, usage, accountManager) {
29
+ const meta = { usedCount: usage.usedCount || 0, limitCount: usage.limitCount || 0, realEmail: usage.email };
30
+ account.usedCount = meta.usedCount;
31
+ account.limitCount = meta.limitCount;
32
+ if (meta.realEmail)
33
+ account.realEmail = meta.realEmail;
34
+ if (accountManager)
35
+ accountManager.updateUsage(account.id, meta);
36
+ }
@@ -0,0 +1,32 @@
1
+ export declare const createKiroPlugin: (id: string) => ({ client, directory }: any) => Promise<{
2
+ auth: {
3
+ provider: string;
4
+ loader: (getAuth: any) => Promise<{
5
+ apiKey: string;
6
+ baseURL: string;
7
+ fetch(input: any, init?: any): Promise<Response>;
8
+ }>;
9
+ methods: {
10
+ id: string;
11
+ label: string;
12
+ type: string;
13
+ authorize: () => Promise<unknown>;
14
+ }[];
15
+ };
16
+ }>;
17
+ export declare const KiroOAuthPlugin: ({ client, directory }: any) => Promise<{
18
+ auth: {
19
+ provider: string;
20
+ loader: (getAuth: any) => Promise<{
21
+ apiKey: string;
22
+ baseURL: string;
23
+ fetch(input: any, init?: any): Promise<Response>;
24
+ }>;
25
+ methods: {
26
+ id: string;
27
+ label: string;
28
+ type: string;
29
+ authorize: () => Promise<unknown>;
30
+ }[];
31
+ };
32
+ }>;
package/dist/plugin.js ADDED
@@ -0,0 +1,222 @@
1
+ import { loadConfig } from './plugin/config';
2
+ import { AccountManager, generateAccountId } from './plugin/accounts';
3
+ import { accessTokenExpired, encodeRefreshToken } from './kiro/auth';
4
+ import { refreshAccessToken } from './plugin/token';
5
+ import { transformToCodeWhisperer } from './plugin/request';
6
+ import { parseEventStream } from './plugin/response';
7
+ import { transformKiroStream } from './plugin/streaming';
8
+ import { fetchUsageLimits, updateAccountQuota } from './plugin/usage';
9
+ import { authorizeKiroIDC } from './kiro/oauth-idc';
10
+ import { startIDCAuthServer } from './plugin/server';
11
+ import { KiroTokenRefreshError } from './plugin/errors';
12
+ import { KIRO_CONSTANTS } from './constants';
13
+ import * as logger from './plugin/logger';
14
+ const KIRO_PROVIDER_ID = 'kiro';
15
+ const KIRO_API_PATTERN = /^(https?:\/\/)?q\.[a-z0-9-]+\.amazonaws\.com/;
16
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
17
+ const isNetworkError = (e) => e instanceof Error && /econnreset|etimedout|enotfound|network|fetch failed/i.test(e.message);
18
+ const extractModel = (url) => url.match(/models\/([^/:]+)/)?.[1] || null;
19
+ export const createKiroPlugin = (id) => async ({ client, directory }) => {
20
+ const config = loadConfig(directory);
21
+ const showToast = (message, variant) => {
22
+ client.tui.showToast({ body: { message, variant } }).catch(() => { });
23
+ };
24
+ return {
25
+ auth: {
26
+ provider: id,
27
+ loader: async (getAuth) => {
28
+ await getAuth();
29
+ const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
30
+ return {
31
+ apiKey: '',
32
+ baseURL: KIRO_CONSTANTS.BASE_URL.replace('/generateAssistantResponse', '').replace('{{region}}', config.default_region || 'us-east-1'),
33
+ async fetch(input, init) {
34
+ const url = typeof input === 'string' ? input : input.url;
35
+ if (!KIRO_API_PATTERN.test(url))
36
+ return fetch(input, init);
37
+ const body = init?.body ? JSON.parse(init.body) : {};
38
+ const model = extractModel(url) || body.model || 'claude-opus-4-5';
39
+ const think = model.endsWith('-thinking') || !!body.providerOptions?.thinkingConfig;
40
+ const budget = body.providerOptions?.thinkingConfig?.thinkingBudget || 20000;
41
+ let retry = 0;
42
+ while (true) {
43
+ const count = am.getAccountCount();
44
+ if (count === 0)
45
+ throw new Error('No accounts. Login first.');
46
+ const acc = am.getCurrentOrNext();
47
+ if (!acc) {
48
+ const w = am.getMinWaitTime() || 60000;
49
+ showToast(`All accounts rate-limited. Waiting ${Math.ceil(w / 1000)}s...`, 'warning');
50
+ await sleep(w);
51
+ continue;
52
+ }
53
+ if (count > 1 && am.shouldShowToast())
54
+ showToast(`Using ${acc.email} (${am.getAccounts().indexOf(acc) + 1}/${count})`, 'info');
55
+ let auth = am.toAuthDetails(acc);
56
+ if (accessTokenExpired(auth)) {
57
+ try {
58
+ logger.log(`Refreshing token for ${acc.email}`);
59
+ auth = await refreshAccessToken(auth);
60
+ am.updateFromAuth(acc, auth);
61
+ await am.saveToDisk();
62
+ }
63
+ catch (e) {
64
+ const msg = e instanceof KiroTokenRefreshError ? e.message : String(e);
65
+ showToast(`Refresh failed for ${acc.email}: ${msg}`, 'error');
66
+ if (e instanceof KiroTokenRefreshError && e.code === 'invalid_grant') {
67
+ am.removeAccount(acc);
68
+ await am.saveToDisk();
69
+ continue;
70
+ }
71
+ throw e;
72
+ }
73
+ }
74
+ const prep = transformToCodeWhisperer(url, init?.body, model, auth, think, budget);
75
+ try {
76
+ const res = await fetch(prep.url, prep.init);
77
+ if (res.ok) {
78
+ if (config.usage_tracking_enabled)
79
+ fetchUsageLimits(auth)
80
+ .then((u) => {
81
+ updateAccountQuota(acc, u, am);
82
+ am.saveToDisk();
83
+ })
84
+ .catch((e) => logger.warn(`Usage sync failed for ${acc.email}: ${e.message}`));
85
+ if (prep.streaming) {
86
+ const s = transformKiroStream(res, model, prep.conversationId);
87
+ return new Response(new ReadableStream({
88
+ async start(c) {
89
+ try {
90
+ for await (const e of s)
91
+ c.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(e)}\n\n`));
92
+ c.close();
93
+ }
94
+ catch (err) {
95
+ c.error(err);
96
+ }
97
+ }
98
+ }), { headers: { 'Content-Type': 'text/event-stream' } });
99
+ }
100
+ const text = await res.text();
101
+ const p = parseEventStream(text);
102
+ const oai = {
103
+ id: prep.conversationId,
104
+ object: 'chat.completion',
105
+ created: Math.floor(Date.now() / 1000),
106
+ model,
107
+ choices: [{ index: 0, message: { role: 'assistant', content: p.content }, finish_reason: p.stopReason === 'tool_use' ? 'tool_calls' : 'stop' }],
108
+ usage: { prompt_tokens: p.inputTokens || 0, completion_tokens: p.outputTokens || 0, total_tokens: (p.inputTokens || 0) + (p.outputTokens || 0) }
109
+ };
110
+ if (p.toolCalls.length > 0)
111
+ oai.choices[0].message.tool_calls = p.toolCalls.map((tc) => ({
112
+ id: tc.toolUseId,
113
+ type: 'function',
114
+ function: { name: tc.name, arguments: typeof tc.input === 'string' ? tc.input : JSON.stringify(tc.input) }
115
+ }));
116
+ return new Response(JSON.stringify(oai), { headers: { 'Content-Type': 'application/json' } });
117
+ }
118
+ if (res.status === 401 && retry < config.rate_limit_max_retries) {
119
+ logger.warn(`Unauthorized (401) on ${acc.email}, retrying...`);
120
+ retry++;
121
+ continue;
122
+ }
123
+ if (res.status === 429) {
124
+ const wait = parseInt(res.headers.get('retry-after') || '60') * 1000;
125
+ am.markRateLimited(acc, wait);
126
+ await am.saveToDisk();
127
+ if (count > 1) {
128
+ showToast(`Rate limited on ${acc.email}. Switching account...`, 'warning');
129
+ continue;
130
+ }
131
+ else {
132
+ showToast(`Rate limited. Retrying in ${Math.ceil(wait / 1000)}s...`, 'warning');
133
+ await sleep(wait);
134
+ continue;
135
+ }
136
+ }
137
+ if ((res.status === 402 || res.status === 403) && count > 1) {
138
+ showToast(`${res.status === 402 ? 'Quota exhausted' : 'Forbidden'} on ${acc.email}. Switching...`, 'warning');
139
+ am.markUnhealthy(acc, res.status === 402 ? 'Quota' : 'Forbidden');
140
+ await am.saveToDisk();
141
+ continue;
142
+ }
143
+ throw new Error(`Kiro Error: ${res.status}`);
144
+ }
145
+ catch (e) {
146
+ if (isNetworkError(e) && retry < config.rate_limit_max_retries) {
147
+ const delay = 5000 * Math.pow(2, retry);
148
+ showToast(`Network error. Retrying in ${Math.ceil(delay / 1000)}s...`, 'warning');
149
+ await sleep(delay);
150
+ retry++;
151
+ continue;
152
+ }
153
+ throw e;
154
+ }
155
+ }
156
+ }
157
+ };
158
+ },
159
+ methods: [
160
+ {
161
+ id: 'idc',
162
+ label: 'AWS Builder ID (IDC)',
163
+ type: 'oauth',
164
+ authorize: async () => new Promise(async (resolve) => {
165
+ const region = config.default_region;
166
+ const authData = await authorizeKiroIDC(region);
167
+ const { url, waitForAuth } = await startIDCAuthServer(authData);
168
+ resolve({
169
+ url,
170
+ instructions: 'Opening browser...',
171
+ method: 'auto',
172
+ callback: async () => {
173
+ try {
174
+ const res = await waitForAuth();
175
+ const am = await AccountManager.loadFromDisk(config.account_selection_strategy);
176
+ const acc = {
177
+ id: generateAccountId(),
178
+ email: res.email,
179
+ authMethod: 'idc',
180
+ region,
181
+ clientId: res.clientId,
182
+ clientSecret: res.clientSecret,
183
+ refreshToken: res.refreshToken,
184
+ accessToken: res.accessToken,
185
+ expiresAt: res.expiresAt,
186
+ rateLimitResetTime: 0,
187
+ isHealthy: true
188
+ };
189
+ try {
190
+ const u = await fetchUsageLimits({
191
+ refresh: encodeRefreshToken({ refreshToken: res.refreshToken, clientId: res.clientId, clientSecret: res.clientSecret, authMethod: 'idc' }),
192
+ access: res.accessToken,
193
+ expires: res.expiresAt,
194
+ authMethod: 'idc',
195
+ region,
196
+ clientId: res.clientId,
197
+ clientSecret: res.clientSecret,
198
+ email: res.email
199
+ });
200
+ am.updateUsage(acc.id, { usedCount: u.usedCount, limitCount: u.limitCount, realEmail: u.email });
201
+ }
202
+ catch (e) {
203
+ logger.warn(`Initial usage fetch failed: ${e.message}`);
204
+ }
205
+ am.addAccount(acc);
206
+ await am.saveToDisk();
207
+ showToast(`Successfully logged in as ${res.email}`, 'success');
208
+ return { type: 'success', key: res.accessToken };
209
+ }
210
+ catch (e) {
211
+ logger.error(`Login failed: ${e.message}`);
212
+ return { type: 'failed' };
213
+ }
214
+ }
215
+ });
216
+ })
217
+ }
218
+ ]
219
+ }
220
+ };
221
+ };
222
+ export const KiroOAuthPlugin = createKiroPlugin(KIRO_PROVIDER_ID);
@@ -0,0 +1,22 @@
1
+ import type { KiroRegion } from './plugin/types';
2
+ export declare const KIRO_CONSTANTS: {
3
+ REFRESH_URL: string;
4
+ REFRESH_IDC_URL: string;
5
+ BASE_URL: string;
6
+ USAGE_LIMITS_URL: string;
7
+ DEFAULT_REGION: KiroRegion;
8
+ ACCESS_TOKEN_EXPIRY_BUFFER_MS: number;
9
+ AXIOS_TIMEOUT: number;
10
+ USER_AGENT: string;
11
+ KIRO_VERSION: string;
12
+ CHAT_TRIGGER_TYPE_MANUAL: string;
13
+ ORIGIN_AI_EDITOR: string;
14
+ };
15
+ export declare const MODEL_MAPPING: Record<string, string>;
16
+ export declare const SUPPORTED_MODELS: string[];
17
+ export declare const KIRO_AUTH_SERVICE: {
18
+ ENDPOINT: string;
19
+ SSO_OIDC_ENDPOINT: string;
20
+ BUILDER_ID_START_URL: string;
21
+ SCOPES: string[];
22
+ };
@@ -0,0 +1,35 @@
1
+ export const KIRO_CONSTANTS = {
2
+ REFRESH_URL: 'https://prod.{{region}}.auth.desktop.kiro.dev/refreshToken',
3
+ REFRESH_IDC_URL: 'https://oidc.{{region}}.amazonaws.com/token',
4
+ BASE_URL: 'https://q.{{region}}.amazonaws.com/generateAssistantResponse',
5
+ USAGE_LIMITS_URL: 'https://q.{{region}}.amazonaws.com/getUsageLimits',
6
+ DEFAULT_REGION: 'us-east-1',
7
+ ACCESS_TOKEN_EXPIRY_BUFFER_MS: 60000,
8
+ AXIOS_TIMEOUT: 120000,
9
+ USER_AGENT: 'KiroIDE',
10
+ KIRO_VERSION: '0.7.5',
11
+ CHAT_TRIGGER_TYPE_MANUAL: 'MANUAL',
12
+ ORIGIN_AI_EDITOR: 'AI_EDITOR',
13
+ };
14
+ export const MODEL_MAPPING = {
15
+ 'claude-opus-4-5': 'claude-opus-4.5',
16
+ 'claude-opus-4-5-20251101': 'claude-opus-4.5',
17
+ 'claude-haiku-4-5': 'claude-haiku-4.5',
18
+ 'claude-sonnet-4-5': 'CLAUDE_SONNET_4_5_20250929_V1_0',
19
+ 'claude-sonnet-4-5-20250929': 'CLAUDE_SONNET_4_5_20250929_V1_0',
20
+ 'claude-sonnet-4-20250514': 'CLAUDE_SONNET_4_20250514_V1_0',
21
+ 'claude-3-7-sonnet-20250219': 'CLAUDE_3_7_SONNET_20250219_V1_0',
22
+ };
23
+ export const SUPPORTED_MODELS = Object.keys(MODEL_MAPPING);
24
+ export const KIRO_AUTH_SERVICE = {
25
+ ENDPOINT: 'https://prod.{{region}}.auth.desktop.kiro.dev',
26
+ SSO_OIDC_ENDPOINT: 'https://oidc.{{region}}.amazonaws.com',
27
+ BUILDER_ID_START_URL: 'https://view.awsapps.com/start',
28
+ SCOPES: [
29
+ 'codewhisperer:completions',
30
+ 'codewhisperer:analysis',
31
+ 'codewhisperer:conversations',
32
+ 'codewhisperer:transformations',
33
+ 'codewhisperer:taskassist'
34
+ ],
35
+ };
@@ -0,0 +1,5 @@
1
+ import type { KiroAuthDetails, RefreshParts } from '../plugin/types';
2
+ export declare function parseRefreshParts(refresh: string): RefreshParts;
3
+ export declare function accessTokenExpired(auth: KiroAuthDetails): boolean;
4
+ export declare function validateAuthDetails(auth: KiroAuthDetails): boolean;
5
+ export declare function encodeRefreshToken(refreshToken: string, profileArn?: string, clientId?: string, clientSecret?: string, authMethod?: string): string;
@@ -0,0 +1,69 @@
1
+ import { KIRO_CONSTANTS } from '../constants';
2
+ export function parseRefreshParts(refresh) {
3
+ const parts = refresh.split('|');
4
+ if (parts.length < 2) {
5
+ throw new Error('Invalid refresh token format');
6
+ }
7
+ const refreshToken = parts[0];
8
+ const authMethod = parts[parts.length - 1];
9
+ if (authMethod === 'social') {
10
+ if (parts.length !== 3) {
11
+ throw new Error('Invalid social auth refresh token format');
12
+ }
13
+ const profileArn = parts[1];
14
+ return {
15
+ refreshToken,
16
+ profileArn,
17
+ authMethod: 'social',
18
+ };
19
+ }
20
+ else if (authMethod === 'idc') {
21
+ if (parts.length !== 4) {
22
+ throw new Error('Invalid IDC auth refresh token format');
23
+ }
24
+ const clientId = parts[1];
25
+ const clientSecret = parts[2];
26
+ return {
27
+ refreshToken,
28
+ clientId,
29
+ clientSecret,
30
+ authMethod: 'idc',
31
+ };
32
+ }
33
+ throw new Error(`Unknown auth method: ${authMethod}`);
34
+ }
35
+ export function accessTokenExpired(auth) {
36
+ if (!auth.access || !auth.expires) {
37
+ return true;
38
+ }
39
+ const now = Date.now();
40
+ const expiryWithBuffer = auth.expires - KIRO_CONSTANTS.ACCESS_TOKEN_EXPIRY_BUFFER_MS;
41
+ return now >= expiryWithBuffer;
42
+ }
43
+ export function validateAuthDetails(auth) {
44
+ if (!auth.refresh || !auth.authMethod || !auth.region) {
45
+ return false;
46
+ }
47
+ if (auth.authMethod === 'social') {
48
+ return !!auth.profileArn && !!auth.refresh;
49
+ }
50
+ else if (auth.authMethod === 'idc') {
51
+ return !!auth.clientId && !!auth.clientSecret && !!auth.refresh;
52
+ }
53
+ return false;
54
+ }
55
+ export function encodeRefreshToken(refreshToken, profileArn, clientId, clientSecret, authMethod) {
56
+ if (authMethod === 'social') {
57
+ if (!profileArn) {
58
+ throw new Error('Missing profileArn for social auth');
59
+ }
60
+ return `${refreshToken}|${profileArn}|social`;
61
+ }
62
+ else if (authMethod === 'idc') {
63
+ if (!clientId || !clientSecret) {
64
+ throw new Error('Missing clientId or clientSecret for IDC auth');
65
+ }
66
+ return `${refreshToken}|${clientId}|${clientSecret}|idc`;
67
+ }
68
+ throw new Error(`Unknown auth method: ${authMethod}`);
69
+ }
@@ -0,0 +1,22 @@
1
+ import type { KiroRegion } from '../plugin/types';
2
+ export interface KiroIDCAuthorization {
3
+ url: string;
4
+ verifier: string;
5
+ region: KiroRegion;
6
+ deviceCode: string;
7
+ userCode: string;
8
+ clientId: string;
9
+ clientSecret: string;
10
+ }
11
+ export interface KiroIDCTokenResult {
12
+ refreshToken: string;
13
+ accessToken: string;
14
+ expiresAt: number;
15
+ email: string;
16
+ clientId: string;
17
+ clientSecret: string;
18
+ region: KiroRegion;
19
+ authMethod: 'idc';
20
+ }
21
+ export declare function authorizeKiroIDC(region?: KiroRegion): Promise<KiroIDCAuthorization>;
22
+ export declare function exchangeKiroIDC(state: string): Promise<KiroIDCTokenResult>;