@zhafron/opencode-kiro-auth 1.2.7 → 1.3.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.
- package/README.md +31 -45
- package/dist/index.d.ts +1 -1
- package/dist/kiro/auth.js +15 -6
- package/dist/plugin/accounts.d.ts +2 -1
- package/dist/plugin/accounts.js +43 -20
- package/dist/plugin/cli.js +1 -1
- package/dist/plugin/config/index.d.ts +2 -2
- package/dist/plugin/config/index.js +2 -2
- package/dist/plugin/config/loader.js +3 -3
- package/dist/plugin/config/schema.d.ts +3 -0
- package/dist/plugin/config/schema.js +2 -0
- package/dist/plugin/request.d.ts +1 -1
- package/dist/plugin/request.js +77 -16
- package/dist/plugin/response.d.ts +1 -1
- package/dist/plugin/server.js +1 -1
- package/dist/plugin/storage/migration.d.ts +1 -0
- package/dist/plugin/storage/migration.js +53 -0
- package/dist/plugin/storage/sqlite.d.ts +16 -0
- package/dist/plugin/storage/sqlite.js +104 -0
- package/dist/plugin/sync/kiro-cli.d.ts +2 -0
- package/dist/plugin/sync/kiro-cli.js +96 -0
- package/dist/plugin/token.js +29 -17
- package/dist/plugin/types.d.ts +1 -27
- package/dist/plugin/usage.d.ts +1 -1
- package/dist/plugin.js +173 -249
- package/package.json +3 -2
- package/dist/plugin/storage.d.ts +0 -7
- package/dist/plugin/storage.js +0 -124
package/README.md
CHANGED
|
@@ -7,14 +7,13 @@ OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to Claude Sonnet a
|
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- AWS Builder ID (IDC)
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- Usage tracking with automatic retry on sync failures.
|
|
10
|
+
- **Multiple Auth Methods**: Supports AWS Builder ID (IDC) and Kiro Desktop (CLI-based) authentication.
|
|
11
|
+
- **Auto-Sync Kiro CLI**: Automatically imports and synchronizes active sessions from your local `kiro-cli` SQLite database.
|
|
12
|
+
- **Gradual Context Truncation**: Intelligently prevents error 400 by reducing context size dynamically during retries.
|
|
13
|
+
- **Intelligent Account Rotation**: Prioritizes multi-account usage based on lowest available quota.
|
|
14
|
+
- **High-Performance Storage**: Efficient account and usage management using native Bun SQLite.
|
|
15
|
+
- **Native Thinking Mode**: Full support for Claude reasoning capabilities via virtual model mappings.
|
|
16
|
+
- **Automated Recovery**: Exponential backoff for rate limits and automated token refresh.
|
|
18
17
|
|
|
19
18
|
## Installation
|
|
20
19
|
|
|
@@ -54,10 +53,14 @@ Add the plugin to your `opencode.json` or `opencode.jsonc`:
|
|
|
54
53
|
|
|
55
54
|
## Setup
|
|
56
55
|
|
|
57
|
-
1.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
1. **Authentication via Kiro CLI (Recommended)**:
|
|
57
|
+
- Perform login directly in your terminal using `kiro-cli login`.
|
|
58
|
+
- The plugin will automatically detect and import your session on startup.
|
|
59
|
+
2. **Direct Authentication**:
|
|
60
|
+
- Run `opencode auth login`.
|
|
61
|
+
- Select `Other`, type `kiro`, and press enter.
|
|
62
|
+
- Follow the instructions for **AWS Builder ID (IDC)**.
|
|
63
|
+
3. Configuration will be automatically managed at `~/.config/opencode/kiro.db`.
|
|
61
64
|
|
|
62
65
|
## Configuration
|
|
63
66
|
|
|
@@ -65,6 +68,7 @@ The plugin supports extensive configuration options. Edit `~/.config/opencode/ki
|
|
|
65
68
|
|
|
66
69
|
```json
|
|
67
70
|
{
|
|
71
|
+
"auto_sync_kiro_cli": true,
|
|
68
72
|
"account_selection_strategy": "lowest-usage",
|
|
69
73
|
"default_region": "us-east-1",
|
|
70
74
|
"rate_limit_retry_delay_ms": 5000,
|
|
@@ -82,46 +86,28 @@ The plugin supports extensive configuration options. Edit `~/.config/opencode/ki
|
|
|
82
86
|
|
|
83
87
|
### Configuration Options
|
|
84
88
|
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
87
|
-
- `
|
|
88
|
-
- `
|
|
89
|
-
- `
|
|
90
|
-
- `
|
|
91
|
-
- `
|
|
92
|
-
- `
|
|
93
|
-
- `
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
96
|
-
- `
|
|
97
|
-
|
|
98
|
-
### Environment Variables
|
|
99
|
-
|
|
100
|
-
All configuration options can be overridden via environment variables:
|
|
101
|
-
|
|
102
|
-
- `KIRO_ACCOUNT_SELECTION_STRATEGY`
|
|
103
|
-
- `KIRO_DEFAULT_REGION`
|
|
104
|
-
- `KIRO_RATE_LIMIT_RETRY_DELAY_MS`
|
|
105
|
-
- `KIRO_RATE_LIMIT_MAX_RETRIES`
|
|
106
|
-
- `KIRO_MAX_REQUEST_ITERATIONS`
|
|
107
|
-
- `KIRO_REQUEST_TIMEOUT_MS`
|
|
108
|
-
- `KIRO_TOKEN_EXPIRY_BUFFER_MS`
|
|
109
|
-
- `KIRO_USAGE_SYNC_MAX_RETRIES`
|
|
110
|
-
- `KIRO_AUTH_SERVER_PORT_START`
|
|
111
|
-
- `KIRO_AUTH_SERVER_PORT_RANGE`
|
|
112
|
-
- `KIRO_USAGE_TRACKING_ENABLED`
|
|
113
|
-
- `KIRO_ENABLE_LOG_API_REQUEST`
|
|
89
|
+
- `auto_sync_kiro_cli`: Automatically sync sessions from Kiro CLI (default: `true`).
|
|
90
|
+
- `account_selection_strategy`: Account rotation strategy (`sticky`, `round-robin`, `lowest-usage`).
|
|
91
|
+
- `default_region`: AWS region (`us-east-1`, `us-west-2`).
|
|
92
|
+
- `rate_limit_retry_delay_ms`: Delay between rate limit retries (1000-60000ms).
|
|
93
|
+
- `rate_limit_max_retries`: Maximum retry attempts for rate limits (0-10).
|
|
94
|
+
- `max_request_iterations`: Maximum loop iterations to prevent hangs (10-1000).
|
|
95
|
+
- `request_timeout_ms`: Request timeout in milliseconds (60000-600000ms).
|
|
96
|
+
- `token_expiry_buffer_ms`: Token refresh buffer time (30000-300000ms).
|
|
97
|
+
- `usage_sync_max_retries`: Retry attempts for usage sync (0-5).
|
|
98
|
+
- `auth_server_port_start`: Starting port for auth server (1024-65535).
|
|
99
|
+
- `auth_server_port_range`: Number of ports to try (1-100).
|
|
100
|
+
- `usage_tracking_enabled`: Enable usage tracking and toast notifications.
|
|
101
|
+
- `enable_log_api_request`: Enable detailed API request logging.
|
|
114
102
|
|
|
115
103
|
## Storage
|
|
116
104
|
|
|
117
105
|
**Linux/macOS:**
|
|
118
|
-
-
|
|
119
|
-
- Usage Tracking: `~/.config/opencode/kiro-usage.json`
|
|
106
|
+
- SQLite Database: `~/.config/opencode/kiro.db`
|
|
120
107
|
- Plugin Config: `~/.config/opencode/kiro.json`
|
|
121
108
|
|
|
122
109
|
**Windows:**
|
|
123
|
-
-
|
|
124
|
-
- Usage Tracking: `%APPDATA%\opencode\kiro-usage.json`
|
|
110
|
+
- SQLite Database: `%APPDATA%\opencode\kiro.db`
|
|
125
111
|
- Plugin Config: `%APPDATA%\opencode\kiro.json`
|
|
126
112
|
|
|
127
113
|
## Acknowledgements
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { KiroOAuthPlugin } from './plugin.js';
|
|
2
2
|
export type { KiroConfig } from './plugin/config/index.js';
|
|
3
|
-
export type {
|
|
3
|
+
export type { KiroAuthMethod, KiroRegion, ManagedAccount } from './plugin/types.js';
|
package/dist/kiro/auth.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export function decodeRefreshToken(refresh) {
|
|
2
2
|
const parts = refresh.split('|');
|
|
3
3
|
if (parts.length < 2)
|
|
4
|
-
return { refreshToken: parts[0], authMethod: '
|
|
4
|
+
return { refreshToken: parts[0], authMethod: 'desktop' };
|
|
5
5
|
const refreshToken = parts[0];
|
|
6
6
|
const authMethod = parts[parts.length - 1];
|
|
7
7
|
if (authMethod === 'idc')
|
|
8
8
|
return { refreshToken, clientId: parts[1], clientSecret: parts[2], authMethod: 'idc' };
|
|
9
|
-
|
|
9
|
+
if (authMethod === 'desktop')
|
|
10
|
+
return { refreshToken, authMethod: 'desktop' };
|
|
11
|
+
return { refreshToken, authMethod: 'desktop' };
|
|
10
12
|
}
|
|
11
13
|
export function accessTokenExpired(auth, bufferMs = 120000) {
|
|
12
14
|
if (!auth.access || !auth.expires)
|
|
@@ -14,10 +16,17 @@ export function accessTokenExpired(auth, bufferMs = 120000) {
|
|
|
14
16
|
return Date.now() >= auth.expires - bufferMs;
|
|
15
17
|
}
|
|
16
18
|
export function validateAuthDetails(auth) {
|
|
17
|
-
|
|
19
|
+
if (!auth.refresh)
|
|
20
|
+
return false;
|
|
21
|
+
if (auth.authMethod === 'idc')
|
|
22
|
+
return !!auth.clientId && !!auth.clientSecret;
|
|
23
|
+
return true;
|
|
18
24
|
}
|
|
19
25
|
export function encodeRefreshToken(parts) {
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
if (parts.authMethod === 'idc') {
|
|
27
|
+
if (!parts.clientId || !parts.clientSecret)
|
|
28
|
+
throw new Error('Missing credentials');
|
|
29
|
+
return `${parts.refreshToken}|${parts.clientId}|${parts.clientSecret}|idc`;
|
|
30
|
+
}
|
|
31
|
+
return `${parts.refreshToken}|desktop`;
|
|
23
32
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AccountSelectionStrategy, KiroAuthDetails, ManagedAccount, UsageMetadata } from './types';
|
|
2
2
|
export declare function generateAccountId(): string;
|
|
3
|
+
export declare function createDeterministicAccountId(email: string, method: string, clientId?: string, profileArn?: string): string;
|
|
3
4
|
export declare class AccountManager {
|
|
4
5
|
private accounts;
|
|
5
6
|
private usage;
|
package/dist/plugin/accounts.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { decodeRefreshToken, encodeRefreshToken } from '../kiro/auth';
|
|
3
|
+
import { kiroDb } from './storage/sqlite';
|
|
4
|
+
import { writeToKiroCli } from './sync/kiro-cli';
|
|
5
5
|
export function generateAccountId() {
|
|
6
6
|
return randomBytes(16).toString('hex');
|
|
7
7
|
}
|
|
8
|
+
export function createDeterministicAccountId(email, method, clientId, profileArn) {
|
|
9
|
+
return createHash('sha256')
|
|
10
|
+
.update(`${email}:${method}:${clientId || ''}:${profileArn || ''}`)
|
|
11
|
+
.digest('hex');
|
|
12
|
+
}
|
|
8
13
|
export class AccountManager {
|
|
9
14
|
accounts;
|
|
10
15
|
usage;
|
|
@@ -27,13 +32,27 @@ export class AccountManager {
|
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
34
|
static async loadFromDisk(strategy) {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const accounts =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const rows = kiroDb.getAccounts();
|
|
36
|
+
const usage = kiroDb.getUsage();
|
|
37
|
+
const accounts = rows.map((r) => ({
|
|
38
|
+
id: r.id,
|
|
39
|
+
email: r.email,
|
|
40
|
+
realEmail: r.real_email,
|
|
41
|
+
authMethod: r.auth_method,
|
|
42
|
+
region: r.region,
|
|
43
|
+
clientId: r.client_id,
|
|
44
|
+
clientSecret: r.client_secret,
|
|
45
|
+
profileArn: r.profile_arn,
|
|
46
|
+
refreshToken: r.refresh_token,
|
|
47
|
+
accessToken: r.access_token,
|
|
48
|
+
expiresAt: r.expires_at,
|
|
49
|
+
rateLimitResetTime: r.rate_limit_reset,
|
|
50
|
+
isHealthy: r.is_healthy === 1,
|
|
51
|
+
unhealthyReason: r.unhealthy_reason,
|
|
52
|
+
recoveryTime: r.recovery_time,
|
|
53
|
+
lastUsed: r.last_used
|
|
35
54
|
}));
|
|
36
|
-
return new AccountManager(accounts,
|
|
55
|
+
return new AccountManager(accounts, usage, strategy || 'sticky');
|
|
37
56
|
}
|
|
38
57
|
getAccountCount() {
|
|
39
58
|
return this.accounts.length;
|
|
@@ -102,6 +121,7 @@ export class AccountManager {
|
|
|
102
121
|
a.realEmail = meta.realEmail;
|
|
103
122
|
}
|
|
104
123
|
this.usage[id] = { ...meta, lastSync: Date.now() };
|
|
124
|
+
kiroDb.upsertUsage(id, this.usage[id]);
|
|
105
125
|
}
|
|
106
126
|
addAccount(a) {
|
|
107
127
|
const i = this.accounts.findIndex((x) => x.id === a.id);
|
|
@@ -109,6 +129,7 @@ export class AccountManager {
|
|
|
109
129
|
this.accounts.push(a);
|
|
110
130
|
else
|
|
111
131
|
this.accounts[i] = a;
|
|
132
|
+
kiroDb.upsertAccount(a);
|
|
112
133
|
}
|
|
113
134
|
removeAccount(a) {
|
|
114
135
|
const removedIndex = this.accounts.findIndex((x) => x.id === a.id);
|
|
@@ -116,15 +137,13 @@ export class AccountManager {
|
|
|
116
137
|
return;
|
|
117
138
|
this.accounts = this.accounts.filter((x) => x.id !== a.id);
|
|
118
139
|
delete this.usage[a.id];
|
|
119
|
-
|
|
140
|
+
kiroDb.deleteAccount(a.id);
|
|
141
|
+
if (this.accounts.length === 0)
|
|
120
142
|
this.cursor = 0;
|
|
121
|
-
|
|
122
|
-
else if (this.cursor >= this.accounts.length) {
|
|
143
|
+
else if (this.cursor >= this.accounts.length)
|
|
123
144
|
this.cursor = this.accounts.length - 1;
|
|
124
|
-
|
|
125
|
-
else if (removedIndex <= this.cursor && this.cursor > 0) {
|
|
145
|
+
else if (removedIndex <= this.cursor && this.cursor > 0)
|
|
126
146
|
this.cursor--;
|
|
127
|
-
}
|
|
128
147
|
}
|
|
129
148
|
updateFromAuth(a, auth) {
|
|
130
149
|
const acc = this.accounts.find((x) => x.id === a.id);
|
|
@@ -140,12 +159,16 @@ export class AccountManager {
|
|
|
140
159
|
acc.profileArn = p.profileArn;
|
|
141
160
|
if (p.clientId)
|
|
142
161
|
acc.clientId = p.clientId;
|
|
162
|
+
kiroDb.upsertAccount(acc);
|
|
163
|
+
writeToKiroCli(acc).catch(() => { });
|
|
143
164
|
}
|
|
144
165
|
}
|
|
145
166
|
markRateLimited(a, ms) {
|
|
146
167
|
const acc = this.accounts.find((x) => x.id === a.id);
|
|
147
|
-
if (acc)
|
|
168
|
+
if (acc) {
|
|
148
169
|
acc.rateLimitResetTime = Date.now() + ms;
|
|
170
|
+
kiroDb.upsertAccount(acc);
|
|
171
|
+
}
|
|
149
172
|
}
|
|
150
173
|
markUnhealthy(a, reason, recovery) {
|
|
151
174
|
const acc = this.accounts.find((x) => x.id === a.id);
|
|
@@ -153,12 +176,12 @@ export class AccountManager {
|
|
|
153
176
|
acc.isHealthy = false;
|
|
154
177
|
acc.unhealthyReason = reason;
|
|
155
178
|
acc.recoveryTime = recovery;
|
|
179
|
+
kiroDb.upsertAccount(acc);
|
|
156
180
|
}
|
|
157
181
|
}
|
|
158
182
|
async saveToDisk() {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
await saveUsage({ version: 1, usage: this.usage });
|
|
183
|
+
for (const a of this.accounts)
|
|
184
|
+
kiroDb.upsertAccount(a);
|
|
162
185
|
}
|
|
163
186
|
toAuthDetails(a) {
|
|
164
187
|
const p = {
|
package/dist/plugin/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createInterface } from 'node:readline/promises';
|
|
2
1
|
import { stdin as input, stdout as output } from 'node:process';
|
|
2
|
+
import { createInterface } from 'node:readline/promises';
|
|
3
3
|
export async function promptAddAnotherAccount(currentCount) {
|
|
4
4
|
const rl = createInterface({ input, output });
|
|
5
5
|
try {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { configExists, getDefaultLogsDir, getProjectConfigPath, getUserConfigPath, loadConfig } from './loader';
|
|
2
|
+
export { DEFAULT_CONFIG, KiroConfigSchema } from './schema';
|
|
2
3
|
export type { KiroConfig } from './schema';
|
|
3
|
-
export { loadConfig, getUserConfigPath, getProjectConfigPath, getDefaultLogsDir, configExists } from './loader';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { configExists, getDefaultLogsDir, getProjectConfigPath, getUserConfigPath, loadConfig } from './loader';
|
|
2
|
+
export { DEFAULT_CONFIG, KiroConfigSchema } from './schema';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync
|
|
2
|
-
import { join, dirname } from 'node:path';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
2
|
import { homedir } from 'node:os';
|
|
4
|
-
import {
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
5
4
|
import * as logger from '../logger';
|
|
5
|
+
import { AccountSelectionStrategySchema, DEFAULT_CONFIG, KiroConfigSchema, RegionSchema } from './schema';
|
|
6
6
|
function getConfigDir() {
|
|
7
7
|
const platform = process.platform;
|
|
8
8
|
if (platform === 'win32') {
|
|
@@ -16,6 +16,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
16
16
|
auth_server_port_start: z.ZodDefault<z.ZodNumber>;
|
|
17
17
|
auth_server_port_range: z.ZodDefault<z.ZodNumber>;
|
|
18
18
|
usage_tracking_enabled: z.ZodDefault<z.ZodBoolean>;
|
|
19
|
+
auto_sync_kiro_cli: z.ZodDefault<z.ZodBoolean>;
|
|
19
20
|
enable_log_api_request: z.ZodDefault<z.ZodBoolean>;
|
|
20
21
|
}, "strip", z.ZodTypeAny, {
|
|
21
22
|
account_selection_strategy: "sticky" | "round-robin" | "lowest-usage";
|
|
@@ -29,6 +30,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
29
30
|
auth_server_port_start: number;
|
|
30
31
|
auth_server_port_range: number;
|
|
31
32
|
usage_tracking_enabled: boolean;
|
|
33
|
+
auto_sync_kiro_cli: boolean;
|
|
32
34
|
enable_log_api_request: boolean;
|
|
33
35
|
$schema?: string | undefined;
|
|
34
36
|
}, {
|
|
@@ -44,6 +46,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
44
46
|
auth_server_port_start?: number | undefined;
|
|
45
47
|
auth_server_port_range?: number | undefined;
|
|
46
48
|
usage_tracking_enabled?: boolean | undefined;
|
|
49
|
+
auto_sync_kiro_cli?: boolean | undefined;
|
|
47
50
|
enable_log_api_request?: boolean | undefined;
|
|
48
51
|
}>;
|
|
49
52
|
export type KiroConfig = z.infer<typeof KiroConfigSchema>;
|
|
@@ -14,6 +14,7 @@ export const KiroConfigSchema = z.object({
|
|
|
14
14
|
auth_server_port_start: z.number().min(1024).max(65535).default(19847),
|
|
15
15
|
auth_server_port_range: z.number().min(1).max(100).default(10),
|
|
16
16
|
usage_tracking_enabled: z.boolean().default(true),
|
|
17
|
+
auto_sync_kiro_cli: z.boolean().default(true),
|
|
17
18
|
enable_log_api_request: z.boolean().default(false)
|
|
18
19
|
});
|
|
19
20
|
export const DEFAULT_CONFIG = {
|
|
@@ -28,5 +29,6 @@ export const DEFAULT_CONFIG = {
|
|
|
28
29
|
auth_server_port_start: 19847,
|
|
29
30
|
auth_server_port_range: 10,
|
|
30
31
|
usage_tracking_enabled: true,
|
|
32
|
+
auto_sync_kiro_cli: true,
|
|
31
33
|
enable_log_api_request: false
|
|
32
34
|
};
|
package/dist/plugin/request.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { KiroAuthDetails, PreparedRequest } from './types';
|
|
2
|
-
export declare function transformToCodeWhisperer(url: string, body: any, model: string, auth: KiroAuthDetails, think?: boolean, budget?: number): PreparedRequest;
|
|
2
|
+
export declare function transformToCodeWhisperer(url: string, body: any, model: string, auth: KiroAuthDetails, think?: boolean, budget?: number, reductionFactor?: number): PreparedRequest;
|
|
3
3
|
export declare function mergeAdjacentMessages(msgs: any[]): any[];
|
|
4
4
|
export declare function convertToolsToCodeWhisperer(tools: any[]): any[];
|
package/dist/plugin/request.js
CHANGED
|
@@ -16,23 +16,37 @@ function sanitizeHistory(history) {
|
|
|
16
16
|
continue;
|
|
17
17
|
if (m.assistantResponseMessage?.toolUses) {
|
|
18
18
|
const next = history[i + 1];
|
|
19
|
-
if (next?.userInputMessage?.userInputMessageContext?.toolResults)
|
|
19
|
+
if (next?.userInputMessage?.userInputMessageContext?.toolResults)
|
|
20
20
|
result.push(m);
|
|
21
|
-
}
|
|
22
21
|
}
|
|
23
22
|
else if (m.userInputMessage?.userInputMessageContext?.toolResults) {
|
|
24
23
|
const prev = result[result.length - 1];
|
|
25
|
-
if (prev?.assistantResponseMessage?.toolUses)
|
|
24
|
+
if (prev?.assistantResponseMessage?.toolUses)
|
|
26
25
|
result.push(m);
|
|
27
|
-
}
|
|
28
26
|
}
|
|
29
|
-
else
|
|
27
|
+
else
|
|
30
28
|
result.push(m);
|
|
31
|
-
}
|
|
32
29
|
}
|
|
33
30
|
return result;
|
|
34
31
|
}
|
|
35
|
-
|
|
32
|
+
function findOriginalToolCall(msgs, toolUseId) {
|
|
33
|
+
for (const m of msgs) {
|
|
34
|
+
if (m.role === 'assistant') {
|
|
35
|
+
if (m.tool_calls) {
|
|
36
|
+
for (const tc of m.tool_calls)
|
|
37
|
+
if (tc.id === toolUseId)
|
|
38
|
+
return tc;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(m.content)) {
|
|
41
|
+
for (const p of m.content)
|
|
42
|
+
if (p.type === 'tool_use' && p.id === toolUseId)
|
|
43
|
+
return p;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
export function transformToCodeWhisperer(url, body, model, auth, think = false, budget = 20000, reductionFactor = 1.0) {
|
|
36
50
|
const req = typeof body === 'string' ? JSON.parse(body) : body;
|
|
37
51
|
const { messages, tools, system } = req;
|
|
38
52
|
const convId = crypto.randomUUID();
|
|
@@ -80,6 +94,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
80
94
|
});
|
|
81
95
|
}
|
|
82
96
|
}
|
|
97
|
+
const toolResultLimit = Math.floor(250000 * reductionFactor);
|
|
83
98
|
for (let i = 0; i < msgs.length - 1; i++) {
|
|
84
99
|
const m = msgs[i];
|
|
85
100
|
if (!m)
|
|
@@ -93,7 +108,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
93
108
|
uim.content += p.text || '';
|
|
94
109
|
else if (p.type === 'tool_result')
|
|
95
110
|
trs.push({
|
|
96
|
-
content: [{ text: truncate(getContentText(p.content || p),
|
|
111
|
+
content: [{ text: truncate(getContentText(p.content || p), toolResultLimit) }],
|
|
97
112
|
status: 'success',
|
|
98
113
|
toolUseId: p.tool_use_id
|
|
99
114
|
});
|
|
@@ -120,14 +135,14 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
120
135
|
if (m.tool_results) {
|
|
121
136
|
for (const tr of m.tool_results)
|
|
122
137
|
trs.push({
|
|
123
|
-
content: [{ text: truncate(getContentText(tr),
|
|
138
|
+
content: [{ text: truncate(getContentText(tr), toolResultLimit) }],
|
|
124
139
|
status: 'success',
|
|
125
140
|
toolUseId: tr.tool_call_id
|
|
126
141
|
});
|
|
127
142
|
}
|
|
128
143
|
else {
|
|
129
144
|
trs.push({
|
|
130
|
-
content: [{ text: truncate(getContentText(m),
|
|
145
|
+
content: [{ text: truncate(getContentText(m), toolResultLimit) }],
|
|
131
146
|
status: 'success',
|
|
132
147
|
toolUseId: m.tool_call_id
|
|
133
148
|
});
|
|
@@ -182,7 +197,8 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
182
197
|
}
|
|
183
198
|
history = sanitizeHistory(history);
|
|
184
199
|
let historySize = JSON.stringify(history).length;
|
|
185
|
-
|
|
200
|
+
const historyLimit = Math.floor(850000 * reductionFactor);
|
|
201
|
+
while (historySize > historyLimit && history.length > 2) {
|
|
186
202
|
history.shift();
|
|
187
203
|
while (history.length > 0) {
|
|
188
204
|
const first = history[0];
|
|
@@ -244,14 +260,14 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
244
260
|
if (curMsg.tool_results) {
|
|
245
261
|
for (const tr of curMsg.tool_results)
|
|
246
262
|
curTrs.push({
|
|
247
|
-
content: [{ text: truncate(getContentText(tr),
|
|
263
|
+
content: [{ text: truncate(getContentText(tr), toolResultLimit) }],
|
|
248
264
|
status: 'success',
|
|
249
265
|
toolUseId: tr.tool_call_id
|
|
250
266
|
});
|
|
251
267
|
}
|
|
252
268
|
else {
|
|
253
269
|
curTrs.push({
|
|
254
|
-
content: [{ text: truncate(getContentText(curMsg),
|
|
270
|
+
content: [{ text: truncate(getContentText(curMsg), toolResultLimit) }],
|
|
255
271
|
status: 'success',
|
|
256
272
|
toolUseId: curMsg.tool_call_id
|
|
257
273
|
});
|
|
@@ -263,7 +279,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
263
279
|
curContent += p.text || '';
|
|
264
280
|
else if (p.type === 'tool_result')
|
|
265
281
|
curTrs.push({
|
|
266
|
-
content: [{ text: truncate(getContentText(p.content || p),
|
|
282
|
+
content: [{ text: truncate(getContentText(p.content || p), toolResultLimit) }],
|
|
267
283
|
status: 'success',
|
|
268
284
|
toolUseId: p.tool_use_id
|
|
269
285
|
});
|
|
@@ -292,15 +308,60 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
|
|
|
292
308
|
}
|
|
293
309
|
}
|
|
294
310
|
};
|
|
311
|
+
const toolUsesInHistory = history.flatMap((h) => h.assistantResponseMessage?.toolUses || []);
|
|
312
|
+
const allToolUseIdsInHistory = new Set(toolUsesInHistory.map((tu) => tu.toolUseId));
|
|
313
|
+
const finalCurTrs = [];
|
|
314
|
+
const orphanedTrs = [];
|
|
315
|
+
for (const tr of curTrs) {
|
|
316
|
+
if (allToolUseIdsInHistory.has(tr.toolUseId))
|
|
317
|
+
finalCurTrs.push(tr);
|
|
318
|
+
else {
|
|
319
|
+
const originalCall = findOriginalToolCall(messages, tr.toolUseId);
|
|
320
|
+
if (originalCall) {
|
|
321
|
+
orphanedTrs.push({
|
|
322
|
+
call: {
|
|
323
|
+
name: originalCall.name || originalCall.function?.name || 'tool',
|
|
324
|
+
toolUseId: tr.toolUseId,
|
|
325
|
+
input: originalCall.input ||
|
|
326
|
+
(originalCall.function?.arguments ? JSON.parse(originalCall.function.arguments) : {})
|
|
327
|
+
},
|
|
328
|
+
result: tr
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
curContent += `\n\n[Output for tool call ${tr.toolUseId}]:\n${tr.content?.[0]?.text || ''}`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (orphanedTrs.length > 0) {
|
|
337
|
+
const prev = history[history.length - 1];
|
|
338
|
+
if (prev && !prev.userInputMessage) {
|
|
339
|
+
history.push({
|
|
340
|
+
userInputMessage: {
|
|
341
|
+
content: 'Running tools...',
|
|
342
|
+
modelId: resolved,
|
|
343
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
history.push({
|
|
348
|
+
assistantResponseMessage: {
|
|
349
|
+
content: 'I will execute the following tools.',
|
|
350
|
+
toolUses: orphanedTrs.map((o) => o.call)
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
finalCurTrs.push(...orphanedTrs.map((o) => o.result));
|
|
354
|
+
}
|
|
295
355
|
if (history.length > 0)
|
|
296
356
|
request.conversationState.history = history;
|
|
297
357
|
const uim = request.conversationState.currentMessage.userInputMessage;
|
|
298
358
|
if (uim) {
|
|
359
|
+
uim.content = curContent;
|
|
299
360
|
if (curImgs.length)
|
|
300
361
|
uim.images = curImgs;
|
|
301
362
|
const ctx = {};
|
|
302
|
-
if (
|
|
303
|
-
ctx.toolResults = deduplicateToolResults(
|
|
363
|
+
if (finalCurTrs.length)
|
|
364
|
+
ctx.toolResults = deduplicateToolResults(finalCurTrs);
|
|
304
365
|
if (cwTools.length)
|
|
305
366
|
ctx.tools = cwTools;
|
|
306
367
|
if (Object.keys(ctx).length)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ParsedResponse, ToolCall } from './types';
|
|
2
2
|
export declare function parseEventStream(rawResponse: string): ParsedResponse;
|
|
3
3
|
export declare function parseEventLine(line: string): any | null;
|
|
4
4
|
export declare function parseBracketToolCalls(text: string): ToolCall[];
|
package/dist/plugin/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
|
-
import { getIDCAuthHtml, getSuccessHtml
|
|
2
|
+
import { getErrorHtml, getIDCAuthHtml, getSuccessHtml } from './auth-page';
|
|
3
3
|
import * as logger from './logger';
|
|
4
4
|
async function tryPort(port) {
|
|
5
5
|
return new Promise((resolve) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function migrateJsonToSqlite(): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import * as logger from '../logger';
|
|
5
|
+
import { kiroDb } from './sqlite';
|
|
6
|
+
function getBaseDir() {
|
|
7
|
+
const platform = process.platform;
|
|
8
|
+
if (platform === 'win32') {
|
|
9
|
+
return join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'opencode');
|
|
10
|
+
}
|
|
11
|
+
return join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');
|
|
12
|
+
}
|
|
13
|
+
export async function migrateJsonToSqlite() {
|
|
14
|
+
const base = getBaseDir();
|
|
15
|
+
const accPath = join(base, 'kiro-accounts.json');
|
|
16
|
+
const usePath = join(base, 'kiro-usage.json');
|
|
17
|
+
try {
|
|
18
|
+
const accExists = await fs
|
|
19
|
+
.access(accPath)
|
|
20
|
+
.then(() => true)
|
|
21
|
+
.catch(() => false);
|
|
22
|
+
if (accExists) {
|
|
23
|
+
const data = JSON.parse(await fs.readFile(accPath, 'utf-8'));
|
|
24
|
+
if (data.accounts && Array.isArray(data.accounts)) {
|
|
25
|
+
for (const acc of data.accounts) {
|
|
26
|
+
kiroDb.upsertAccount({
|
|
27
|
+
...acc,
|
|
28
|
+
rateLimitResetTime: acc.rateLimitResetTime || 0,
|
|
29
|
+
isHealthy: acc.isHealthy !== false,
|
|
30
|
+
lastUsed: acc.lastUsed || 0
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
await fs.rename(accPath, accPath + '.bak');
|
|
35
|
+
}
|
|
36
|
+
const useExists = await fs
|
|
37
|
+
.access(usePath)
|
|
38
|
+
.then(() => true)
|
|
39
|
+
.catch(() => false);
|
|
40
|
+
if (useExists) {
|
|
41
|
+
const data = JSON.parse(await fs.readFile(usePath, 'utf-8'));
|
|
42
|
+
if (data.usage && typeof data.usage === 'object') {
|
|
43
|
+
for (const [id, meta] of Object.entries(data.usage)) {
|
|
44
|
+
kiroDb.upsertUsage(id, meta);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await fs.rename(usePath, usePath + '.bak');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
logger.error('Migration failed', e);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UsageMetadata } from '../types';
|
|
2
|
+
export declare const DB_PATH: string;
|
|
3
|
+
export declare class KiroDatabase {
|
|
4
|
+
private db;
|
|
5
|
+
constructor(path?: string);
|
|
6
|
+
private init;
|
|
7
|
+
getAccounts(): any[];
|
|
8
|
+
upsertAccount(acc: any): void;
|
|
9
|
+
deleteAccount(id: string): void;
|
|
10
|
+
getUsage(): Record<string, UsageMetadata>;
|
|
11
|
+
upsertUsage(id: string, meta: UsageMetadata): void;
|
|
12
|
+
getSetting(key: string): string | null;
|
|
13
|
+
setSetting(key: string, value: string): void;
|
|
14
|
+
close(): void;
|
|
15
|
+
}
|
|
16
|
+
export declare const kiroDb: KiroDatabase;
|