blun-king-cli 9.1.293 → 9.1.295
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/bin/abort-listener-policy.cjs +30 -1
- package/bin/update-notice.js +49 -3
- package/blun.mjs +6 -9
- package/package.json +1 -1
|
@@ -11,4 +11,33 @@ async function waitForAbortableCompletion(signal, work, onAbort) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
function createAbortOutcome(signal, outcome, isRelevant = () => true) {
|
|
15
|
+
let listener;
|
|
16
|
+
let listening = false;
|
|
17
|
+
const promise = new Promise((resolve) => {
|
|
18
|
+
if (signal === undefined) return;
|
|
19
|
+
const resolveIfRelevant = () => {
|
|
20
|
+
if (isRelevant()) resolve(outcome());
|
|
21
|
+
};
|
|
22
|
+
if (signal.aborted) {
|
|
23
|
+
resolveIfRelevant();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
listener = resolveIfRelevant;
|
|
27
|
+
listening = true;
|
|
28
|
+
signal.addEventListener('abort', listener, { once: true });
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
promise,
|
|
32
|
+
cleanup() {
|
|
33
|
+
if (!listening) return;
|
|
34
|
+
listening = false;
|
|
35
|
+
signal.removeEventListener('abort', listener);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
createAbortOutcome,
|
|
42
|
+
waitForAbortableCompletion,
|
|
43
|
+
};
|
package/bin/update-notice.js
CHANGED
|
@@ -22,6 +22,7 @@ const INSTALL_REGISTRY_URL = 'https://registry.npmjs.org/';
|
|
|
22
22
|
const FALLBACK_MANIFEST_URL = 'https://chat.blun.ai/blun-code-version.json';
|
|
23
23
|
const SNOOZE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
24
24
|
const REQUEST_TIMEOUT_MS = 2_500;
|
|
25
|
+
const UPDATE_SOURCE_RETRY_DELAYS_MS = Object.freeze([250, 750]);
|
|
25
26
|
const STATE_FILE = 'update-notice.json';
|
|
26
27
|
const UPDATE_SKIP_FILE = 'update-skip-version.json';
|
|
27
28
|
const PENDING_UPDATE_FILE = 'update-pending.json';
|
|
@@ -385,10 +386,54 @@ function requestTrustedJson(url, options = {}) {
|
|
|
385
386
|
});
|
|
386
387
|
}
|
|
387
388
|
|
|
388
|
-
|
|
389
|
+
function retryableUpdateSourceError(error) {
|
|
390
|
+
const message = error instanceof Error ? error.message : String(error || '');
|
|
391
|
+
if (message === 'UPDATE_REQUEST_TIMEOUT') return true;
|
|
392
|
+
const statusMatch = /^UPDATE_HTTP_STATUS_(\d{3})$/u.exec(message);
|
|
393
|
+
if (statusMatch) {
|
|
394
|
+
const status = Number(statusMatch[1]);
|
|
395
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
396
|
+
}
|
|
397
|
+
return new Set([
|
|
398
|
+
'EAI_AGAIN',
|
|
399
|
+
'ECONNREFUSED',
|
|
400
|
+
'ECONNRESET',
|
|
401
|
+
'ENETDOWN',
|
|
402
|
+
'ENETUNREACH',
|
|
403
|
+
'ENOTFOUND',
|
|
404
|
+
'EPIPE',
|
|
405
|
+
'ETIMEDOUT',
|
|
406
|
+
]).has(error?.code);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function sleepForUpdateRetry(delayMs) {
|
|
410
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function requestTrustedJsonWithRetry(url, options = {}) {
|
|
414
|
+
const request = options.request || requestTrustedJson;
|
|
415
|
+
const sleep = options.sleep || sleepForUpdateRetry;
|
|
416
|
+
const configuredDelays = Array.isArray(options.retryDelaysMs)
|
|
417
|
+
? options.retryDelaysMs
|
|
418
|
+
: UPDATE_SOURCE_RETRY_DELAYS_MS;
|
|
419
|
+
const retryDelaysMs = configuredDelays
|
|
420
|
+
.slice(0, UPDATE_SOURCE_RETRY_DELAYS_MS.length)
|
|
421
|
+
.map((value) => (Number.isFinite(value) && value >= 0 ? Math.min(value, 2_000) : 0));
|
|
422
|
+
|
|
423
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
424
|
+
try {
|
|
425
|
+
return await request(url, options);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (attempt >= retryDelaysMs.length || !retryableUpdateSourceError(error)) throw error;
|
|
428
|
+
await sleep(retryDelaysMs[attempt]);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async function loadReleaseSources(options = {}) {
|
|
389
434
|
const [registry, fallback] = await Promise.allSettled([
|
|
390
|
-
|
|
391
|
-
|
|
435
|
+
requestTrustedJsonWithRetry(REGISTRY_URL, options),
|
|
436
|
+
requestTrustedJsonWithRetry(FALLBACK_MANIFEST_URL, options),
|
|
392
437
|
]);
|
|
393
438
|
return {
|
|
394
439
|
registryDocument: registry.status === 'fulfilled' ? registry.value : undefined,
|
|
@@ -1636,6 +1681,7 @@ module.exports = {
|
|
|
1636
1681
|
readSkippedVersion,
|
|
1637
1682
|
readSnoozeState,
|
|
1638
1683
|
requestTrustedJson,
|
|
1684
|
+
requestTrustedJsonWithRetry,
|
|
1639
1685
|
resolveEffectiveRelease,
|
|
1640
1686
|
runExplicitUpdate,
|
|
1641
1687
|
runUpdateNotice,
|
package/blun.mjs
CHANGED
|
@@ -29400,7 +29400,7 @@ function buildBackgroundTaskNotificationBody(info) {
|
|
|
29400
29400
|
"The subagent retains its full prior context across the restart, but any in-flight tool call lost its result and may need to be redone."
|
|
29401
29401
|
].join("\n")}`;
|
|
29402
29402
|
}
|
|
29403
|
-
var MAX_OUTPUT_BYTES$2, NOTIFICATION_FALLBACK_PREVIEW_BYTES, MAX_FOREGROUND_CAPTURE_BYTES, captureForegroundOutputChunk, SIGTERM_GRACE_MS$1, USER_INTERRUPT_REASON$1, _ALPHABET, BackgroundManager;
|
|
29403
|
+
var MAX_OUTPUT_BYTES$2, NOTIFICATION_FALLBACK_PREVIEW_BYTES, MAX_FOREGROUND_CAPTURE_BYTES, captureForegroundOutputChunk, createAbortOutcome, SIGTERM_GRACE_MS$1, USER_INTERRUPT_REASON$1, _ALPHABET, BackgroundManager;
|
|
29404
29404
|
var init_background = __esmMin((() => {
|
|
29405
29405
|
init_dist$4();
|
|
29406
29406
|
init_errors$4();
|
|
@@ -29415,6 +29415,7 @@ var init_background = __esmMin((() => {
|
|
|
29415
29415
|
MAX_OUTPUT_BYTES$2 = 1024 * 1024;
|
|
29416
29416
|
NOTIFICATION_FALLBACK_PREVIEW_BYTES = 3e3;
|
|
29417
29417
|
({ MAX_FOREGROUND_CAPTURE_BYTES, captureForegroundOutputChunk } = createRequire(import.meta.url)("./bin/foreground-output-capture-policy.cjs"));
|
|
29418
|
+
({ createAbortOutcome } = createRequire(import.meta.url)("./bin/abort-listener-policy.cjs"));
|
|
29418
29419
|
SIGTERM_GRACE_MS$1 = 5e3;
|
|
29419
29420
|
USER_INTERRUPT_REASON$1 = "Interrupted by user";
|
|
29420
29421
|
_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
@@ -29886,6 +29887,7 @@ var init_background = __esmMin((() => {
|
|
|
29886
29887
|
});
|
|
29887
29888
|
const timeout = resettableTimeoutOutcome(entry.options.timeoutMs, { kind: "timeout" });
|
|
29888
29889
|
entry.timeoutHandle = timeout;
|
|
29890
|
+
const signalOutcome = this.signalOutcome(entry);
|
|
29889
29891
|
const outcome = await Promise.race([
|
|
29890
29892
|
worker.then((settlement) => ({
|
|
29891
29893
|
kind: "worker",
|
|
@@ -29896,9 +29898,10 @@ var init_background = __esmMin((() => {
|
|
|
29896
29898
|
kind: "stop",
|
|
29897
29899
|
request
|
|
29898
29900
|
})),
|
|
29899
|
-
|
|
29901
|
+
signalOutcome.promise
|
|
29900
29902
|
]).finally(() => {
|
|
29901
29903
|
timeout.clear();
|
|
29904
|
+
signalOutcome.cleanup();
|
|
29902
29905
|
entry.timeoutHandle = void 0;
|
|
29903
29906
|
});
|
|
29904
29907
|
const settlement = await this.settlementForOutcome(entry, outcome, worker);
|
|
@@ -29906,7 +29909,6 @@ var init_background = __esmMin((() => {
|
|
|
29906
29909
|
}
|
|
29907
29910
|
signalOutcome(entry) {
|
|
29908
29911
|
const signal = entry.options.signal;
|
|
29909
|
-
if (signal === void 0) return new Promise(() => {});
|
|
29910
29912
|
const outcome = () => ({
|
|
29911
29913
|
kind: "stop",
|
|
29912
29914
|
request: {
|
|
@@ -29914,12 +29916,7 @@ var init_background = __esmMin((() => {
|
|
|
29914
29916
|
abortReason: signal.reason
|
|
29915
29917
|
}
|
|
29916
29918
|
});
|
|
29917
|
-
|
|
29918
|
-
return new Promise((resolve) => {
|
|
29919
|
-
signal.addEventListener("abort", () => {
|
|
29920
|
-
if (!this.isDetached(entry)) resolve(outcome());
|
|
29921
|
-
}, { once: true });
|
|
29922
|
-
});
|
|
29919
|
+
return createAbortOutcome(signal, outcome, () => !this.isDetached(entry));
|
|
29923
29920
|
}
|
|
29924
29921
|
async settlementForOutcome(entry, outcome, worker) {
|
|
29925
29922
|
if (outcome.kind === "worker") return outcome.settlement;
|