@kervnet/opencode-kiro-auth 1.5.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.
Files changed (106) hide show
  1. package/README.md +159 -0
  2. package/dist/constants.d.ts +24 -0
  3. package/dist/constants.js +55 -0
  4. package/dist/core/account/account-selector.d.ts +25 -0
  5. package/dist/core/account/account-selector.js +84 -0
  6. package/dist/core/account/usage-tracker.d.ts +17 -0
  7. package/dist/core/account/usage-tracker.js +39 -0
  8. package/dist/core/auth/auth-handler.d.ts +15 -0
  9. package/dist/core/auth/auth-handler.js +43 -0
  10. package/dist/core/auth/idc-auth-method.d.ts +17 -0
  11. package/dist/core/auth/idc-auth-method.js +200 -0
  12. package/dist/core/auth/token-refresher.d.ts +22 -0
  13. package/dist/core/auth/token-refresher.js +53 -0
  14. package/dist/core/index.d.ts +9 -0
  15. package/dist/core/index.js +9 -0
  16. package/dist/core/request/error-handler.d.ts +30 -0
  17. package/dist/core/request/error-handler.js +113 -0
  18. package/dist/core/request/request-handler.d.ts +27 -0
  19. package/dist/core/request/request-handler.js +199 -0
  20. package/dist/core/request/response-handler.d.ts +5 -0
  21. package/dist/core/request/response-handler.js +61 -0
  22. package/dist/core/request/retry-strategy.d.ts +18 -0
  23. package/dist/core/request/retry-strategy.js +28 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +1 -0
  26. package/dist/infrastructure/database/account-cache.d.ts +14 -0
  27. package/dist/infrastructure/database/account-cache.js +44 -0
  28. package/dist/infrastructure/database/account-repository.d.ts +12 -0
  29. package/dist/infrastructure/database/account-repository.js +64 -0
  30. package/dist/infrastructure/index.d.ts +7 -0
  31. package/dist/infrastructure/index.js +7 -0
  32. package/dist/infrastructure/transformers/event-stream-parser.d.ts +7 -0
  33. package/dist/infrastructure/transformers/event-stream-parser.js +115 -0
  34. package/dist/infrastructure/transformers/history-builder.d.ts +5 -0
  35. package/dist/infrastructure/transformers/history-builder.js +171 -0
  36. package/dist/infrastructure/transformers/message-transformer.d.ts +6 -0
  37. package/dist/infrastructure/transformers/message-transformer.js +102 -0
  38. package/dist/infrastructure/transformers/tool-call-parser.d.ts +4 -0
  39. package/dist/infrastructure/transformers/tool-call-parser.js +45 -0
  40. package/dist/infrastructure/transformers/tool-transformer.d.ts +2 -0
  41. package/dist/infrastructure/transformers/tool-transformer.js +19 -0
  42. package/dist/kiro/auth.d.ts +4 -0
  43. package/dist/kiro/auth.js +25 -0
  44. package/dist/kiro/oauth-idc.d.ts +24 -0
  45. package/dist/kiro/oauth-idc.js +151 -0
  46. package/dist/plugin/accounts.d.ts +29 -0
  47. package/dist/plugin/accounts.js +235 -0
  48. package/dist/plugin/auth-page.d.ts +3 -0
  49. package/dist/plugin/auth-page.js +573 -0
  50. package/dist/plugin/cli.d.ts +8 -0
  51. package/dist/plugin/cli.js +103 -0
  52. package/dist/plugin/config/index.d.ts +3 -0
  53. package/dist/plugin/config/index.js +2 -0
  54. package/dist/plugin/config/loader.d.ts +6 -0
  55. package/dist/plugin/config/loader.js +129 -0
  56. package/dist/plugin/config/schema.d.ts +56 -0
  57. package/dist/plugin/config/schema.js +36 -0
  58. package/dist/plugin/errors.d.ts +17 -0
  59. package/dist/plugin/errors.js +34 -0
  60. package/dist/plugin/health.d.ts +1 -0
  61. package/dist/plugin/health.js +9 -0
  62. package/dist/plugin/image-handler.d.ts +14 -0
  63. package/dist/plugin/image-handler.js +64 -0
  64. package/dist/plugin/logger.d.ts +8 -0
  65. package/dist/plugin/logger.js +63 -0
  66. package/dist/plugin/models.d.ts +1 -0
  67. package/dist/plugin/models.js +8 -0
  68. package/dist/plugin/request.d.ts +2 -0
  69. package/dist/plugin/request.js +239 -0
  70. package/dist/plugin/response.d.ts +3 -0
  71. package/dist/plugin/response.js +95 -0
  72. package/dist/plugin/server.d.ts +24 -0
  73. package/dist/plugin/server.js +166 -0
  74. package/dist/plugin/storage/locked-operations.d.ts +5 -0
  75. package/dist/plugin/storage/locked-operations.js +91 -0
  76. package/dist/plugin/storage/migrations.d.ts +2 -0
  77. package/dist/plugin/storage/migrations.js +109 -0
  78. package/dist/plugin/storage/sqlite.d.ts +17 -0
  79. package/dist/plugin/storage/sqlite.js +134 -0
  80. package/dist/plugin/streaming/index.d.ts +2 -0
  81. package/dist/plugin/streaming/index.js +2 -0
  82. package/dist/plugin/streaming/openai-converter.d.ts +2 -0
  83. package/dist/plugin/streaming/openai-converter.js +68 -0
  84. package/dist/plugin/streaming/stream-parser.d.ts +5 -0
  85. package/dist/plugin/streaming/stream-parser.js +136 -0
  86. package/dist/plugin/streaming/stream-state.d.ts +5 -0
  87. package/dist/plugin/streaming/stream-state.js +59 -0
  88. package/dist/plugin/streaming/stream-transformer.d.ts +1 -0
  89. package/dist/plugin/streaming/stream-transformer.js +248 -0
  90. package/dist/plugin/streaming/types.d.ts +25 -0
  91. package/dist/plugin/streaming/types.js +2 -0
  92. package/dist/plugin/sync/aws-sso.d.ts +2 -0
  93. package/dist/plugin/sync/aws-sso.js +50 -0
  94. package/dist/plugin/sync/kiro-cli-parser.d.ts +8 -0
  95. package/dist/plugin/sync/kiro-cli-parser.js +72 -0
  96. package/dist/plugin/sync/kiro-cli.d.ts +2 -0
  97. package/dist/plugin/sync/kiro-cli.js +197 -0
  98. package/dist/plugin/token.d.ts +2 -0
  99. package/dist/plugin/token.js +79 -0
  100. package/dist/plugin/types.d.ts +109 -0
  101. package/dist/plugin/types.js +0 -0
  102. package/dist/plugin/usage.d.ts +3 -0
  103. package/dist/plugin/usage.js +45 -0
  104. package/dist/plugin.d.ts +32 -0
  105. package/dist/plugin.js +37 -0
  106. package/package.json +65 -0
@@ -0,0 +1,79 @@
1
+ import crypto from 'node:crypto';
2
+ import { decodeRefreshToken, encodeRefreshToken } from '../kiro/auth';
3
+ import { KiroTokenRefreshError } from './errors';
4
+ export async function refreshAccessToken(auth) {
5
+ const p = decodeRefreshToken(auth.refresh);
6
+ const isIdc = auth.authMethod === 'idc';
7
+ const isAwsSso = auth.authMethod === 'aws-sso';
8
+ const url = isIdc || isAwsSso
9
+ ? `https://oidc.${auth.region}.amazonaws.com/token`
10
+ : `https://prod.${auth.region}.auth.desktop.kiro.dev/refreshToken`;
11
+ if ((isIdc || isAwsSso) && (!p.clientId || !p.clientSecret)) {
12
+ throw new KiroTokenRefreshError('Missing creds', 'MISSING_CREDENTIALS');
13
+ }
14
+ const requestBody = isIdc || isAwsSso
15
+ ? {
16
+ refreshToken: p.refreshToken,
17
+ clientId: p.clientId,
18
+ clientSecret: p.clientSecret,
19
+ grantType: 'refresh_token'
20
+ }
21
+ : {
22
+ refreshToken: p.refreshToken
23
+ };
24
+ const machineId = crypto
25
+ .createHash('sha256')
26
+ .update(auth.profileArn || auth.clientId || 'KIRO_DEFAULT_MACHINE')
27
+ .digest('hex');
28
+ const ua = isIdc || isAwsSso ? 'aws-sdk-js/1.0.0' : `KiroIDE-0.7.45-${machineId}`;
29
+ try {
30
+ const res = await fetch(url, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ Accept: 'application/json',
35
+ 'amz-sdk-request': 'attempt=1; max=1',
36
+ 'x-amzn-kiro-agent-mode': 'vibe',
37
+ 'user-agent': ua,
38
+ Connection: 'close'
39
+ },
40
+ body: JSON.stringify(requestBody)
41
+ });
42
+ if (!res.ok) {
43
+ const txt = await res.text();
44
+ let data = {};
45
+ try {
46
+ data = JSON.parse(txt);
47
+ }
48
+ catch {
49
+ data = { message: txt };
50
+ }
51
+ throw new KiroTokenRefreshError(`Refresh failed: ${data.message || data.error_description || txt}`, data.error || `HTTP_${res.status}`);
52
+ }
53
+ const d = await res.json();
54
+ const acc = d.access_token || d.accessToken;
55
+ if (!acc)
56
+ throw new KiroTokenRefreshError('No access token', 'INVALID_RESPONSE');
57
+ const upP = {
58
+ refreshToken: d.refresh_token || d.refreshToken || p.refreshToken,
59
+ clientId: p.clientId,
60
+ clientSecret: p.clientSecret,
61
+ authMethod: auth.authMethod
62
+ };
63
+ return {
64
+ refresh: encodeRefreshToken(upP),
65
+ access: acc,
66
+ expires: Date.now() + (d.expires_in || d.expiresIn || 3600) * 1000,
67
+ authMethod: auth.authMethod,
68
+ region: auth.region,
69
+ clientId: auth.clientId,
70
+ clientSecret: auth.clientSecret,
71
+ email: auth.email || d.userInfo?.email
72
+ };
73
+ }
74
+ catch (error) {
75
+ if (error instanceof KiroTokenRefreshError)
76
+ throw error;
77
+ throw new KiroTokenRefreshError(`Token refresh failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'NETWORK_ERROR', error instanceof Error ? error : undefined);
78
+ }
79
+ }
@@ -0,0 +1,109 @@
1
+ export type KiroAuthMethod = 'idc' | 'desktop' | 'aws-sso';
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
+ authMethod: KiroAuthMethod;
25
+ region: KiroRegion;
26
+ clientId?: string;
27
+ clientSecret?: string;
28
+ profileArn?: string;
29
+ refreshToken: string;
30
+ accessToken: string;
31
+ expiresAt: number;
32
+ rateLimitResetTime: number;
33
+ isHealthy: boolean;
34
+ unhealthyReason?: string;
35
+ recoveryTime?: number;
36
+ failCount: number;
37
+ usedCount?: number;
38
+ limitCount?: number;
39
+ lastSync?: number;
40
+ lastUsed?: number;
41
+ }
42
+ export interface CodeWhispererMessage {
43
+ userInputMessage?: {
44
+ content: string;
45
+ modelId: string;
46
+ origin: string;
47
+ images?: Array<{
48
+ format: string;
49
+ source: {
50
+ bytes: string;
51
+ };
52
+ }>;
53
+ userInputMessageContext?: {
54
+ toolResults?: Array<{
55
+ toolUseId: string;
56
+ content: Array<{
57
+ text?: string;
58
+ }>;
59
+ status?: string;
60
+ }>;
61
+ tools?: Array<{
62
+ toolSpecification: {
63
+ name: string;
64
+ description: string;
65
+ inputSchema: {
66
+ json: Record<string, unknown>;
67
+ };
68
+ };
69
+ }>;
70
+ };
71
+ };
72
+ assistantResponseMessage?: {
73
+ content: string;
74
+ toolUses?: Array<{
75
+ input: any;
76
+ name: string;
77
+ toolUseId: string;
78
+ }>;
79
+ };
80
+ }
81
+ export interface CodeWhispererRequest {
82
+ conversationState: {
83
+ chatTriggerType: string;
84
+ conversationId: string;
85
+ history?: CodeWhispererMessage[];
86
+ currentMessage: CodeWhispererMessage;
87
+ };
88
+ profileArn?: string;
89
+ }
90
+ export interface ToolCall {
91
+ toolUseId: string;
92
+ name: string;
93
+ input: string | Record<string, unknown>;
94
+ }
95
+ export interface ParsedResponse {
96
+ content: string;
97
+ toolCalls: ToolCall[];
98
+ stopReason?: string;
99
+ inputTokens?: number;
100
+ outputTokens?: number;
101
+ }
102
+ export interface PreparedRequest {
103
+ url: string;
104
+ init: RequestInit;
105
+ streaming: boolean;
106
+ effectiveModel: string;
107
+ conversationId: string;
108
+ }
109
+ export type AccountSelectionStrategy = 'sticky' | 'round-robin' | 'lowest-usage';
File without changes
@@ -0,0 +1,3 @@
1
+ import { KiroAuthDetails, ManagedAccount } 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,45 @@
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: {
7
+ Authorization: `Bearer ${auth.access}`,
8
+ 'Content-Type': 'application/json',
9
+ 'x-amzn-kiro-agent-mode': 'vibe',
10
+ 'amz-sdk-request': 'attempt=1; max=1'
11
+ }
12
+ });
13
+ if (!res.ok)
14
+ throw new Error(`Status: ${res.status}`);
15
+ const data = await res.json();
16
+ let usedCount = 0, limitCount = 0;
17
+ if (Array.isArray(data.usageBreakdownList)) {
18
+ for (const s of data.usageBreakdownList) {
19
+ if (s.freeTrialInfo) {
20
+ usedCount += s.freeTrialInfo.currentUsage || 0;
21
+ limitCount += s.freeTrialInfo.usageLimit || 0;
22
+ }
23
+ usedCount += s.currentUsage || 0;
24
+ limitCount += s.usageLimit || 0;
25
+ }
26
+ }
27
+ return { usedCount, limitCount, email: data.userInfo?.email };
28
+ }
29
+ catch (e) {
30
+ throw e;
31
+ }
32
+ }
33
+ export function updateAccountQuota(account, usage, accountManager) {
34
+ const meta = {
35
+ usedCount: usage.usedCount || 0,
36
+ limitCount: usage.limitCount || 0,
37
+ email: usage.email
38
+ };
39
+ account.usedCount = meta.usedCount;
40
+ account.limitCount = meta.limitCount;
41
+ if (usage.email)
42
+ account.email = usage.email;
43
+ if (accountManager)
44
+ accountManager.updateUsage(account.id, meta);
45
+ }
@@ -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: "oauth";
13
+ authorize: (inputs?: any) => Promise<any>;
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: "oauth";
29
+ authorize: (inputs?: any) => Promise<any>;
30
+ }[];
31
+ };
32
+ }>;
package/dist/plugin.js ADDED
@@ -0,0 +1,37 @@
1
+ import { KIRO_CONSTANTS } from './constants.js';
2
+ import { AuthHandler } from './core/auth/auth-handler.js';
3
+ import { RequestHandler } from './core/request/request-handler.js';
4
+ import { AccountCache } from './infrastructure/database/account-cache.js';
5
+ import { AccountRepository } from './infrastructure/database/account-repository.js';
6
+ import { AccountManager } from './plugin/accounts.js';
7
+ import { loadConfig } from './plugin/config/index.js';
8
+ const KIRO_PROVIDER_ID = 'kiro';
9
+ export const createKiroPlugin = (id) => async ({ client, directory }) => {
10
+ const config = loadConfig(directory);
11
+ const showToast = (message, variant) => {
12
+ client.tui.showToast({ body: { message, variant } }).catch(() => { });
13
+ };
14
+ const cache = new AccountCache(60000);
15
+ const repository = new AccountRepository(cache);
16
+ const authHandler = new AuthHandler(config, repository);
17
+ const accountManager = await AccountManager.loadFromDisk(config.account_selection_strategy);
18
+ authHandler.setAccountManager(accountManager);
19
+ // Sync AWS SSO before OpenCode checks for accounts
20
+ await authHandler.initialize();
21
+ const requestHandler = new RequestHandler(accountManager, config, repository);
22
+ return {
23
+ auth: {
24
+ provider: id,
25
+ loader: async (getAuth) => {
26
+ await getAuth();
27
+ return {
28
+ apiKey: '',
29
+ baseURL: KIRO_CONSTANTS.BASE_URL.replace('/generateAssistantResponse', '').replace('{{region}}', config.default_region || 'us-east-1'),
30
+ fetch: (input, init) => requestHandler.handle(input, init, showToast)
31
+ };
32
+ },
33
+ methods: authHandler.getMethods()
34
+ }
35
+ };
36
+ };
37
+ export const KiroOAuthPlugin = createKiroPlugin(KIRO_PROVIDER_ID);
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@kervnet/opencode-kiro-auth",
3
+ "version": "1.5.1",
4
+ "description": "OpenCode plugin for AWS Kiro (CodeWhisperer) with IAM Identity Center profile support",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.build.json",
10
+ "format": "prettier --write 'src/**/*.ts'",
11
+ "typecheck": "tsc --noEmit",
12
+ "prepare": "husky"
13
+ },
14
+ "keywords": [
15
+ "opencode",
16
+ "plugin",
17
+ "kiro",
18
+ "codewhisperer",
19
+ "aws",
20
+ "claude",
21
+ "ai",
22
+ "auth"
23
+ ],
24
+ "author": "tickernelz",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/tickernelz/opencode-kiro-auth.git"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "dependencies": {
34
+ "@opencode-ai/plugin": "^0.15.30",
35
+ "proper-lockfile": "^4.1.2",
36
+ "zod": "^3.24.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "@types/proper-lockfile": "^4.1.4",
41
+ "bun-types": "^1.3.6",
42
+ "husky": "^9.1.7",
43
+ "lint-staged": "^16.2.7",
44
+ "prettier": "^3.4.2",
45
+ "prettier-plugin-organize-imports": "^4.1.0",
46
+ "typescript": "^5.7.3"
47
+ },
48
+ "opencode": {
49
+ "type": "plugin",
50
+ "hooks": [
51
+ "auth",
52
+ "event"
53
+ ]
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "package.json",
58
+ "README.md"
59
+ ],
60
+ "lint-staged": {
61
+ "*.ts": [
62
+ "prettier --write"
63
+ ]
64
+ }
65
+ }