@sunerpy/opencode-kiro-auth 0.8.0 → 0.9.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 +4 -0
- package/dist/core/account/account-selector.js +3 -0
- package/dist/plugin/accounts.d.ts +10 -0
- package/dist/plugin/accounts.js +40 -2
- package/dist/plugin/config/schema.d.ts +13 -0
- package/dist/plugin/config/schema.js +11 -0
- package/dist/plugin/storage/locked-operations.js +4 -2
- package/dist/plugin/storage/migrations.js +8 -0
- package/dist/plugin/storage/sqlite.js +8 -5
- package/dist/plugin/types.d.ts +1 -0
- package/dist/plugin/usage.d.ts +16 -3
- package/dist/plugin/usage.js +31 -3
- package/dist/plugin.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,10 @@ multi-account or long-idle setups, enable
|
|
|
108
108
|
(`token_keepalive_enabled: true`) to keep idle accounts' tokens fresh while
|
|
109
109
|
OpenCode is running.
|
|
110
110
|
|
|
111
|
+
Paid-overage protection is on by default; see
|
|
112
|
+
[Overage protection](docs/CONFIGURATION.md#overage-protection) before disabling
|
|
113
|
+
`stop_on_overage`.
|
|
114
|
+
|
|
111
115
|
## Multiple accounts & rotation
|
|
112
116
|
|
|
113
117
|
You can register more than one Kiro account and let the plugin spread
|
|
@@ -26,6 +26,9 @@ export class AccountSelector {
|
|
|
26
26
|
let acc = this.accountManager.getCurrentOrNext();
|
|
27
27
|
if (!acc) {
|
|
28
28
|
this.circuitBreakerTrips++;
|
|
29
|
+
if (this.accountManager.allSelectableBlockedByOverage()) {
|
|
30
|
+
throw new Error('All accounts have exceeded their free quota and entered paid overage. Set "stop_on_overage": false in ~/.config/opencode/kiro.json to continue with paid overage, or wait for the quota to reset.');
|
|
31
|
+
}
|
|
29
32
|
const wait = this.accountManager.getMinWaitTime();
|
|
30
33
|
if (wait > 0 && wait < 30000) {
|
|
31
34
|
if (this.accountManager.shouldShowToast()) {
|
|
@@ -10,24 +10,33 @@ export declare class AccountManager {
|
|
|
10
10
|
private stickyId?;
|
|
11
11
|
private quotaAvoidanceEnabled;
|
|
12
12
|
private quotaReserveThreshold;
|
|
13
|
+
private stopOnOverage;
|
|
14
|
+
private overageThreshold;
|
|
13
15
|
constructor(accounts: ManagedAccount[], strategy?: AccountSelectionStrategy, opts?: {
|
|
14
16
|
quotaAvoidanceEnabled?: boolean;
|
|
15
17
|
quotaReserveThreshold?: number;
|
|
18
|
+
stopOnOverage?: boolean;
|
|
19
|
+
overageThreshold?: number;
|
|
16
20
|
});
|
|
17
21
|
static loadFromDisk(strategy?: AccountSelectionStrategy, opts?: {
|
|
18
22
|
quotaAvoidanceEnabled?: boolean;
|
|
19
23
|
quotaReserveThreshold?: number;
|
|
24
|
+
stopOnOverage?: boolean;
|
|
25
|
+
overageThreshold?: number;
|
|
20
26
|
}): Promise<AccountManager>;
|
|
21
27
|
getAccountCount(): number;
|
|
22
28
|
getAccounts(): ManagedAccount[];
|
|
23
29
|
shouldShowToast(debounce?: number): boolean;
|
|
24
30
|
shouldShowUsageToast(debounce?: number): boolean;
|
|
25
31
|
getMinWaitTime(): number;
|
|
32
|
+
allSelectableBlockedByOverage(): boolean;
|
|
26
33
|
getCurrentOrNext(): ManagedAccount | null;
|
|
27
34
|
updateUsage(id: string, meta: {
|
|
28
35
|
usedCount: number;
|
|
29
36
|
limitCount: number;
|
|
37
|
+
overageCount?: number;
|
|
30
38
|
email?: string;
|
|
39
|
+
lastSync?: number;
|
|
31
40
|
}): void;
|
|
32
41
|
addAccount(a: ManagedAccount): void;
|
|
33
42
|
removeAccount(a: ManagedAccount): void;
|
|
@@ -36,4 +45,5 @@ export declare class AccountManager {
|
|
|
36
45
|
markUnhealthy(a: ManagedAccount, reason: string, recovery?: number): void;
|
|
37
46
|
saveToDisk(): Promise<void>;
|
|
38
47
|
toAuthDetails(a: ManagedAccount): KiroAuthDetails;
|
|
48
|
+
private isOverageBlocked;
|
|
39
49
|
}
|
package/dist/plugin/accounts.js
CHANGED
|
@@ -19,12 +19,16 @@ export class AccountManager {
|
|
|
19
19
|
stickyId;
|
|
20
20
|
quotaAvoidanceEnabled;
|
|
21
21
|
quotaReserveThreshold;
|
|
22
|
+
stopOnOverage;
|
|
23
|
+
overageThreshold;
|
|
22
24
|
constructor(accounts, strategy = 'sticky', opts) {
|
|
23
25
|
this.accounts = accounts;
|
|
24
26
|
this.cursor = 0;
|
|
25
27
|
this.strategy = strategy;
|
|
26
28
|
this.quotaAvoidanceEnabled = opts?.quotaAvoidanceEnabled ?? true;
|
|
27
29
|
this.quotaReserveThreshold = opts?.quotaReserveThreshold ?? 0.95;
|
|
30
|
+
this.stopOnOverage = opts?.stopOnOverage ?? true;
|
|
31
|
+
this.overageThreshold = opts?.overageThreshold ?? 0;
|
|
28
32
|
}
|
|
29
33
|
static async loadFromDisk(strategy, opts) {
|
|
30
34
|
const rows = kiroDb.getAccounts();
|
|
@@ -48,7 +52,9 @@ export class AccountManager {
|
|
|
48
52
|
failCount: r.fail_count || 0,
|
|
49
53
|
lastUsed: r.last_used,
|
|
50
54
|
usedCount: r.used_count,
|
|
51
|
-
limitCount: r.limit_count
|
|
55
|
+
limitCount: r.limit_count,
|
|
56
|
+
overageCount: r.overage_count || 0,
|
|
57
|
+
lastSync: r.last_sync
|
|
52
58
|
}));
|
|
53
59
|
return new AccountManager(accounts, strategy || 'sticky', opts);
|
|
54
60
|
}
|
|
@@ -75,9 +81,31 @@ export class AccountManager {
|
|
|
75
81
|
const waits = this.accounts.map((a) => (a.rateLimitResetTime || 0) - now).filter((t) => t > 0);
|
|
76
82
|
return waits.length > 0 ? Math.min(...waits) : 0;
|
|
77
83
|
}
|
|
84
|
+
allSelectableBlockedByOverage() {
|
|
85
|
+
if (!this.stopOnOverage)
|
|
86
|
+
return false;
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const healthEligible = (a) => {
|
|
89
|
+
if (isPermanentError(a.unhealthyReason))
|
|
90
|
+
return false;
|
|
91
|
+
if (a.isHealthy)
|
|
92
|
+
return true;
|
|
93
|
+
if (isAccessTokenError(a.unhealthyReason))
|
|
94
|
+
return true;
|
|
95
|
+
return a.failCount < 10;
|
|
96
|
+
};
|
|
97
|
+
const rateLimited = (a) => !!(a.rateLimitResetTime && now < a.rateLimitResetTime);
|
|
98
|
+
const hasOverageBlockedEligible = this.accounts.some((a) => healthEligible(a) && this.isOverageBlocked(a));
|
|
99
|
+
const hasCleanRateLimited = this.accounts.some((a) => healthEligible(a) && rateLimited(a) && !this.isOverageBlocked(a));
|
|
100
|
+
const hasHealthySelectable = this.accounts.some((a) => a.isHealthy && !rateLimited(a) && !this.isOverageBlocked(a));
|
|
101
|
+
return hasOverageBlockedEligible && !hasCleanRateLimited && !hasHealthySelectable;
|
|
102
|
+
}
|
|
78
103
|
getCurrentOrNext() {
|
|
79
104
|
const now = Date.now();
|
|
105
|
+
const overageBlocked = (a) => this.isOverageBlocked(a);
|
|
80
106
|
const available = this.accounts.filter((a) => {
|
|
107
|
+
if (overageBlocked(a))
|
|
108
|
+
return false;
|
|
81
109
|
if (!a.isHealthy) {
|
|
82
110
|
if (isPermanentError(a.unhealthyReason)) {
|
|
83
111
|
return false;
|
|
@@ -129,7 +157,10 @@ export class AccountManager {
|
|
|
129
157
|
}
|
|
130
158
|
if (!selected) {
|
|
131
159
|
const fallback = this.accounts
|
|
132
|
-
.filter((a) => !a.isHealthy &&
|
|
160
|
+
.filter((a) => !a.isHealthy &&
|
|
161
|
+
a.failCount < 10 &&
|
|
162
|
+
!isPermanentError(a.unhealthyReason) &&
|
|
163
|
+
!overageBlocked(a))
|
|
133
164
|
.sort((a, b) => (a.usedCount || 0) - (b.usedCount || 0) || (a.lastUsed || 0) - (b.lastUsed || 0))[0];
|
|
134
165
|
if (fallback) {
|
|
135
166
|
fallback.isHealthy = true;
|
|
@@ -139,6 +170,8 @@ export class AccountManager {
|
|
|
139
170
|
}
|
|
140
171
|
}
|
|
141
172
|
if (selected) {
|
|
173
|
+
if (overageBlocked(selected))
|
|
174
|
+
return null;
|
|
142
175
|
selected.lastUsed = now;
|
|
143
176
|
selected.usedCount = (selected.usedCount || 0) + 1;
|
|
144
177
|
return selected;
|
|
@@ -150,6 +183,8 @@ export class AccountManager {
|
|
|
150
183
|
if (a) {
|
|
151
184
|
a.usedCount = meta.usedCount;
|
|
152
185
|
a.limitCount = meta.limitCount;
|
|
186
|
+
a.overageCount = meta.overageCount ?? 0;
|
|
187
|
+
a.lastSync = meta.lastSync ?? a.lastSync;
|
|
153
188
|
if (meta.email)
|
|
154
189
|
a.email = meta.email;
|
|
155
190
|
if (!isPermanentError(a.unhealthyReason)) {
|
|
@@ -290,4 +325,7 @@ export class AccountManager {
|
|
|
290
325
|
email: a.email
|
|
291
326
|
};
|
|
292
327
|
}
|
|
328
|
+
isOverageBlocked(a) {
|
|
329
|
+
return this.stopOnOverage && (a.overageCount ?? 0) > this.overageThreshold;
|
|
330
|
+
}
|
|
293
331
|
}
|
|
@@ -32,6 +32,15 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
32
32
|
* near-full and softly avoided. Default 0.95 (95%).
|
|
33
33
|
*/
|
|
34
34
|
quota_reserve_threshold: z.ZodDefault<z.ZodNumber>;
|
|
35
|
+
/**
|
|
36
|
+
* Exclude accounts that have entered AWS paid overage from selection.
|
|
37
|
+
*/
|
|
38
|
+
stop_on_overage: z.ZodDefault<z.ZodBoolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Paid-overage invocations tolerated before stopping an account. 0 means
|
|
41
|
+
* stop on any overage.
|
|
42
|
+
*/
|
|
43
|
+
overage_threshold: z.ZodDefault<z.ZodNumber>;
|
|
35
44
|
default_region: z.ZodDefault<z.ZodEnum<["us-east-1", "us-east-2", "us-west-1", "us-west-2", "af-south-1", "ap-east-1", "ap-south-2", "ap-southeast-3", "ap-southeast-5", "ap-southeast-4", "ap-south-1", "ap-southeast-6", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-east-2", "ap-southeast-7", "ap-northeast-1", "ca-central-1", "ca-west-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", "eu-south-2", "eu-north-1", "eu-central-2", "il-central-1", "mx-central-1", "me-south-1", "me-central-1", "sa-east-1"]>>;
|
|
36
45
|
rate_limit_retry_delay_ms: z.ZodDefault<z.ZodNumber>;
|
|
37
46
|
rate_limit_max_retries: z.ZodDefault<z.ZodNumber>;
|
|
@@ -77,6 +86,8 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
77
86
|
account_selection_strategy: "sticky" | "round-robin" | "lowest-usage";
|
|
78
87
|
quota_avoidance_enabled: boolean;
|
|
79
88
|
quota_reserve_threshold: number;
|
|
89
|
+
stop_on_overage: boolean;
|
|
90
|
+
overage_threshold: number;
|
|
80
91
|
default_region: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "af-south-1" | "ap-east-1" | "ap-south-2" | "ap-southeast-3" | "ap-southeast-5" | "ap-southeast-4" | "ap-south-1" | "ap-southeast-6" | "ap-northeast-3" | "ap-northeast-2" | "ap-southeast-1" | "ap-southeast-2" | "ap-east-2" | "ap-southeast-7" | "ap-northeast-1" | "ca-central-1" | "ca-west-1" | "eu-central-1" | "eu-west-1" | "eu-west-2" | "eu-south-1" | "eu-west-3" | "eu-south-2" | "eu-north-1" | "eu-central-2" | "il-central-1" | "mx-central-1" | "me-south-1" | "me-central-1" | "sa-east-1";
|
|
81
92
|
rate_limit_retry_delay_ms: number;
|
|
82
93
|
rate_limit_max_retries: number;
|
|
@@ -106,6 +117,8 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
106
117
|
account_selection_strategy?: "sticky" | "round-robin" | "lowest-usage" | undefined;
|
|
107
118
|
quota_avoidance_enabled?: boolean | undefined;
|
|
108
119
|
quota_reserve_threshold?: number | undefined;
|
|
120
|
+
stop_on_overage?: boolean | undefined;
|
|
121
|
+
overage_threshold?: number | undefined;
|
|
109
122
|
default_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "af-south-1" | "ap-east-1" | "ap-south-2" | "ap-southeast-3" | "ap-southeast-5" | "ap-southeast-4" | "ap-south-1" | "ap-southeast-6" | "ap-northeast-3" | "ap-northeast-2" | "ap-southeast-1" | "ap-southeast-2" | "ap-east-2" | "ap-southeast-7" | "ap-northeast-1" | "ca-central-1" | "ca-west-1" | "eu-central-1" | "eu-west-1" | "eu-west-2" | "eu-south-1" | "eu-west-3" | "eu-south-2" | "eu-north-1" | "eu-central-2" | "il-central-1" | "mx-central-1" | "me-south-1" | "me-central-1" | "sa-east-1" | undefined;
|
|
110
123
|
rate_limit_retry_delay_ms?: number | undefined;
|
|
111
124
|
rate_limit_max_retries?: number | undefined;
|
|
@@ -64,6 +64,15 @@ export const KiroConfigSchema = z.object({
|
|
|
64
64
|
* near-full and softly avoided. Default 0.95 (95%).
|
|
65
65
|
*/
|
|
66
66
|
quota_reserve_threshold: z.number().min(0).max(1).default(0.95),
|
|
67
|
+
/**
|
|
68
|
+
* Exclude accounts that have entered AWS paid overage from selection.
|
|
69
|
+
*/
|
|
70
|
+
stop_on_overage: z.boolean().default(true),
|
|
71
|
+
/**
|
|
72
|
+
* Paid-overage invocations tolerated before stopping an account. 0 means
|
|
73
|
+
* stop on any overage.
|
|
74
|
+
*/
|
|
75
|
+
overage_threshold: z.number().min(0).default(0),
|
|
67
76
|
default_region: RegionSchema.default('us-east-1'),
|
|
68
77
|
rate_limit_retry_delay_ms: z.number().min(1000).max(60000).default(5000),
|
|
69
78
|
rate_limit_max_retries: z.number().min(0).max(10).default(3),
|
|
@@ -110,6 +119,8 @@ export const DEFAULT_CONFIG = {
|
|
|
110
119
|
account_selection_strategy: 'lowest-usage',
|
|
111
120
|
quota_avoidance_enabled: true,
|
|
112
121
|
quota_reserve_threshold: 0.95,
|
|
122
|
+
stop_on_overage: true,
|
|
123
|
+
overage_threshold: 0,
|
|
113
124
|
default_region: 'us-east-1',
|
|
114
125
|
rate_limit_retry_delay_ms: 5000,
|
|
115
126
|
rate_limit_max_retries: 3,
|
|
@@ -139,6 +139,7 @@ export function mergeAccounts(existing, incoming) {
|
|
|
139
139
|
// in-memory token triple from another process must not clobber a newer
|
|
140
140
|
// persisted token triple during this cross-process merge.
|
|
141
141
|
const tokenWinner = (acc.expiresAt || 0) >= (existingAcc.expiresAt || 0) ? acc : existingAcc;
|
|
142
|
+
const usageWinner = (acc.lastSync || 0) >= (existingAcc.lastSync || 0) ? acc : existingAcc;
|
|
142
143
|
accountMap.set(acc.id, {
|
|
143
144
|
...existingAcc,
|
|
144
145
|
...acc,
|
|
@@ -146,8 +147,9 @@ export function mergeAccounts(existing, incoming) {
|
|
|
146
147
|
accessToken: tokenWinner.accessToken,
|
|
147
148
|
expiresAt: tokenWinner.expiresAt,
|
|
148
149
|
lastUsed: Math.max(existingAcc.lastUsed || 0, acc.lastUsed || 0),
|
|
149
|
-
usedCount:
|
|
150
|
-
limitCount:
|
|
150
|
+
usedCount: usageWinner.usedCount ?? 0,
|
|
151
|
+
limitCount: usageWinner.limitCount ?? 0,
|
|
152
|
+
overageCount: usageWinner.overageCount ?? 0,
|
|
151
153
|
rateLimitResetTime: Math.max(existingAcc.rateLimitResetTime || 0, acc.rateLimitResetTime || 0),
|
|
152
154
|
isHealthy: incomingRecovered
|
|
153
155
|
? true
|
|
@@ -6,6 +6,7 @@ export function runMigrations(db) {
|
|
|
6
6
|
migrateOidcRegionColumn(db);
|
|
7
7
|
migrateDropRefreshTokenUniqueIndex(db);
|
|
8
8
|
migrateRemovedAccountsTable(db);
|
|
9
|
+
migrateOverageColumn(db);
|
|
9
10
|
}
|
|
10
11
|
function migrateToUniqueRefreshToken(db) {
|
|
11
12
|
const hasIndex = db
|
|
@@ -159,3 +160,10 @@ function migrateRemovedAccountsTable(db) {
|
|
|
159
160
|
// so kiro-cli auto-sync does not silently re-import them. Cleared only on deliberate login.
|
|
160
161
|
db.exec('CREATE TABLE IF NOT EXISTS removed_accounts (id TEXT PRIMARY KEY, removed_at INTEGER NOT NULL)');
|
|
161
162
|
}
|
|
163
|
+
function migrateOverageColumn(db) {
|
|
164
|
+
const columns = db.prepare('PRAGMA table_info(accounts)').all();
|
|
165
|
+
const names = new Set(columns.map((c) => c.name));
|
|
166
|
+
if (!names.has('overage_count')) {
|
|
167
|
+
db.exec('ALTER TABLE accounts ADD COLUMN overage_count INTEGER DEFAULT 0');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -33,7 +33,8 @@ export class KiroDatabase {
|
|
|
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,
|
|
35
35
|
recovery_time INTEGER, fail_count INTEGER DEFAULT 0, last_used INTEGER DEFAULT 0,
|
|
36
|
-
used_count INTEGER DEFAULT 0, limit_count INTEGER DEFAULT 0,
|
|
36
|
+
used_count INTEGER DEFAULT 0, limit_count INTEGER DEFAULT 0,
|
|
37
|
+
overage_count INTEGER DEFAULT 0, last_sync INTEGER DEFAULT 0
|
|
37
38
|
)
|
|
38
39
|
`);
|
|
39
40
|
runMigrations(this.db);
|
|
@@ -56,8 +57,8 @@ export class KiroDatabase {
|
|
|
56
57
|
id, email, auth_method, region, oidc_region, client_id, client_secret,
|
|
57
58
|
profile_arn, start_url, refresh_token, access_token, expires_at, rate_limit_reset,
|
|
58
59
|
is_healthy, unhealthy_reason, recovery_time, fail_count, last_used,
|
|
59
|
-
used_count, limit_count, last_sync
|
|
60
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
60
|
+
used_count, limit_count, overage_count, last_sync
|
|
61
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
61
62
|
ON CONFLICT(id) DO UPDATE SET
|
|
62
63
|
id=excluded.id, email=excluded.email, auth_method=excluded.auth_method,
|
|
63
64
|
region=excluded.region, oidc_region=excluded.oidc_region, client_id=excluded.client_id, client_secret=excluded.client_secret,
|
|
@@ -66,9 +67,10 @@ export class KiroDatabase {
|
|
|
66
67
|
rate_limit_reset=excluded.rate_limit_reset, is_healthy=excluded.is_healthy,
|
|
67
68
|
unhealthy_reason=excluded.unhealthy_reason, recovery_time=excluded.recovery_time,
|
|
68
69
|
fail_count=excluded.fail_count, last_used=excluded.last_used,
|
|
69
|
-
used_count=excluded.used_count, limit_count=excluded.limit_count,
|
|
70
|
+
used_count=excluded.used_count, limit_count=excluded.limit_count,
|
|
71
|
+
overage_count=excluded.overage_count, last_sync=excluded.last_sync
|
|
70
72
|
`)
|
|
71
|
-
.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);
|
|
73
|
+
.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.overageCount || 0, acc.lastSync || 0);
|
|
72
74
|
}
|
|
73
75
|
isRemovedSync(id) {
|
|
74
76
|
return !!this.db.prepare('SELECT id FROM removed_accounts WHERE id = ?').get(id);
|
|
@@ -233,6 +235,7 @@ export class KiroDatabase {
|
|
|
233
235
|
lastUsed: row.last_used,
|
|
234
236
|
usedCount: row.used_count,
|
|
235
237
|
limitCount: row.limit_count,
|
|
238
|
+
overageCount: row.overage_count || 0,
|
|
236
239
|
lastSync: row.last_sync
|
|
237
240
|
};
|
|
238
241
|
}
|
package/dist/plugin/types.d.ts
CHANGED
package/dist/plugin/usage.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
import { KiroAuthDetails, ManagedAccount } from './types.js';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { KiroAuthDetails, ManagedAccount } from './types.js';
|
|
2
|
+
interface UsageSnapshot {
|
|
3
|
+
usedCount: number;
|
|
4
|
+
limitCount: number;
|
|
5
|
+
overageCount: number;
|
|
6
|
+
email?: string;
|
|
7
|
+
}
|
|
8
|
+
interface UsageUpdateMeta extends UsageSnapshot {
|
|
9
|
+
lastSync: number;
|
|
10
|
+
}
|
|
11
|
+
interface AccountUsageManager {
|
|
12
|
+
updateUsage(id: string, meta: UsageUpdateMeta): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function fetchUsageLimits(auth: KiroAuthDetails): Promise<UsageSnapshot>;
|
|
15
|
+
export declare function updateAccountQuota(account: ManagedAccount, usage: Partial<UsageSnapshot>, accountManager?: AccountUsageManager): void;
|
|
16
|
+
export {};
|
package/dist/plugin/usage.js
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const UsageLimitsResponseSchema = z
|
|
3
|
+
.object({
|
|
4
|
+
usageBreakdownList: z
|
|
5
|
+
.array(z
|
|
6
|
+
.object({
|
|
7
|
+
currentUsage: z.number().optional(),
|
|
8
|
+
usageLimit: z.number().optional(),
|
|
9
|
+
currentOverages: z.number().optional(),
|
|
10
|
+
freeTrialInfo: z
|
|
11
|
+
.object({
|
|
12
|
+
currentUsage: z.number().optional(),
|
|
13
|
+
usageLimit: z.number().optional()
|
|
14
|
+
})
|
|
15
|
+
.passthrough()
|
|
16
|
+
.nullable()
|
|
17
|
+
.optional()
|
|
18
|
+
})
|
|
19
|
+
.passthrough())
|
|
20
|
+
.optional(),
|
|
21
|
+
userInfo: z.object({ email: z.string().optional() }).passthrough().optional()
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
1
24
|
export async function fetchUsageLimits(auth) {
|
|
2
25
|
// Try different parameter combinations
|
|
3
26
|
const attempts = [
|
|
@@ -42,8 +65,8 @@ export async function fetchUsageLimits(auth) {
|
|
|
42
65
|
lastError = new Error(`Status: ${res.status}${errType ? ` (${errType})` : ''}${requestId ? ` [${requestId}]` : ''}: ${msg}`);
|
|
43
66
|
continue;
|
|
44
67
|
}
|
|
45
|
-
const data = await res.json();
|
|
46
|
-
let usedCount = 0, limitCount = 0;
|
|
68
|
+
const data = UsageLimitsResponseSchema.parse(await res.json());
|
|
69
|
+
let usedCount = 0, limitCount = 0, overageCount = 0;
|
|
47
70
|
if (Array.isArray(data.usageBreakdownList)) {
|
|
48
71
|
for (const s of data.usageBreakdownList) {
|
|
49
72
|
if (s.freeTrialInfo) {
|
|
@@ -52,9 +75,10 @@ export async function fetchUsageLimits(auth) {
|
|
|
52
75
|
}
|
|
53
76
|
usedCount += s.currentUsage || 0;
|
|
54
77
|
limitCount += s.usageLimit || 0;
|
|
78
|
+
overageCount += s.currentOverages || 0;
|
|
55
79
|
}
|
|
56
80
|
}
|
|
57
|
-
return { usedCount, limitCount, email: data.userInfo?.email };
|
|
81
|
+
return { usedCount, limitCount, overageCount, email: data.userInfo?.email };
|
|
58
82
|
}
|
|
59
83
|
catch (e) {
|
|
60
84
|
lastError = e instanceof Error ? e : new Error(String(e));
|
|
@@ -68,10 +92,14 @@ export function updateAccountQuota(account, usage, accountManager) {
|
|
|
68
92
|
const meta = {
|
|
69
93
|
usedCount: usage.usedCount || 0,
|
|
70
94
|
limitCount: usage.limitCount || 0,
|
|
95
|
+
overageCount: usage.overageCount || 0,
|
|
96
|
+
lastSync: Date.now(),
|
|
71
97
|
email: usage.email
|
|
72
98
|
};
|
|
73
99
|
account.usedCount = meta.usedCount;
|
|
74
100
|
account.limitCount = meta.limitCount;
|
|
101
|
+
account.overageCount = meta.overageCount;
|
|
102
|
+
account.lastSync = meta.lastSync;
|
|
75
103
|
if (usage.email)
|
|
76
104
|
account.email = usage.email;
|
|
77
105
|
if (accountManager)
|
package/dist/plugin.js
CHANGED
|
@@ -43,7 +43,9 @@ export const createKiroPlugin = (id) => async ({ client, directory }) => {
|
|
|
43
43
|
const authHandler = new AuthHandler(config, repository);
|
|
44
44
|
const accountManager = await AccountManager.loadFromDisk(config.account_selection_strategy, {
|
|
45
45
|
quotaAvoidanceEnabled: config.quota_avoidance_enabled,
|
|
46
|
-
quotaReserveThreshold: config.quota_reserve_threshold
|
|
46
|
+
quotaReserveThreshold: config.quota_reserve_threshold,
|
|
47
|
+
stopOnOverage: config.stop_on_overage,
|
|
48
|
+
overageThreshold: config.overage_threshold
|
|
47
49
|
});
|
|
48
50
|
authHandler.setAccountManager(accountManager);
|
|
49
51
|
const requestHandler = new RequestHandler(accountManager, config, repository, client);
|