@sunerpy/opencode-kiro-auth 0.13.5 → 0.13.7
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.
|
@@ -75,12 +75,26 @@ export class RequestHandler {
|
|
|
75
75
|
else
|
|
76
76
|
inboundSignal?.addEventListener('abort', abortFromInbound, { once: true });
|
|
77
77
|
let timeout;
|
|
78
|
-
const beginUpstreamWait = (phase, timeoutMs) => {
|
|
78
|
+
const beginUpstreamWait = (phase, timeoutMs, details = {}) => {
|
|
79
79
|
if (requestController.signal.aborted)
|
|
80
80
|
return;
|
|
81
81
|
if (timeout)
|
|
82
82
|
clearDeadlineTimeout(timeout);
|
|
83
|
-
|
|
83
|
+
if (timeoutMs <= 0) {
|
|
84
|
+
timeout = undefined;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const startedAt = Date.now();
|
|
88
|
+
timeout = setDeadlineTimeout(() => {
|
|
89
|
+
logger.warn('Kiro upstream wait timed out', {
|
|
90
|
+
phase,
|
|
91
|
+
configuredTimeoutMs: timeoutMs,
|
|
92
|
+
elapsedMs: Date.now() - startedAt,
|
|
93
|
+
platform: process.platform,
|
|
94
|
+
...details
|
|
95
|
+
});
|
|
96
|
+
requestController.abort(new DOMException(`Kiro request timed out waiting for ${phase}`, 'TimeoutError'));
|
|
97
|
+
}, timeoutMs);
|
|
84
98
|
};
|
|
85
99
|
const endUpstreamWait = () => {
|
|
86
100
|
if (!timeout)
|
|
@@ -201,7 +215,17 @@ export class RequestHandler {
|
|
|
201
215
|
await this.usageTracker.syncUsage(acc, auth, isCurrentAttempt);
|
|
202
216
|
};
|
|
203
217
|
let sdkResponse;
|
|
204
|
-
|
|
218
|
+
if (this.config.sdk_response_timeout_enabled) {
|
|
219
|
+
const messageContext = sdkPrep.conversationState.currentMessage?.userInputMessage?.userInputMessageContext;
|
|
220
|
+
beginUpstreamWait('SDK response', this.config.sdk_response_timeout_ms, {
|
|
221
|
+
model,
|
|
222
|
+
effectiveModel: sdkPrep.effectiveModel,
|
|
223
|
+
effort: sdkPrep.effort,
|
|
224
|
+
region: sdkPrep.region,
|
|
225
|
+
historyLength: sdkPrep.conversationState.history?.length ?? 0,
|
|
226
|
+
toolCount: messageContext?.tools?.length ?? 0
|
|
227
|
+
});
|
|
228
|
+
}
|
|
205
229
|
try {
|
|
206
230
|
sdkResponse = await client.send(command, { abortSignal: signal });
|
|
207
231
|
}
|
|
@@ -214,7 +238,11 @@ export class RequestHandler {
|
|
|
214
238
|
}
|
|
215
239
|
const response = await this.responseHandler.handleSdkSuccess(sdkResponse, model, sdkPrep.conversationId, sdkPrep.streaming, {
|
|
216
240
|
signal,
|
|
217
|
-
onUpstreamWaitStart: () => beginUpstreamWait('stream event', this.config.request_timeout_ms
|
|
241
|
+
onUpstreamWaitStart: () => beginUpstreamWait('stream event', this.config.request_timeout_ms, {
|
|
242
|
+
model,
|
|
243
|
+
effectiveModel: sdkPrep.effectiveModel,
|
|
244
|
+
region: sdkPrep.region
|
|
245
|
+
}),
|
|
218
246
|
onUpstreamWaitEnd: endUpstreamWait,
|
|
219
247
|
onComplete: completeRequest,
|
|
220
248
|
onTerminal: cleanupRequest,
|
|
@@ -135,6 +135,7 @@ function applyEnvOverrides(config) {
|
|
|
135
135
|
rate_limit_retry_delay_ms: parseNumberEnv(env.KIRO_RATE_LIMIT_RETRY_DELAY_MS, config.rate_limit_retry_delay_ms),
|
|
136
136
|
rate_limit_max_retries: parseNumberEnv(env.KIRO_RATE_LIMIT_MAX_RETRIES, config.rate_limit_max_retries),
|
|
137
137
|
max_request_iterations: parseNumberEnv(env.KIRO_MAX_REQUEST_ITERATIONS, config.max_request_iterations),
|
|
138
|
+
sdk_response_timeout_enabled: parseBooleanEnv(env.KIRO_SDK_RESPONSE_TIMEOUT_ENABLED, config.sdk_response_timeout_enabled),
|
|
138
139
|
sdk_response_timeout_ms: parseNumberEnv(env.KIRO_SDK_RESPONSE_TIMEOUT_MS, config.sdk_response_timeout_ms),
|
|
139
140
|
request_timeout_ms: parseNumberEnv(env.KIRO_REQUEST_TIMEOUT_MS, config.request_timeout_ms),
|
|
140
141
|
token_expiry_buffer_ms: parseNumberEnv(env.KIRO_TOKEN_EXPIRY_BUFFER_MS, config.token_expiry_buffer_ms),
|
|
@@ -55,10 +55,15 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
55
55
|
rate_limit_retry_delay_ms: z.ZodDefault<z.ZodNumber>;
|
|
56
56
|
rate_limit_max_retries: z.ZodDefault<z.ZodNumber>;
|
|
57
57
|
max_request_iterations: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
/**
|
|
59
|
+
* Opt into a fixed deadline while waiting for client.send() to return.
|
|
60
|
+
* Disabled by default because a pending request is ambiguous: Kiro may
|
|
61
|
+
* still be performing a valid long-running generation.
|
|
62
|
+
*/
|
|
63
|
+
sdk_response_timeout_enabled: z.ZodDefault<z.ZodBoolean>;
|
|
58
64
|
/**
|
|
59
65
|
* Maximum wait for client.send() to return the initial SDK response.
|
|
60
|
-
*
|
|
61
|
-
* event stream becomes available.
|
|
66
|
+
* Only used when sdk_response_timeout_enabled is true.
|
|
62
67
|
*/
|
|
63
68
|
sdk_response_timeout_ms: z.ZodDefault<z.ZodNumber>;
|
|
64
69
|
/**
|
|
@@ -113,6 +118,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
113
118
|
rate_limit_retry_delay_ms: number;
|
|
114
119
|
rate_limit_max_retries: number;
|
|
115
120
|
max_request_iterations: number;
|
|
121
|
+
sdk_response_timeout_enabled: boolean;
|
|
116
122
|
sdk_response_timeout_ms: number;
|
|
117
123
|
request_timeout_ms: number;
|
|
118
124
|
token_expiry_buffer_ms: number;
|
|
@@ -147,6 +153,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
|
|
|
147
153
|
rate_limit_retry_delay_ms?: number | undefined;
|
|
148
154
|
rate_limit_max_retries?: number | undefined;
|
|
149
155
|
max_request_iterations?: number | undefined;
|
|
156
|
+
sdk_response_timeout_enabled?: boolean | undefined;
|
|
150
157
|
sdk_response_timeout_ms?: number | undefined;
|
|
151
158
|
request_timeout_ms?: number | undefined;
|
|
152
159
|
token_expiry_buffer_ms?: number | undefined;
|
|
@@ -87,10 +87,15 @@ export const KiroConfigSchema = z.object({
|
|
|
87
87
|
rate_limit_retry_delay_ms: z.number().min(1000).max(60000).default(5000),
|
|
88
88
|
rate_limit_max_retries: z.number().min(0).max(10).default(3),
|
|
89
89
|
max_request_iterations: z.number().min(5).max(1000).default(20),
|
|
90
|
+
/**
|
|
91
|
+
* Opt into a fixed deadline while waiting for client.send() to return.
|
|
92
|
+
* Disabled by default because a pending request is ambiguous: Kiro may
|
|
93
|
+
* still be performing a valid long-running generation.
|
|
94
|
+
*/
|
|
95
|
+
sdk_response_timeout_enabled: z.boolean().default(false),
|
|
90
96
|
/**
|
|
91
97
|
* Maximum wait for client.send() to return the initial SDK response.
|
|
92
|
-
*
|
|
93
|
-
* event stream becomes available.
|
|
98
|
+
* Only used when sdk_response_timeout_enabled is true.
|
|
94
99
|
*/
|
|
95
100
|
sdk_response_timeout_ms: z.number().min(30000).max(600000).default(300000),
|
|
96
101
|
/**
|
|
@@ -146,6 +151,7 @@ export const DEFAULT_CONFIG = {
|
|
|
146
151
|
rate_limit_retry_delay_ms: 5000,
|
|
147
152
|
rate_limit_max_retries: 3,
|
|
148
153
|
max_request_iterations: 20,
|
|
154
|
+
sdk_response_timeout_enabled: false,
|
|
149
155
|
sdk_response_timeout_ms: 300000,
|
|
150
156
|
request_timeout_ms: 120000,
|
|
151
157
|
token_expiry_buffer_ms: 300000,
|
|
@@ -5,16 +5,14 @@ import lockfile from 'proper-lockfile';
|
|
|
5
5
|
import { isPermanentError } from '../health.js';
|
|
6
6
|
import { getKeepAliveLockPath, getRefreshLockPath } from '../paths.js';
|
|
7
7
|
export { getKeepAliveLockPath, getRefreshLockPath } from '../paths.js';
|
|
8
|
-
const
|
|
8
|
+
const DATABASE_LOCK_OPTIONS = {
|
|
9
9
|
stale: 10000,
|
|
10
|
-
retries:
|
|
11
|
-
retries: 5,
|
|
12
|
-
minTimeout: 100,
|
|
13
|
-
maxTimeout: 1000,
|
|
14
|
-
factor: 2
|
|
15
|
-
},
|
|
10
|
+
retries: 0,
|
|
16
11
|
realpath: false
|
|
17
12
|
};
|
|
13
|
+
const DATABASE_LOCK_DEADLINE_MS = 10000;
|
|
14
|
+
const DATABASE_LOCK_MIN_BACKOFF_MS = 25;
|
|
15
|
+
const DATABASE_LOCK_MAX_BACKOFF_MS = 250;
|
|
18
16
|
const REFRESH_LOCK_OPTIONS = {
|
|
19
17
|
stale: 15000,
|
|
20
18
|
retries: {
|
|
@@ -38,6 +36,29 @@ function blockingBackoff(ms) {
|
|
|
38
36
|
function isLockContention(e) {
|
|
39
37
|
return typeof e === 'object' && e !== null && 'code' in e && e.code === 'ELOCKED';
|
|
40
38
|
}
|
|
39
|
+
function asyncBackoff(attempt, remainingMs) {
|
|
40
|
+
const ceiling = Math.min(DATABASE_LOCK_MIN_BACKOFF_MS * 2 ** Math.min(attempt, 4), DATABASE_LOCK_MAX_BACKOFF_MS, remainingMs);
|
|
41
|
+
const floor = Math.max(1, Math.floor(ceiling / 2));
|
|
42
|
+
const delay = floor + Math.floor(Math.random() * (ceiling - floor + 1));
|
|
43
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
44
|
+
}
|
|
45
|
+
async function acquireDatabaseLock(dbPath) {
|
|
46
|
+
// A deadline avoids fixed retry-count starvation; jitter keeps contenders
|
|
47
|
+
// from repeatedly attempting the atomic mkdir in lockstep.
|
|
48
|
+
const deadline = Date.now() + DATABASE_LOCK_DEADLINE_MS;
|
|
49
|
+
let attempt = 0;
|
|
50
|
+
for (;;) {
|
|
51
|
+
try {
|
|
52
|
+
return await lockfile.lock(dbPath, DATABASE_LOCK_OPTIONS);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const remainingMs = deadline - Date.now();
|
|
56
|
+
if (!isLockContention(e) || remainingMs <= 0)
|
|
57
|
+
throw e;
|
|
58
|
+
await asyncBackoff(attempt++, remainingMs);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
41
62
|
export function withDatabaseLockSync(dbPath, fn) {
|
|
42
63
|
if (!existsSync(dbPath)) {
|
|
43
64
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
@@ -73,13 +94,12 @@ export function withDatabaseLockSync(dbPath, fn) {
|
|
|
73
94
|
}
|
|
74
95
|
export async function withDatabaseLock(dbPath, fn) {
|
|
75
96
|
if (!existsSync(dbPath)) {
|
|
76
|
-
|
|
77
|
-
await fs.mkdir(dir, { recursive: true });
|
|
97
|
+
await fs.mkdir(dirname(dbPath), { recursive: true });
|
|
78
98
|
await fs.writeFile(dbPath, '');
|
|
79
99
|
}
|
|
80
100
|
let release = null;
|
|
81
101
|
try {
|
|
82
|
-
release = await
|
|
102
|
+
release = await acquireDatabaseLock(dbPath);
|
|
83
103
|
return await fn();
|
|
84
104
|
}
|
|
85
105
|
finally {
|