@link-assistant/agent 0.13.0 → 0.13.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.
- package/package.json +1 -1
- package/src/bun/index.ts +73 -7
package/package.json
CHANGED
package/src/bun/index.ts
CHANGED
|
@@ -13,26 +13,69 @@ export namespace BunProc {
|
|
|
13
13
|
// Lock key for serializing package installations to prevent race conditions
|
|
14
14
|
const INSTALL_LOCK_KEY = 'bun-install';
|
|
15
15
|
|
|
16
|
+
// Default timeout for subprocess commands (2 minutes)
|
|
17
|
+
// This prevents indefinite hangs from known Bun issues:
|
|
18
|
+
// - HTTP 304 response handling (https://github.com/oven-sh/bun/issues/5831)
|
|
19
|
+
// - Failed dependency fetch (https://github.com/oven-sh/bun/issues/26341)
|
|
20
|
+
// - IPv6 configuration issues
|
|
21
|
+
const DEFAULT_TIMEOUT_MS = 120000;
|
|
22
|
+
|
|
23
|
+
// Timeout specifically for package installation (60 seconds)
|
|
24
|
+
// Package installations should complete within this time for typical packages
|
|
25
|
+
const INSTALL_TIMEOUT_MS = 60000;
|
|
26
|
+
|
|
27
|
+
export const TimeoutError = NamedError.create(
|
|
28
|
+
'BunTimeoutError',
|
|
29
|
+
z.object({
|
|
30
|
+
cmd: z.array(z.string()),
|
|
31
|
+
timeoutMs: z.number(),
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
16
35
|
export async function run(
|
|
17
36
|
cmd: string[],
|
|
18
|
-
options?: Bun.SpawnOptions.OptionsObject<any, any, any>
|
|
37
|
+
options?: Bun.SpawnOptions.OptionsObject<any, any, any> & {
|
|
38
|
+
timeout?: number;
|
|
39
|
+
}
|
|
19
40
|
) {
|
|
41
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
42
|
+
|
|
20
43
|
log.info(() => ({
|
|
21
44
|
message: 'running',
|
|
22
45
|
cmd: [which(), ...cmd],
|
|
23
|
-
|
|
46
|
+
timeout,
|
|
47
|
+
cwd: options?.cwd,
|
|
24
48
|
}));
|
|
49
|
+
|
|
25
50
|
const result = Bun.spawn([which(), ...cmd], {
|
|
26
51
|
...options,
|
|
27
52
|
stdout: 'pipe',
|
|
28
53
|
stderr: 'pipe',
|
|
54
|
+
timeout, // Automatically kills process after timeout
|
|
55
|
+
killSignal: 'SIGTERM', // Graceful termination signal
|
|
29
56
|
env: {
|
|
30
57
|
...process.env,
|
|
31
58
|
...options?.env,
|
|
32
59
|
BUN_BE_BUN: '1',
|
|
33
60
|
},
|
|
34
61
|
});
|
|
62
|
+
|
|
35
63
|
const code = await result.exited;
|
|
64
|
+
|
|
65
|
+
// Check if process was killed due to timeout
|
|
66
|
+
if (result.signalCode === 'SIGTERM' && code !== 0) {
|
|
67
|
+
log.error(() => ({
|
|
68
|
+
message: 'command timed out',
|
|
69
|
+
cmd: [which(), ...cmd],
|
|
70
|
+
timeout,
|
|
71
|
+
signalCode: result.signalCode,
|
|
72
|
+
}));
|
|
73
|
+
throw new TimeoutError({
|
|
74
|
+
cmd: [which(), ...cmd],
|
|
75
|
+
timeoutMs: timeout,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
36
79
|
const stdout = result.stdout
|
|
37
80
|
? typeof result.stdout === 'number'
|
|
38
81
|
? result.stdout
|
|
@@ -84,6 +127,13 @@ export namespace BunProc {
|
|
|
84
127
|
);
|
|
85
128
|
}
|
|
86
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Check if an error is a timeout error
|
|
132
|
+
*/
|
|
133
|
+
function isTimeoutError(error: unknown): boolean {
|
|
134
|
+
return error instanceof TimeoutError;
|
|
135
|
+
}
|
|
136
|
+
|
|
87
137
|
/**
|
|
88
138
|
* Wait for a specified duration
|
|
89
139
|
*/
|
|
@@ -139,12 +189,13 @@ export namespace BunProc {
|
|
|
139
189
|
version,
|
|
140
190
|
}));
|
|
141
191
|
|
|
142
|
-
// Retry logic for cache-related errors
|
|
192
|
+
// Retry logic for cache-related errors and timeout errors
|
|
143
193
|
let lastError: Error | undefined;
|
|
144
194
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
145
195
|
try {
|
|
146
196
|
await BunProc.run(args, {
|
|
147
197
|
cwd: Global.Path.cache,
|
|
198
|
+
timeout: INSTALL_TIMEOUT_MS, // Use specific timeout for package installation
|
|
148
199
|
});
|
|
149
200
|
|
|
150
201
|
log.info(() => ({
|
|
@@ -159,6 +210,7 @@ export namespace BunProc {
|
|
|
159
210
|
} catch (e) {
|
|
160
211
|
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
161
212
|
const isCacheError = isCacheRelatedError(errorMsg);
|
|
213
|
+
const isTimeout = isTimeoutError(e);
|
|
162
214
|
|
|
163
215
|
log.warn(() => ({
|
|
164
216
|
message: 'package installation attempt failed',
|
|
@@ -168,11 +220,15 @@ export namespace BunProc {
|
|
|
168
220
|
maxRetries: MAX_RETRIES,
|
|
169
221
|
error: errorMsg,
|
|
170
222
|
isCacheError,
|
|
223
|
+
isTimeout,
|
|
171
224
|
}));
|
|
172
225
|
|
|
173
|
-
|
|
226
|
+
// Retry on cache-related errors or timeout errors
|
|
227
|
+
if ((isCacheError || isTimeout) && attempt < MAX_RETRIES) {
|
|
174
228
|
log.info(() => ({
|
|
175
|
-
message:
|
|
229
|
+
message: isTimeout
|
|
230
|
+
? 'retrying installation after timeout (possible network issue)'
|
|
231
|
+
: 'retrying installation after cache-related error',
|
|
176
232
|
pkg,
|
|
177
233
|
version,
|
|
178
234
|
attempt,
|
|
@@ -184,7 +240,7 @@ export namespace BunProc {
|
|
|
184
240
|
continue;
|
|
185
241
|
}
|
|
186
242
|
|
|
187
|
-
// Non-
|
|
243
|
+
// Non-retriable error or final attempt - log and throw
|
|
188
244
|
log.error(() => ({
|
|
189
245
|
message: 'package installation failed',
|
|
190
246
|
pkg,
|
|
@@ -192,10 +248,11 @@ export namespace BunProc {
|
|
|
192
248
|
error: errorMsg,
|
|
193
249
|
stack: e instanceof Error ? e.stack : undefined,
|
|
194
250
|
possibleCacheCorruption: isCacheError,
|
|
251
|
+
timedOut: isTimeout,
|
|
195
252
|
attempts: attempt,
|
|
196
253
|
}));
|
|
197
254
|
|
|
198
|
-
// Provide helpful recovery instructions
|
|
255
|
+
// Provide helpful recovery instructions
|
|
199
256
|
if (isCacheError) {
|
|
200
257
|
log.error(() => ({
|
|
201
258
|
message:
|
|
@@ -203,6 +260,15 @@ export namespace BunProc {
|
|
|
203
260
|
}));
|
|
204
261
|
}
|
|
205
262
|
|
|
263
|
+
if (isTimeout) {
|
|
264
|
+
log.error(() => ({
|
|
265
|
+
message:
|
|
266
|
+
'Package installation timed out. This may be due to network issues or Bun hanging. ' +
|
|
267
|
+
'Try: 1) Check network connectivity, 2) Run "bun pm cache rm" to clear cache, ' +
|
|
268
|
+
'3) Check for IPv6 issues (try disabling IPv6)',
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
|
|
206
272
|
throw new InstallFailedError(
|
|
207
273
|
{ pkg, version, details: errorMsg },
|
|
208
274
|
{
|