@cortexkit/aft-opencode 0.26.0 → 0.26.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/dist/bg-notifications.d.ts +3 -0
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +552 -246
- package/dist/lsp-github-install.d.ts +2 -2
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/tui.js +7 -7
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -7803,7 +7803,7 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
7803
7803
|
});
|
|
7804
7804
|
|
|
7805
7805
|
// src/index.ts
|
|
7806
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as
|
|
7806
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
7807
7807
|
import { createRequire as createRequire2 } from "module";
|
|
7808
7808
|
import { homedir as homedir11 } from "os";
|
|
7809
7809
|
import { join as join21 } from "path";
|
|
@@ -7820,12 +7820,22 @@ function getActiveLogger() {
|
|
|
7820
7820
|
return loggerGlobal()[ACTIVE_LOGGER_SYMBOL];
|
|
7821
7821
|
}
|
|
7822
7822
|
function getLogFilePath() {
|
|
7823
|
-
|
|
7823
|
+
try {
|
|
7824
|
+
return getActiveLogger()?.getLogFilePath?.();
|
|
7825
|
+
} catch (err) {
|
|
7826
|
+
console.error(`[aft-bridge] ERROR: active logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7824
7829
|
}
|
|
7825
7830
|
function log(message, meta) {
|
|
7826
7831
|
const active = getActiveLogger();
|
|
7827
7832
|
if (active) {
|
|
7828
|
-
|
|
7833
|
+
try {
|
|
7834
|
+
active.log(message, meta);
|
|
7835
|
+
} catch (err) {
|
|
7836
|
+
console.error(`[aft-bridge] ERROR: active logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7837
|
+
console.error(`[aft-bridge] ${message}`);
|
|
7838
|
+
}
|
|
7829
7839
|
} else {
|
|
7830
7840
|
console.error(`[aft-bridge] ${message}`);
|
|
7831
7841
|
}
|
|
@@ -7833,7 +7843,12 @@ function log(message, meta) {
|
|
|
7833
7843
|
function warn(message, meta) {
|
|
7834
7844
|
const active = getActiveLogger();
|
|
7835
7845
|
if (active) {
|
|
7836
|
-
|
|
7846
|
+
try {
|
|
7847
|
+
active.warn(message, meta);
|
|
7848
|
+
} catch (err) {
|
|
7849
|
+
console.error(`[aft-bridge] ERROR: active logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7850
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
7851
|
+
}
|
|
7837
7852
|
} else {
|
|
7838
7853
|
console.error(`[aft-bridge] WARN: ${message}`);
|
|
7839
7854
|
}
|
|
@@ -7841,20 +7856,16 @@ function warn(message, meta) {
|
|
|
7841
7856
|
function error(message, meta) {
|
|
7842
7857
|
const active = getActiveLogger();
|
|
7843
7858
|
if (active) {
|
|
7844
|
-
|
|
7859
|
+
try {
|
|
7860
|
+
active.error(message, meta);
|
|
7861
|
+
} catch (err) {
|
|
7862
|
+
console.error(`[aft-bridge] ERROR: active logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7863
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
7864
|
+
}
|
|
7845
7865
|
} else {
|
|
7846
7866
|
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
7847
7867
|
}
|
|
7848
7868
|
}
|
|
7849
|
-
function sessionLog(sessionId, message) {
|
|
7850
|
-
log(message, sessionId ? { sessionId } : undefined);
|
|
7851
|
-
}
|
|
7852
|
-
function sessionWarn(sessionId, message) {
|
|
7853
|
-
warn(message, sessionId ? { sessionId } : undefined);
|
|
7854
|
-
}
|
|
7855
|
-
function sessionError(sessionId, message) {
|
|
7856
|
-
error(message, sessionId ? { sessionId } : undefined);
|
|
7857
|
-
}
|
|
7858
7869
|
// ../aft-bridge/dist/bridge.js
|
|
7859
7870
|
import { spawn } from "child_process";
|
|
7860
7871
|
import { homedir } from "os";
|
|
@@ -7949,6 +7960,7 @@ class BinaryBridge {
|
|
|
7949
7960
|
pending = new Map;
|
|
7950
7961
|
nextId = 1;
|
|
7951
7962
|
stdoutBuffer = "";
|
|
7963
|
+
stderrBuffer = "";
|
|
7952
7964
|
stderrTail = [];
|
|
7953
7965
|
_restartCount = 0;
|
|
7954
7966
|
_shuttingDown = false;
|
|
@@ -7984,24 +7996,62 @@ class BinaryBridge {
|
|
|
7984
7996
|
}
|
|
7985
7997
|
logVia(message, meta) {
|
|
7986
7998
|
const logger = this.logger ?? getActiveLogger();
|
|
7987
|
-
if (logger)
|
|
7988
|
-
|
|
7989
|
-
|
|
7999
|
+
if (logger) {
|
|
8000
|
+
try {
|
|
8001
|
+
logger.log(message, meta);
|
|
8002
|
+
} catch (err) {
|
|
8003
|
+
console.error(`[aft-bridge] ERROR: logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8004
|
+
console.error(`[aft-bridge] ${message}`);
|
|
8005
|
+
}
|
|
8006
|
+
} else {
|
|
7990
8007
|
log(message, meta);
|
|
8008
|
+
}
|
|
7991
8009
|
}
|
|
7992
8010
|
warnVia(message, meta) {
|
|
7993
8011
|
const logger = this.logger ?? getActiveLogger();
|
|
7994
|
-
if (logger)
|
|
7995
|
-
|
|
7996
|
-
|
|
8012
|
+
if (logger) {
|
|
8013
|
+
try {
|
|
8014
|
+
logger.warn(message, meta);
|
|
8015
|
+
} catch (err) {
|
|
8016
|
+
console.error(`[aft-bridge] ERROR: logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8017
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
8018
|
+
}
|
|
8019
|
+
} else {
|
|
7997
8020
|
warn(message, meta);
|
|
8021
|
+
}
|
|
7998
8022
|
}
|
|
7999
8023
|
errorVia(message, meta) {
|
|
8000
8024
|
const logger = this.logger ?? getActiveLogger();
|
|
8001
|
-
if (logger)
|
|
8002
|
-
|
|
8003
|
-
|
|
8025
|
+
if (logger) {
|
|
8026
|
+
try {
|
|
8027
|
+
logger.error(message, meta);
|
|
8028
|
+
} catch (err) {
|
|
8029
|
+
console.error(`[aft-bridge] ERROR: logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8030
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
8031
|
+
}
|
|
8032
|
+
} else {
|
|
8004
8033
|
error(message, meta);
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
getLogFilePathVia() {
|
|
8037
|
+
if (this.logger?.getLogFilePath) {
|
|
8038
|
+
try {
|
|
8039
|
+
return this.logger.getLogFilePath();
|
|
8040
|
+
} catch (err) {
|
|
8041
|
+
console.error(`[aft-bridge] ERROR: logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8042
|
+
return;
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
return getLogFilePath();
|
|
8046
|
+
}
|
|
8047
|
+
sessionLogVia(sessionId, message) {
|
|
8048
|
+
this.logVia(message, sessionId ? { sessionId } : undefined);
|
|
8049
|
+
}
|
|
8050
|
+
sessionWarnVia(sessionId, message) {
|
|
8051
|
+
this.warnVia(message, sessionId ? { sessionId } : undefined);
|
|
8052
|
+
}
|
|
8053
|
+
sessionErrorVia(sessionId, message) {
|
|
8054
|
+
this.errorVia(message, sessionId ? { sessionId } : undefined);
|
|
8005
8055
|
}
|
|
8006
8056
|
get restartCount() {
|
|
8007
8057
|
return this._restartCount;
|
|
@@ -8060,7 +8110,7 @@ class BinaryBridge {
|
|
|
8060
8110
|
await this.deliverConfigureWarnings(configResult, params, options);
|
|
8061
8111
|
await this.checkVersion();
|
|
8062
8112
|
if (!this.isAlive()) {
|
|
8063
|
-
throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${
|
|
8113
|
+
throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
|
|
8064
8114
|
}
|
|
8065
8115
|
this.configured = true;
|
|
8066
8116
|
} finally {
|
|
@@ -8096,9 +8146,9 @@ class BinaryBridge {
|
|
|
8096
8146
|
const restartSuffix = keepBridgeOnTimeout ? "" : " \u2014 restarting bridge";
|
|
8097
8147
|
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
8098
8148
|
if (requestSessionId) {
|
|
8099
|
-
|
|
8149
|
+
this.sessionWarnVia(requestSessionId, timeoutMsg);
|
|
8100
8150
|
} else {
|
|
8101
|
-
|
|
8151
|
+
this.warnVia(timeoutMsg);
|
|
8102
8152
|
}
|
|
8103
8153
|
reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
8104
8154
|
if (!keepBridgeOnTimeout) {
|
|
@@ -8125,7 +8175,7 @@ class BinaryBridge {
|
|
|
8125
8175
|
});
|
|
8126
8176
|
} catch (err) {
|
|
8127
8177
|
if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
|
|
8128
|
-
|
|
8178
|
+
this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
|
|
8129
8179
|
return this.sendWithVersionMismatchRetry(command, params, options, false);
|
|
8130
8180
|
}
|
|
8131
8181
|
throw err;
|
|
@@ -8145,7 +8195,7 @@ class BinaryBridge {
|
|
|
8145
8195
|
warnings: configResult.warnings
|
|
8146
8196
|
});
|
|
8147
8197
|
} catch (err) {
|
|
8148
|
-
|
|
8198
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8149
8199
|
} finally {
|
|
8150
8200
|
if (sessionId) {
|
|
8151
8201
|
this.configureWarningClients.delete(sessionId);
|
|
@@ -8179,7 +8229,7 @@ class BinaryBridge {
|
|
|
8179
8229
|
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
8180
8230
|
return;
|
|
8181
8231
|
this.cachedStatus = snapshot;
|
|
8182
|
-
|
|
8232
|
+
this.logVia("Received status_changed push frame; cached AFT status snapshot");
|
|
8183
8233
|
for (const listener of this.statusListeners) {
|
|
8184
8234
|
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
8185
8235
|
}
|
|
@@ -8188,7 +8238,7 @@ class BinaryBridge {
|
|
|
8188
8238
|
try {
|
|
8189
8239
|
listener(snapshot);
|
|
8190
8240
|
} catch (err) {
|
|
8191
|
-
|
|
8241
|
+
this.warnVia(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8192
8242
|
}
|
|
8193
8243
|
}
|
|
8194
8244
|
async shutdown() {
|
|
@@ -8206,7 +8256,7 @@ class BinaryBridge {
|
|
|
8206
8256
|
}, 5000);
|
|
8207
8257
|
proc.once("exit", () => {
|
|
8208
8258
|
clearTimeout(forceKillTimer);
|
|
8209
|
-
|
|
8259
|
+
this.logVia("Process exited during shutdown");
|
|
8210
8260
|
resolve();
|
|
8211
8261
|
});
|
|
8212
8262
|
proc.kill("SIGTERM");
|
|
@@ -8225,9 +8275,9 @@ class BinaryBridge {
|
|
|
8225
8275
|
if (typeof binaryVersion !== "string") {
|
|
8226
8276
|
throw new Error(`Binary did not report a version \u2014 likely too old (minVersion: ${this.minVersion})`);
|
|
8227
8277
|
}
|
|
8228
|
-
|
|
8278
|
+
this.logVia(`Binary version: ${binaryVersion}`);
|
|
8229
8279
|
if (compareSemver(binaryVersion, this.minVersion) < 0) {
|
|
8230
|
-
|
|
8280
|
+
this.warnVia(`Binary version ${binaryVersion} is older than required ${this.minVersion}`);
|
|
8231
8281
|
const replacementPath = await this.onVersionMismatch?.(binaryVersion, this.minVersion);
|
|
8232
8282
|
if (replacementPath === undefined) {
|
|
8233
8283
|
return;
|
|
@@ -8239,7 +8289,7 @@ class BinaryBridge {
|
|
|
8239
8289
|
throw new BridgeReplacedDuringVersionCheck(replacementPath);
|
|
8240
8290
|
}
|
|
8241
8291
|
} catch (err) {
|
|
8242
|
-
|
|
8292
|
+
this.warnVia(`Version check failed: ${err.message}`);
|
|
8243
8293
|
throw err;
|
|
8244
8294
|
}
|
|
8245
8295
|
}
|
|
@@ -8259,7 +8309,7 @@ class BinaryBridge {
|
|
|
8259
8309
|
}, 5000);
|
|
8260
8310
|
proc.once("exit", () => {
|
|
8261
8311
|
clearTimeout(forceKillTimer);
|
|
8262
|
-
|
|
8312
|
+
this.logVia("Process exited during coordinated binary replacement");
|
|
8263
8313
|
resolve();
|
|
8264
8314
|
});
|
|
8265
8315
|
proc.kill("SIGTERM");
|
|
@@ -8272,9 +8322,9 @@ class BinaryBridge {
|
|
|
8272
8322
|
}
|
|
8273
8323
|
spawnProcess(triggeringSessionId) {
|
|
8274
8324
|
if (triggeringSessionId) {
|
|
8275
|
-
|
|
8325
|
+
this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
8276
8326
|
} else {
|
|
8277
|
-
|
|
8327
|
+
this.logVia(`Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
8278
8328
|
}
|
|
8279
8329
|
const semantic = this.configOverrides.semantic;
|
|
8280
8330
|
const semanticBackend = (() => {
|
|
@@ -8321,11 +8371,12 @@ class BinaryBridge {
|
|
|
8321
8371
|
const remaining = stderrDecoder.end();
|
|
8322
8372
|
if (remaining)
|
|
8323
8373
|
this.onStderrData(remaining);
|
|
8374
|
+
this.flushStderrBuffer();
|
|
8324
8375
|
});
|
|
8325
8376
|
child.on("error", (err) => {
|
|
8326
8377
|
if (this.process !== currentChild)
|
|
8327
8378
|
return;
|
|
8328
|
-
|
|
8379
|
+
this.errorVia(`Process error: ${err.message}${this.formatStderrTail()}`);
|
|
8329
8380
|
this.handleCrash();
|
|
8330
8381
|
});
|
|
8331
8382
|
child.on("exit", (code, signal) => {
|
|
@@ -8333,7 +8384,7 @@ class BinaryBridge {
|
|
|
8333
8384
|
return;
|
|
8334
8385
|
if (this._shuttingDown)
|
|
8335
8386
|
return;
|
|
8336
|
-
|
|
8387
|
+
this.logVia(`Process exited: code=${code}, signal=${signal}`);
|
|
8337
8388
|
if (signal === "SIGTERM" || signal === "SIGKILL" || signal === "SIGHUP" || signal === "SIGINT") {
|
|
8338
8389
|
this.process = null;
|
|
8339
8390
|
this.configured = false;
|
|
@@ -8345,6 +8396,7 @@ class BinaryBridge {
|
|
|
8345
8396
|
});
|
|
8346
8397
|
this.process = child;
|
|
8347
8398
|
this.stdoutBuffer = "";
|
|
8399
|
+
this.stderrBuffer = "";
|
|
8348
8400
|
this.stderrTail = [];
|
|
8349
8401
|
}
|
|
8350
8402
|
pushStderrLine(line) {
|
|
@@ -8354,16 +8406,28 @@ class BinaryBridge {
|
|
|
8354
8406
|
}
|
|
8355
8407
|
}
|
|
8356
8408
|
onStderrData(data) {
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8409
|
+
this.stderrBuffer += data;
|
|
8410
|
+
let newlineIdx;
|
|
8411
|
+
while ((newlineIdx = this.stderrBuffer.indexOf(`
|
|
8412
|
+
`)) !== -1) {
|
|
8413
|
+
const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
8414
|
+
this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
|
|
8360
8415
|
if (!line)
|
|
8361
8416
|
continue;
|
|
8362
8417
|
const tagged = tagStderrLine(line);
|
|
8363
|
-
|
|
8418
|
+
this.logVia(tagged);
|
|
8364
8419
|
this.pushStderrLine(tagged);
|
|
8365
8420
|
}
|
|
8366
8421
|
}
|
|
8422
|
+
flushStderrBuffer() {
|
|
8423
|
+
const line = this.stderrBuffer.replace(/\r$/, "");
|
|
8424
|
+
this.stderrBuffer = "";
|
|
8425
|
+
if (!line)
|
|
8426
|
+
return;
|
|
8427
|
+
const tagged = tagStderrLine(line);
|
|
8428
|
+
this.logVia(tagged);
|
|
8429
|
+
this.pushStderrLine(tagged);
|
|
8430
|
+
}
|
|
8367
8431
|
formatStderrTail() {
|
|
8368
8432
|
if (this.stderrTail.length === 0)
|
|
8369
8433
|
return "";
|
|
@@ -8421,7 +8485,7 @@ class BinaryBridge {
|
|
|
8421
8485
|
}
|
|
8422
8486
|
if (response.type === "configure_warnings") {
|
|
8423
8487
|
this.handleConfigureWarningsFrame(response).catch((err) => {
|
|
8424
|
-
|
|
8488
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8425
8489
|
});
|
|
8426
8490
|
continue;
|
|
8427
8491
|
}
|
|
@@ -8439,10 +8503,10 @@ class BinaryBridge {
|
|
|
8439
8503
|
this.scheduleRestartCountReset();
|
|
8440
8504
|
entry.resolve(response);
|
|
8441
8505
|
} else if (typeof response.type === "string") {
|
|
8442
|
-
|
|
8506
|
+
this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
|
|
8443
8507
|
}
|
|
8444
8508
|
} catch (_err) {
|
|
8445
|
-
|
|
8509
|
+
this.warnVia(`Failed to parse stdout line: ${line}`);
|
|
8446
8510
|
}
|
|
8447
8511
|
}
|
|
8448
8512
|
}
|
|
@@ -8456,17 +8520,17 @@ class BinaryBridge {
|
|
|
8456
8520
|
this.configured = false;
|
|
8457
8521
|
const tail = this.formatStderrTail();
|
|
8458
8522
|
this.stderrTail = [];
|
|
8459
|
-
const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${
|
|
8523
|
+
const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
|
|
8460
8524
|
if (tail) {
|
|
8461
8525
|
if (triggeringSessionId) {
|
|
8462
|
-
|
|
8526
|
+
this.sessionErrorVia(triggeringSessionId, killedMsg);
|
|
8463
8527
|
} else {
|
|
8464
|
-
|
|
8528
|
+
this.errorVia(killedMsg);
|
|
8465
8529
|
}
|
|
8466
8530
|
} else if (triggeringSessionId) {
|
|
8467
|
-
|
|
8531
|
+
this.sessionWarnVia(triggeringSessionId, killedMsg);
|
|
8468
8532
|
} else {
|
|
8469
|
-
|
|
8533
|
+
this.warnVia(killedMsg);
|
|
8470
8534
|
}
|
|
8471
8535
|
}
|
|
8472
8536
|
handleCrash(cause) {
|
|
@@ -8479,25 +8543,25 @@ class BinaryBridge {
|
|
|
8479
8543
|
this.configured = false;
|
|
8480
8544
|
const tail = this.formatStderrTail();
|
|
8481
8545
|
if (tail) {
|
|
8482
|
-
|
|
8546
|
+
this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
|
|
8483
8547
|
}
|
|
8484
|
-
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${
|
|
8548
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
|
|
8485
8549
|
if (this._restartCount < this.maxRestarts) {
|
|
8486
8550
|
const delay = 100 * 2 ** this._restartCount;
|
|
8487
8551
|
this._restartCount++;
|
|
8488
|
-
|
|
8552
|
+
this.logVia(`Auto-restart #${this._restartCount} in ${delay}ms`);
|
|
8489
8553
|
setTimeout(() => {
|
|
8490
8554
|
if (!this._shuttingDown && !this.isAlive()) {
|
|
8491
8555
|
try {
|
|
8492
8556
|
this.spawnProcess();
|
|
8493
8557
|
} catch (err) {
|
|
8494
|
-
|
|
8558
|
+
this.errorVia(`Failed to restart: ${err.message}`);
|
|
8495
8559
|
}
|
|
8496
8560
|
}
|
|
8497
8561
|
}, delay);
|
|
8498
8562
|
this.scheduleRestartCountReset();
|
|
8499
8563
|
} else {
|
|
8500
|
-
|
|
8564
|
+
this.errorVia(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${this.getLogFilePathVia()}${tail}`);
|
|
8501
8565
|
this.scheduleRestartCountReset();
|
|
8502
8566
|
}
|
|
8503
8567
|
}
|
|
@@ -8523,9 +8587,12 @@ class BinaryBridge {
|
|
|
8523
8587
|
}
|
|
8524
8588
|
}
|
|
8525
8589
|
// ../aft-bridge/dist/downloader.js
|
|
8526
|
-
import {
|
|
8590
|
+
import { createHash } from "crypto";
|
|
8591
|
+
import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, unlinkSync } from "fs";
|
|
8527
8592
|
import { homedir as homedir2 } from "os";
|
|
8528
8593
|
import { join as join2 } from "path";
|
|
8594
|
+
import { Readable } from "stream";
|
|
8595
|
+
import { pipeline } from "stream/promises";
|
|
8529
8596
|
|
|
8530
8597
|
// ../aft-bridge/dist/platform.js
|
|
8531
8598
|
var PLATFORM_ARCH_MAP = {
|
|
@@ -8543,6 +8610,11 @@ var PLATFORM_ASSET_MAP = {
|
|
|
8543
8610
|
|
|
8544
8611
|
// ../aft-bridge/dist/downloader.js
|
|
8545
8612
|
var REPO = "cortexkit/aft";
|
|
8613
|
+
var DOWNLOAD_TIMEOUT_MS = 300000;
|
|
8614
|
+
var LATEST_TAG_TIMEOUT_MS = 30000;
|
|
8615
|
+
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
8616
|
+
var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
|
|
8617
|
+
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
8546
8618
|
function getCacheDir() {
|
|
8547
8619
|
if (process.platform === "win32") {
|
|
8548
8620
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
@@ -8584,54 +8656,101 @@ async function downloadBinary(version) {
|
|
|
8584
8656
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
|
|
8585
8657
|
const checksumUrl = `https://github.com/${REPO}/releases/download/${tag}/checksums.sha256`;
|
|
8586
8658
|
log(`Downloading AFT binary (${tag}) for ${platformKey}...`);
|
|
8659
|
+
const lockPath = join2(versionedCacheDir, ".download.lock");
|
|
8660
|
+
let releaseLock = null;
|
|
8661
|
+
let binaryController = null;
|
|
8662
|
+
let checksumController = null;
|
|
8663
|
+
let binaryTimeout = null;
|
|
8664
|
+
let checksumTimeout = null;
|
|
8665
|
+
const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
8587
8666
|
try {
|
|
8588
8667
|
if (!existsSync(versionedCacheDir)) {
|
|
8589
8668
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
8590
8669
|
}
|
|
8670
|
+
releaseLock = await acquireDownloadLock(lockPath);
|
|
8671
|
+
if (existsSync(binaryPath)) {
|
|
8672
|
+
return binaryPath;
|
|
8673
|
+
}
|
|
8674
|
+
binaryController = new AbortController;
|
|
8675
|
+
checksumController = new AbortController;
|
|
8676
|
+
const activeBinaryController = binaryController;
|
|
8677
|
+
const activeChecksumController = checksumController;
|
|
8678
|
+
binaryTimeout = setTimeout(() => activeBinaryController.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
8679
|
+
checksumTimeout = setTimeout(() => activeChecksumController.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
8591
8680
|
const [binaryResponse, checksumResponse] = await Promise.all([
|
|
8592
|
-
fetch(downloadUrl, { redirect: "follow" }),
|
|
8593
|
-
fetch(checksumUrl, { redirect: "follow" })
|
|
8681
|
+
fetch(downloadUrl, { redirect: "follow", signal: activeBinaryController.signal }),
|
|
8682
|
+
fetch(checksumUrl, { redirect: "follow", signal: activeChecksumController.signal })
|
|
8594
8683
|
]);
|
|
8595
8684
|
if (!binaryResponse.ok) {
|
|
8596
8685
|
throw new Error(`HTTP ${binaryResponse.status}: ${binaryResponse.statusText} (${downloadUrl})`);
|
|
8597
8686
|
}
|
|
8598
|
-
|
|
8687
|
+
if (!binaryResponse.body) {
|
|
8688
|
+
throw new Error(`Download response for ${assetName} had no body`);
|
|
8689
|
+
}
|
|
8690
|
+
const advertised = Number.parseInt(binaryResponse.headers.get("content-length") ?? "", 10);
|
|
8691
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES) {
|
|
8692
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES}`);
|
|
8693
|
+
}
|
|
8599
8694
|
if (!checksumResponse.ok) {
|
|
8600
8695
|
warn(`Checksum verification failed: no checksums.sha256 found for ${tag}. ` + "Binary download aborted for security reasons.");
|
|
8601
8696
|
return null;
|
|
8602
8697
|
}
|
|
8603
8698
|
const checksumText = await checksumResponse.text();
|
|
8699
|
+
clearTimeout(checksumTimeout);
|
|
8700
|
+
checksumTimeout = null;
|
|
8604
8701
|
const expectedHash = parseChecksumForAsset(checksumText, assetName);
|
|
8605
8702
|
if (!expectedHash) {
|
|
8606
8703
|
warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
|
|
8607
8704
|
return null;
|
|
8608
8705
|
}
|
|
8609
|
-
const
|
|
8610
|
-
|
|
8706
|
+
const hash = createHash("sha256");
|
|
8707
|
+
let bytesWritten = 0;
|
|
8708
|
+
const guard = new TransformStream({
|
|
8709
|
+
transform(chunk, controller) {
|
|
8710
|
+
bytesWritten += chunk.byteLength;
|
|
8711
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES) {
|
|
8712
|
+
controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
8713
|
+
return;
|
|
8714
|
+
}
|
|
8715
|
+
hash.update(chunk);
|
|
8716
|
+
controller.enqueue(chunk);
|
|
8717
|
+
}
|
|
8718
|
+
});
|
|
8719
|
+
const guarded = binaryResponse.body.pipeThrough(guard);
|
|
8720
|
+
const nodeStream = Readable.fromWeb(guarded);
|
|
8721
|
+
await pipeline(nodeStream, createWriteStream(tmpPath), { signal: binaryController.signal });
|
|
8722
|
+
clearTimeout(binaryTimeout);
|
|
8723
|
+
binaryTimeout = null;
|
|
8724
|
+
const actualHash = hash.digest("hex");
|
|
8611
8725
|
if (actualHash !== expectedHash) {
|
|
8612
|
-
throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. The binary may have been tampered with
|
|
8726
|
+
throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. ` + "The binary may have been tampered with.");
|
|
8613
8727
|
}
|
|
8614
8728
|
log(`Checksum verified (SHA-256: ${actualHash.slice(0, 16)}...)`);
|
|
8615
|
-
const tmpPath = `${binaryPath}.tmp`;
|
|
8616
|
-
const { writeFileSync } = await import("fs");
|
|
8617
|
-
writeFileSync(tmpPath, Buffer.from(arrayBuffer));
|
|
8618
8729
|
if (process.platform !== "win32") {
|
|
8619
8730
|
chmodSync(tmpPath, 493);
|
|
8620
8731
|
}
|
|
8621
|
-
const { renameSync } = await import("fs");
|
|
8622
8732
|
renameSync(tmpPath, binaryPath);
|
|
8623
8733
|
log(`AFT binary ready at ${binaryPath}`);
|
|
8624
8734
|
return binaryPath;
|
|
8625
8735
|
} catch (err) {
|
|
8626
8736
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8627
8737
|
error(`Failed to download AFT binary: ${msg}`);
|
|
8628
|
-
const tmpPath = `${binaryPath}.tmp`;
|
|
8629
8738
|
if (existsSync(tmpPath)) {
|
|
8630
8739
|
try {
|
|
8631
8740
|
unlinkSync(tmpPath);
|
|
8632
8741
|
} catch {}
|
|
8633
8742
|
}
|
|
8634
8743
|
return null;
|
|
8744
|
+
} finally {
|
|
8745
|
+
if (binaryTimeout) {
|
|
8746
|
+
binaryController?.abort();
|
|
8747
|
+
clearTimeout(binaryTimeout);
|
|
8748
|
+
}
|
|
8749
|
+
if (checksumTimeout) {
|
|
8750
|
+
checksumController?.abort();
|
|
8751
|
+
clearTimeout(checksumTimeout);
|
|
8752
|
+
}
|
|
8753
|
+
releaseLock?.();
|
|
8635
8754
|
}
|
|
8636
8755
|
}
|
|
8637
8756
|
async function ensureBinary(version) {
|
|
@@ -8648,6 +8767,39 @@ async function ensureBinary(version) {
|
|
|
8648
8767
|
log("No cached binary found, downloading latest...");
|
|
8649
8768
|
return downloadBinary();
|
|
8650
8769
|
}
|
|
8770
|
+
async function acquireDownloadLock(lockPath) {
|
|
8771
|
+
const startedAt = Date.now();
|
|
8772
|
+
while (true) {
|
|
8773
|
+
try {
|
|
8774
|
+
const fd = openSync(lockPath, "wx");
|
|
8775
|
+
return () => {
|
|
8776
|
+
try {
|
|
8777
|
+
closeSync(fd);
|
|
8778
|
+
} catch {}
|
|
8779
|
+
try {
|
|
8780
|
+
rmSync(lockPath, { force: true });
|
|
8781
|
+
} catch {}
|
|
8782
|
+
};
|
|
8783
|
+
} catch (err) {
|
|
8784
|
+
const code = err.code;
|
|
8785
|
+
if (code !== "EEXIST")
|
|
8786
|
+
throw err;
|
|
8787
|
+
try {
|
|
8788
|
+
const ageMs = Date.now() - statSync(lockPath).mtimeMs;
|
|
8789
|
+
if (ageMs > DOWNLOAD_LOCK_STALE_MS) {
|
|
8790
|
+
rmSync(lockPath, { force: true });
|
|
8791
|
+
continue;
|
|
8792
|
+
}
|
|
8793
|
+
} catch {
|
|
8794
|
+
continue;
|
|
8795
|
+
}
|
|
8796
|
+
if (Date.now() - startedAt > DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
8797
|
+
throw new Error(`Timed out waiting for download lock: ${lockPath}`);
|
|
8798
|
+
}
|
|
8799
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
8800
|
+
}
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8651
8803
|
function parseChecksumForAsset(checksumText, assetName) {
|
|
8652
8804
|
for (const line of checksumText.split(`
|
|
8653
8805
|
`)) {
|
|
@@ -8662,9 +8814,12 @@ function parseChecksumForAsset(checksumText, assetName) {
|
|
|
8662
8814
|
return null;
|
|
8663
8815
|
}
|
|
8664
8816
|
async function fetchLatestTag() {
|
|
8817
|
+
const controller = new AbortController;
|
|
8818
|
+
const timeout = setTimeout(() => controller.abort(), LATEST_TAG_TIMEOUT_MS);
|
|
8665
8819
|
try {
|
|
8666
8820
|
const response = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
|
|
8667
|
-
headers: { Accept: "application/vnd.github.v3+json" }
|
|
8821
|
+
headers: { Accept: "application/vnd.github.v3+json" },
|
|
8822
|
+
signal: controller.signal
|
|
8668
8823
|
});
|
|
8669
8824
|
if (!response.ok)
|
|
8670
8825
|
return null;
|
|
@@ -8672,18 +8827,20 @@ async function fetchLatestTag() {
|
|
|
8672
8827
|
return data.tag_name ?? null;
|
|
8673
8828
|
} catch {
|
|
8674
8829
|
return null;
|
|
8830
|
+
} finally {
|
|
8831
|
+
clearTimeout(timeout);
|
|
8675
8832
|
}
|
|
8676
8833
|
}
|
|
8677
8834
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
8678
8835
|
import { execFileSync } from "child_process";
|
|
8679
|
-
import { createHash } from "crypto";
|
|
8680
|
-
import { chmodSync as chmodSync2, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, openSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, statSync, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
8836
|
+
import { createHash as createHash2 } from "crypto";
|
|
8837
|
+
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync, createWriteStream as createWriteStream2, existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, openSync as openSync2, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
8681
8838
|
import { dirname, join as join3, relative, resolve } from "path";
|
|
8682
|
-
import { Readable } from "stream";
|
|
8683
|
-
import { pipeline } from "stream/promises";
|
|
8839
|
+
import { Readable as Readable2 } from "stream";
|
|
8840
|
+
import { pipeline as pipeline2 } from "stream/promises";
|
|
8684
8841
|
var ORT_VERSION = "1.24.4";
|
|
8685
8842
|
var ORT_REPO = "microsoft/onnxruntime";
|
|
8686
|
-
var
|
|
8843
|
+
var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
|
|
8687
8844
|
var MAX_EXTRACT_BYTES = 1 * 1024 * 1024 * 1024;
|
|
8688
8845
|
var ONNX_LOCK_FILE = ".aft-onnx-installing";
|
|
8689
8846
|
var ONNX_INSTALLED_META_FILE = ".aft-onnx-installed";
|
|
@@ -8804,7 +8961,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
8804
8961
|
if (Number.isFinite(pid) && pid > 0) {
|
|
8805
8962
|
if (process.platform === "win32") {
|
|
8806
8963
|
try {
|
|
8807
|
-
const ageMs = Date.now() -
|
|
8964
|
+
const ageMs = Date.now() - statSync2(stagingDir).mtimeMs;
|
|
8808
8965
|
abandoned = ageMs > STALE_LOCK_MS;
|
|
8809
8966
|
} catch {
|
|
8810
8967
|
abandoned = true;
|
|
@@ -8818,7 +8975,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
8818
8975
|
if (abandoned) {
|
|
8819
8976
|
log(`[onnx] removing abandoned staging dir ${stagingDir}`);
|
|
8820
8977
|
try {
|
|
8821
|
-
|
|
8978
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
8822
8979
|
} catch (err) {
|
|
8823
8980
|
warn(`[onnx] failed to remove ${stagingDir}: ${err}`);
|
|
8824
8981
|
}
|
|
@@ -8830,7 +8987,7 @@ function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
|
8830
8987
|
try {
|
|
8831
8988
|
if (existsSync2(ortDir) && !existsSync2(join3(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
8832
8989
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
8833
|
-
|
|
8990
|
+
rmSync2(ortDir, { recursive: true, force: true });
|
|
8834
8991
|
}
|
|
8835
8992
|
} catch (err) {
|
|
8836
8993
|
warn(`[onnx] failed to sweep ${ortDir}: ${err}`);
|
|
@@ -8910,24 +9067,24 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
8910
9067
|
throw new Error(`download failed (HTTP ${res.status})`);
|
|
8911
9068
|
}
|
|
8912
9069
|
const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
|
|
8913
|
-
if (Number.isFinite(advertised) && advertised >
|
|
8914
|
-
throw new Error(`Content-Length ${advertised} exceeds max ${
|
|
9070
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
9071
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
8915
9072
|
}
|
|
8916
9073
|
mkdirSync2(dirname(destPath), { recursive: true });
|
|
8917
9074
|
let bytesWritten = 0;
|
|
8918
9075
|
const guard = new TransformStream({
|
|
8919
9076
|
transform(chunk, transformController) {
|
|
8920
9077
|
bytesWritten += chunk.byteLength;
|
|
8921
|
-
if (bytesWritten >
|
|
8922
|
-
transformController.error(new Error(`download exceeded ${
|
|
9078
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES2) {
|
|
9079
|
+
transformController.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES2} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
8923
9080
|
return;
|
|
8924
9081
|
}
|
|
8925
9082
|
transformController.enqueue(chunk);
|
|
8926
9083
|
}
|
|
8927
9084
|
});
|
|
8928
9085
|
const guarded = res.body.pipeThrough(guard);
|
|
8929
|
-
const nodeStream =
|
|
8930
|
-
await
|
|
9086
|
+
const nodeStream = Readable2.fromWeb(guarded);
|
|
9087
|
+
await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
|
|
8931
9088
|
} catch (err) {
|
|
8932
9089
|
try {
|
|
8933
9090
|
unlinkSync2(destPath);
|
|
@@ -9026,16 +9183,16 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
9026
9183
|
warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
|
|
9027
9184
|
}
|
|
9028
9185
|
writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
|
|
9029
|
-
|
|
9186
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
9030
9187
|
log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
|
|
9031
9188
|
return targetDir;
|
|
9032
9189
|
} catch (err) {
|
|
9033
9190
|
error(`Failed to download ONNX Runtime: ${err}`);
|
|
9034
9191
|
try {
|
|
9035
|
-
|
|
9192
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
9036
9193
|
} catch {}
|
|
9037
9194
|
try {
|
|
9038
|
-
|
|
9195
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
9039
9196
|
} catch {}
|
|
9040
9197
|
return null;
|
|
9041
9198
|
}
|
|
@@ -9052,7 +9209,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
9052
9209
|
}
|
|
9053
9210
|
} catch (copyErr) {
|
|
9054
9211
|
if (requiredLibs.has(libFile)) {
|
|
9055
|
-
|
|
9212
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
9056
9213
|
throw copyErr;
|
|
9057
9214
|
}
|
|
9058
9215
|
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
@@ -9067,7 +9224,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
9067
9224
|
symlinkSync(link.target, dst);
|
|
9068
9225
|
} catch (symlinkErr) {
|
|
9069
9226
|
if (requiredLibs.has(link.name)) {
|
|
9070
|
-
|
|
9227
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
9071
9228
|
throw symlinkErr;
|
|
9072
9229
|
}
|
|
9073
9230
|
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
@@ -9075,7 +9232,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
9075
9232
|
}
|
|
9076
9233
|
const requiredPath = join3(targetDir, info.libName);
|
|
9077
9234
|
if (!existsSync2(requiredPath)) {
|
|
9078
|
-
|
|
9235
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
9079
9236
|
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
9080
9237
|
}
|
|
9081
9238
|
}
|
|
@@ -9108,7 +9265,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
9108
9265
|
function readOnnxInstalledMeta(installDir) {
|
|
9109
9266
|
const path = join3(installDir, ONNX_INSTALLED_META_FILE);
|
|
9110
9267
|
try {
|
|
9111
|
-
if (!
|
|
9268
|
+
if (!statSync2(path).isFile())
|
|
9112
9269
|
return null;
|
|
9113
9270
|
const raw = readFileSync(path, "utf8");
|
|
9114
9271
|
const parsed = JSON.parse(raw);
|
|
@@ -9125,20 +9282,20 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
9125
9282
|
}
|
|
9126
9283
|
}
|
|
9127
9284
|
function sha256File(path) {
|
|
9128
|
-
const hash =
|
|
9285
|
+
const hash = createHash2("sha256");
|
|
9129
9286
|
hash.update(readFileSync(path));
|
|
9130
9287
|
return hash.digest("hex");
|
|
9131
9288
|
}
|
|
9132
9289
|
function acquireLock(lockPath) {
|
|
9133
9290
|
const tryClaim = () => {
|
|
9134
9291
|
try {
|
|
9135
|
-
const fd =
|
|
9292
|
+
const fd = openSync2(lockPath, "wx");
|
|
9136
9293
|
try {
|
|
9137
9294
|
writeFileSync(fd, `${process.pid}
|
|
9138
9295
|
${new Date().toISOString()}
|
|
9139
9296
|
`);
|
|
9140
9297
|
} finally {
|
|
9141
|
-
|
|
9298
|
+
closeSync2(fd);
|
|
9142
9299
|
}
|
|
9143
9300
|
return true;
|
|
9144
9301
|
} catch (err) {
|
|
@@ -9159,7 +9316,7 @@ ${new Date().toISOString()}
|
|
|
9159
9316
|
const parsed = Number.parseInt(firstLine, 10);
|
|
9160
9317
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
9161
9318
|
owningPid = parsed;
|
|
9162
|
-
lockMtimeMs =
|
|
9319
|
+
lockMtimeMs = statSync2(lockPath).mtimeMs;
|
|
9163
9320
|
} catch {
|
|
9164
9321
|
return tryClaim();
|
|
9165
9322
|
}
|
|
@@ -9276,7 +9433,9 @@ class BridgePool {
|
|
|
9276
9433
|
onVersionMismatch: options.onVersionMismatch,
|
|
9277
9434
|
onConfigureWarnings: options.onConfigureWarnings,
|
|
9278
9435
|
onBashCompletion: options.onBashCompletion,
|
|
9279
|
-
onBashLongRunning: options.onBashLongRunning
|
|
9436
|
+
onBashLongRunning: options.onBashLongRunning,
|
|
9437
|
+
errorPrefix: options.errorPrefix,
|
|
9438
|
+
logger: options.logger
|
|
9280
9439
|
};
|
|
9281
9440
|
this.configOverrides = configOverrides;
|
|
9282
9441
|
if (Number.isFinite(this.idleTimeoutMs)) {
|
|
@@ -9348,24 +9507,32 @@ class BridgePool {
|
|
|
9348
9507
|
}
|
|
9349
9508
|
async replaceBinary(newPath) {
|
|
9350
9509
|
this.binaryPath = newPath;
|
|
9351
|
-
const shutdowns = Array.from(this.bridges.values()).map((entry) => entry.bridge.shutdown());
|
|
9352
9510
|
this.bridges.clear();
|
|
9353
|
-
await Promise.allSettled(shutdowns);
|
|
9354
9511
|
this.log(`Binary path updated to ${newPath}. All bridges cleared \u2014 next calls will use the new binary.`);
|
|
9355
9512
|
return newPath;
|
|
9356
9513
|
}
|
|
9357
9514
|
log(message, meta) {
|
|
9358
9515
|
const logger = this.logger ?? getActiveLogger();
|
|
9359
|
-
if (logger)
|
|
9360
|
-
|
|
9361
|
-
|
|
9516
|
+
if (logger) {
|
|
9517
|
+
try {
|
|
9518
|
+
logger.log(message, meta);
|
|
9519
|
+
} catch (err) {
|
|
9520
|
+
console.error(`[aft-bridge] ERROR: pool logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
9521
|
+
console.error(`[aft-bridge] ${message}`);
|
|
9522
|
+
}
|
|
9523
|
+
} else
|
|
9362
9524
|
log(message, meta);
|
|
9363
9525
|
}
|
|
9364
9526
|
error(message, meta) {
|
|
9365
9527
|
const logger = this.logger ?? getActiveLogger();
|
|
9366
|
-
if (logger)
|
|
9367
|
-
|
|
9368
|
-
|
|
9528
|
+
if (logger) {
|
|
9529
|
+
try {
|
|
9530
|
+
logger.error(message, meta);
|
|
9531
|
+
} catch (err) {
|
|
9532
|
+
console.error(`[aft-bridge] ERROR: pool logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
9533
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
9534
|
+
}
|
|
9535
|
+
} else
|
|
9369
9536
|
error(message, meta);
|
|
9370
9537
|
}
|
|
9371
9538
|
setConfigureOverride(key, value) {
|
|
@@ -9392,7 +9559,7 @@ function normalizeKey(projectRoot) {
|
|
|
9392
9559
|
}
|
|
9393
9560
|
// ../aft-bridge/dist/resolver.js
|
|
9394
9561
|
import { execSync, spawnSync } from "child_process";
|
|
9395
|
-
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync } from "fs";
|
|
9562
|
+
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync2 } from "fs";
|
|
9396
9563
|
import { createRequire } from "module";
|
|
9397
9564
|
import { homedir as homedir4 } from "os";
|
|
9398
9565
|
import { join as join4 } from "path";
|
|
@@ -9423,15 +9590,19 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
9423
9590
|
const versionedDir = join4(cacheDir, tag);
|
|
9424
9591
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
9425
9592
|
const cachedPath = join4(versionedDir, `aft${ext}`);
|
|
9426
|
-
if (existsSync3(cachedPath))
|
|
9427
|
-
|
|
9593
|
+
if (existsSync3(cachedPath)) {
|
|
9594
|
+
const cachedVersion = readBinaryVersion(cachedPath);
|
|
9595
|
+
if (cachedVersion === version)
|
|
9596
|
+
return cachedPath;
|
|
9597
|
+
warn(`Cached binary at ${cachedPath} reports ${cachedVersion ?? "no version"}, expected ${version}; refreshing from npm package`);
|
|
9598
|
+
}
|
|
9428
9599
|
mkdirSync3(versionedDir, { recursive: true });
|
|
9429
|
-
const tmpPath = `${cachedPath}.tmp`;
|
|
9600
|
+
const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
|
|
9430
9601
|
copyFileSync2(npmBinaryPath, tmpPath);
|
|
9431
9602
|
if (process.platform !== "win32") {
|
|
9432
9603
|
chmodSync3(tmpPath, 493);
|
|
9433
9604
|
}
|
|
9434
|
-
|
|
9605
|
+
renameSync2(tmpPath, cachedPath);
|
|
9435
9606
|
log(`Copied npm binary to versioned cache: ${cachedPath}`);
|
|
9436
9607
|
return cachedPath;
|
|
9437
9608
|
} catch (err) {
|
|
@@ -9439,6 +9610,17 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
9439
9610
|
return null;
|
|
9440
9611
|
}
|
|
9441
9612
|
}
|
|
9613
|
+
function normalizeBareVersion(version) {
|
|
9614
|
+
return version.startsWith("v") ? version.slice(1) : version;
|
|
9615
|
+
}
|
|
9616
|
+
function isExpectedCachedBinary(binaryPath, expectedVersion) {
|
|
9617
|
+
const expected = normalizeBareVersion(expectedVersion);
|
|
9618
|
+
const actual = readBinaryVersion(binaryPath);
|
|
9619
|
+
if (actual === expected)
|
|
9620
|
+
return true;
|
|
9621
|
+
warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; skipping cache candidate`);
|
|
9622
|
+
return false;
|
|
9623
|
+
}
|
|
9442
9624
|
function platformKey(platform = process.platform, arch = process.arch) {
|
|
9443
9625
|
const archMap = PLATFORM_ARCH_MAP[platform];
|
|
9444
9626
|
if (!archMap) {
|
|
@@ -9463,7 +9645,7 @@ function findBinarySync(expectedVersion) {
|
|
|
9463
9645
|
if (pluginVersion) {
|
|
9464
9646
|
const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
|
|
9465
9647
|
const versionCached = getCachedBinaryPath(tag);
|
|
9466
|
-
if (versionCached)
|
|
9648
|
+
if (versionCached && isExpectedCachedBinary(versionCached, pluginVersion))
|
|
9467
9649
|
return versionCached;
|
|
9468
9650
|
}
|
|
9469
9651
|
try {
|
|
@@ -9473,10 +9655,12 @@ function findBinarySync(expectedVersion) {
|
|
|
9473
9655
|
const resolved = req.resolve(packageBin);
|
|
9474
9656
|
if (existsSync3(resolved)) {
|
|
9475
9657
|
const npmVersion = readBinaryVersion(resolved);
|
|
9476
|
-
if (
|
|
9658
|
+
if (npmVersion === null) {
|
|
9659
|
+
warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
|
|
9660
|
+
} else if (pluginVersion && npmVersion !== normalizeBareVersion(pluginVersion)) {
|
|
9477
9661
|
warn(`npm platform package binary v${npmVersion} does not match plugin v${pluginVersion}; skipping (continuing to PATH lookup)`);
|
|
9478
9662
|
} else {
|
|
9479
|
-
const copied = copyToVersionedCache(resolved, npmVersion
|
|
9663
|
+
const copied = copyToVersionedCache(resolved, npmVersion);
|
|
9480
9664
|
return copied ?? resolved;
|
|
9481
9665
|
}
|
|
9482
9666
|
}
|
|
@@ -9525,7 +9709,7 @@ async function findBinary(expectedVersion) {
|
|
|
9525
9709
|
`));
|
|
9526
9710
|
}
|
|
9527
9711
|
// ../aft-bridge/dist/url-fetch.js
|
|
9528
|
-
import { createHash as
|
|
9712
|
+
import { createHash as createHash3 } from "crypto";
|
|
9529
9713
|
import { lookup } from "dns/promises";
|
|
9530
9714
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
9531
9715
|
import { isIP } from "net";
|
|
@@ -9539,7 +9723,7 @@ function cacheDir(storageDir) {
|
|
|
9539
9723
|
return join5(storageDir, "url_cache");
|
|
9540
9724
|
}
|
|
9541
9725
|
function hashUrl(url) {
|
|
9542
|
-
return
|
|
9726
|
+
return createHash3("sha256").update(url).digest("hex").slice(0, 16);
|
|
9543
9727
|
}
|
|
9544
9728
|
function metaPath(storageDir, hash) {
|
|
9545
9729
|
return join5(cacheDir(storageDir), `${hash}.meta.json`);
|
|
@@ -9797,8 +9981,8 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
9797
9981
|
const contentFile = contentPath(storageDir, hash, extension);
|
|
9798
9982
|
const tmpContent = `${contentFile}.tmp-${process.pid}`;
|
|
9799
9983
|
writeFileSync2(tmpContent, body);
|
|
9800
|
-
const { renameSync:
|
|
9801
|
-
|
|
9984
|
+
const { renameSync: renameSync3 } = await import("fs");
|
|
9985
|
+
renameSync3(tmpContent, contentFile);
|
|
9802
9986
|
const meta = {
|
|
9803
9987
|
url,
|
|
9804
9988
|
contentType,
|
|
@@ -9807,7 +9991,7 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
9807
9991
|
};
|
|
9808
9992
|
const tmpMeta = `${metaFile}.tmp-${process.pid}`;
|
|
9809
9993
|
writeFileSync2(tmpMeta, JSON.stringify(meta));
|
|
9810
|
-
|
|
9994
|
+
renameSync3(tmpMeta, metaFile);
|
|
9811
9995
|
log(`URL cached (${total} bytes): ${url}`);
|
|
9812
9996
|
return contentFile;
|
|
9813
9997
|
}
|
|
@@ -9961,24 +10145,24 @@ function warn2(message, data) {
|
|
|
9961
10145
|
function error2(message, data) {
|
|
9962
10146
|
write("ERROR", message, data);
|
|
9963
10147
|
}
|
|
9964
|
-
function
|
|
10148
|
+
function sessionLog(sessionId, message, data) {
|
|
9965
10149
|
write("INFO", message, data, sessionId);
|
|
9966
10150
|
}
|
|
9967
|
-
function
|
|
10151
|
+
function sessionWarn(sessionId, message, data) {
|
|
9968
10152
|
write("WARN", message, data, sessionId);
|
|
9969
10153
|
}
|
|
9970
|
-
function
|
|
10154
|
+
function sessionError(sessionId, message, data) {
|
|
9971
10155
|
write("ERROR", message, data, sessionId);
|
|
9972
10156
|
}
|
|
9973
10157
|
var bridgeLogger = {
|
|
9974
10158
|
log(message, meta) {
|
|
9975
|
-
|
|
10159
|
+
sessionLog(meta?.sessionId, message);
|
|
9976
10160
|
},
|
|
9977
10161
|
warn(message, meta) {
|
|
9978
|
-
|
|
10162
|
+
sessionWarn(meta?.sessionId, message);
|
|
9979
10163
|
},
|
|
9980
10164
|
error(message, meta) {
|
|
9981
|
-
|
|
10165
|
+
sessionError(meta?.sessionId, message);
|
|
9982
10166
|
},
|
|
9983
10167
|
getLogFilePath: () => logFile
|
|
9984
10168
|
};
|
|
@@ -10073,6 +10257,7 @@ var sessionBgStates = new Map;
|
|
|
10073
10257
|
var SESSION_BG_STATE_IDLE_TTL_MS = 60 * 60 * 1000;
|
|
10074
10258
|
var DEBOUNCE_STEP_MS = 200;
|
|
10075
10259
|
var DEBOUNCE_CAP_MS = 1000;
|
|
10260
|
+
var MAX_WAKE_SEND_ATTEMPTS = 5;
|
|
10076
10261
|
var UNKNOWN_COMPLETION_TTL_MS = 5000;
|
|
10077
10262
|
var UNKNOWN_COMPLETION_CAP = 32;
|
|
10078
10263
|
var DEFAULT_SESSION_ID = "__default__";
|
|
@@ -10125,17 +10310,24 @@ async function appendInTurnBgCompletions(drainContext, output) {
|
|
|
10125
10310
|
return;
|
|
10126
10311
|
const state = stateFor(drainContext.sessionID);
|
|
10127
10312
|
if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0) {
|
|
10128
|
-
|
|
10313
|
+
await drainCompletions(drainContext);
|
|
10314
|
+
if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0) {
|
|
10315
|
+
return;
|
|
10316
|
+
}
|
|
10129
10317
|
}
|
|
10130
|
-
if (state.outstandingTaskIds.size > 0) {
|
|
10318
|
+
if (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted) {
|
|
10131
10319
|
await drainCompletions(drainContext);
|
|
10132
10320
|
}
|
|
10133
10321
|
if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
10134
10322
|
return;
|
|
10323
|
+
const deliveredCompletions = [...state.pendingCompletions];
|
|
10135
10324
|
const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning);
|
|
10136
10325
|
output.output = appendReminder(output.output ?? "", reminder);
|
|
10137
10326
|
state.pendingCompletions = [];
|
|
10138
10327
|
state.pendingLongRunning = [];
|
|
10328
|
+
state.wakeRetryAttempts = 0;
|
|
10329
|
+
state.wakeHardStopped = false;
|
|
10330
|
+
await ackCompletions(drainContext, deliveredCompletions);
|
|
10139
10331
|
if (state.debounceTimer) {
|
|
10140
10332
|
clearTimeout(state.debounceTimer);
|
|
10141
10333
|
state.debounceTimer = null;
|
|
@@ -10149,12 +10341,12 @@ async function handleIdleBgCompletions(drainContext) {
|
|
|
10149
10341
|
}
|
|
10150
10342
|
async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
10151
10343
|
const state = stateFor(drainContext.sessionID);
|
|
10152
|
-
if (!skipDrain && state.outstandingTaskIds.size > 0) {
|
|
10344
|
+
if (!skipDrain && (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
|
|
10153
10345
|
await drainCompletions(drainContext);
|
|
10154
10346
|
}
|
|
10155
10347
|
if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
10156
10348
|
return;
|
|
10157
|
-
scheduleWake(state, async (reminder) => {
|
|
10349
|
+
scheduleWake(state, async (reminder, deliveredCompletions) => {
|
|
10158
10350
|
const client = drainContext.client;
|
|
10159
10351
|
if (typeof client.session?.promptAsync !== "function") {
|
|
10160
10352
|
throw new Error("client.session.promptAsync is unavailable");
|
|
@@ -10178,8 +10370,9 @@ async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
|
10178
10370
|
path: { id: drainContext.sessionID },
|
|
10179
10371
|
body
|
|
10180
10372
|
});
|
|
10181
|
-
|
|
10182
|
-
|
|
10373
|
+
await ackCompletions(drainContext, deliveredCompletions);
|
|
10374
|
+
}, (err, hardStopped) => {
|
|
10375
|
+
sessionWarn(drainContext.sessionID, hardStopped ? `${LOG_PREFIX} wake send failed ${MAX_WAKE_SEND_ATTEMPTS} times; stopping retries: ${err instanceof Error ? err.message : String(err)}` : `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10183
10376
|
});
|
|
10184
10377
|
}
|
|
10185
10378
|
function formatSystemReminder(completions) {
|
|
@@ -10230,19 +10423,40 @@ function extractSessionID(value) {
|
|
|
10230
10423
|
return;
|
|
10231
10424
|
}
|
|
10232
10425
|
async function drainCompletions({ ctx, directory, sessionID }) {
|
|
10426
|
+
const state = stateFor(sessionID);
|
|
10233
10427
|
try {
|
|
10234
10428
|
const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
|
|
10235
10429
|
const response = await bridge.send("bash_drain_completions", { session_id: sessionID });
|
|
10236
10430
|
if (response.success === false) {
|
|
10237
|
-
|
|
10431
|
+
sessionWarn(sessionID, `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
|
|
10238
10432
|
return;
|
|
10239
10433
|
}
|
|
10240
|
-
|
|
10434
|
+
state.forcedDrainCompleted = true;
|
|
10435
|
+
ingestDrainedBgCompletions(sessionID, response.bg_completions);
|
|
10436
|
+
} catch (err) {
|
|
10437
|
+
sessionWarn(sessionID, `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10438
|
+
}
|
|
10439
|
+
}
|
|
10440
|
+
async function ackCompletions({ ctx, directory, sessionID }, completions) {
|
|
10441
|
+
const taskIds = [...new Set(completions.map((completion) => completion.task_id))];
|
|
10442
|
+
if (taskIds.length === 0)
|
|
10443
|
+
return;
|
|
10444
|
+
try {
|
|
10445
|
+
const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
|
|
10446
|
+
const response = await bridge.send("bash_ack_completions", {
|
|
10447
|
+
session_id: sessionID,
|
|
10448
|
+
task_ids: taskIds
|
|
10449
|
+
});
|
|
10450
|
+
if (response.success === false) {
|
|
10451
|
+
sessionWarn(sessionID, `${LOG_PREFIX} ack failed: ${String(response.message ?? "unknown error")}`);
|
|
10452
|
+
}
|
|
10241
10453
|
} catch (err) {
|
|
10242
|
-
|
|
10454
|
+
sessionWarn(sessionID, `${LOG_PREFIX} ack failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10243
10455
|
}
|
|
10244
10456
|
}
|
|
10245
10457
|
function scheduleWake(state, sendWake, onSendFailure) {
|
|
10458
|
+
if (state.wakeHardStopped)
|
|
10459
|
+
return;
|
|
10246
10460
|
const now = Date.now();
|
|
10247
10461
|
const pendingCount = state.pendingCompletions.length + state.pendingLongRunning.length;
|
|
10248
10462
|
if (state.debounceTimer && pendingCount <= state.scheduledCompletionCount) {
|
|
@@ -10271,13 +10485,22 @@ function scheduleWake(state, sendWake, onSendFailure) {
|
|
|
10271
10485
|
const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
|
|
10272
10486
|
state.pendingCompletions = [];
|
|
10273
10487
|
state.pendingLongRunning = [];
|
|
10274
|
-
sendWake(reminder).then(() => {
|
|
10488
|
+
sendWake(reminder, pending).then(() => {
|
|
10275
10489
|
state.retryDelayMs = null;
|
|
10490
|
+
state.wakeRetryAttempts = 0;
|
|
10491
|
+
state.wakeHardStopped = false;
|
|
10276
10492
|
}).catch((err) => {
|
|
10277
10493
|
state.pendingCompletions = [...pending, ...state.pendingCompletions];
|
|
10278
10494
|
state.pendingLongRunning = [...pendingLongRunning, ...state.pendingLongRunning];
|
|
10495
|
+
state.wakeRetryAttempts += 1;
|
|
10496
|
+
if (state.wakeRetryAttempts >= MAX_WAKE_SEND_ATTEMPTS) {
|
|
10497
|
+
state.retryDelayMs = null;
|
|
10498
|
+
state.wakeHardStopped = true;
|
|
10499
|
+
onSendFailure(err, true);
|
|
10500
|
+
return;
|
|
10501
|
+
}
|
|
10279
10502
|
state.retryDelayMs = Math.min((delay || DEBOUNCE_STEP_MS) * 2, DEBOUNCE_CAP_MS);
|
|
10280
|
-
onSendFailure(err);
|
|
10503
|
+
onSendFailure(err, false);
|
|
10281
10504
|
scheduleWake(state, sendWake, onSendFailure);
|
|
10282
10505
|
});
|
|
10283
10506
|
}, delay);
|
|
@@ -10298,6 +10521,9 @@ function stateFor(sessionID) {
|
|
|
10298
10521
|
scheduledFireAt: null,
|
|
10299
10522
|
scheduledCompletionCount: 0,
|
|
10300
10523
|
retryDelayMs: null,
|
|
10524
|
+
wakeRetryAttempts: 0,
|
|
10525
|
+
wakeHardStopped: false,
|
|
10526
|
+
forcedDrainCompleted: false,
|
|
10301
10527
|
unknownCompletions: [],
|
|
10302
10528
|
lastSeenAt: now
|
|
10303
10529
|
};
|
|
@@ -10307,6 +10533,22 @@ function stateFor(sessionID) {
|
|
|
10307
10533
|
}
|
|
10308
10534
|
return state;
|
|
10309
10535
|
}
|
|
10536
|
+
function ingestDrainedBgCompletions(sessionID, completions) {
|
|
10537
|
+
if (!Array.isArray(completions) || completions.length === 0)
|
|
10538
|
+
return [];
|
|
10539
|
+
const state = stateFor(sessionID);
|
|
10540
|
+
const accepted = [];
|
|
10541
|
+
for (const completion of completions) {
|
|
10542
|
+
if (!isBgCompletion(completion))
|
|
10543
|
+
continue;
|
|
10544
|
+
state.outstandingTaskIds.delete(completion.task_id);
|
|
10545
|
+
if (!state.pendingCompletions.some((pending) => pending.task_id === completion.task_id) && !accepted.some((pending) => pending.task_id === completion.task_id)) {
|
|
10546
|
+
accepted.push(completion);
|
|
10547
|
+
}
|
|
10548
|
+
}
|
|
10549
|
+
state.pendingCompletions.push(...accepted);
|
|
10550
|
+
return accepted;
|
|
10551
|
+
}
|
|
10310
10552
|
function cleanupIdleSessionStates(now) {
|
|
10311
10553
|
const cutoff = now - SESSION_BG_STATE_IDLE_TTL_MS;
|
|
10312
10554
|
for (const [sessionID, state] of sessionBgStates) {
|
|
@@ -10397,7 +10639,7 @@ function formatDuration(completion) {
|
|
|
10397
10639
|
|
|
10398
10640
|
// src/config.ts
|
|
10399
10641
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
10400
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as
|
|
10642
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
10401
10643
|
import { homedir as homedir5 } from "os";
|
|
10402
10644
|
import { join as join7 } from "path";
|
|
10403
10645
|
|
|
@@ -24176,7 +24418,7 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
|
|
|
24176
24418
|
${serialized}` : serialized;
|
|
24177
24419
|
tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
24178
24420
|
writeFileSync3(tmpPath, nextContent, "utf-8");
|
|
24179
|
-
|
|
24421
|
+
renameSync3(tmpPath, configPath);
|
|
24180
24422
|
logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
|
|
24181
24423
|
return { migrated: true, oldKeys };
|
|
24182
24424
|
} catch (err) {
|
|
@@ -24411,19 +24653,19 @@ function loadAftConfig(projectDirectory) {
|
|
|
24411
24653
|
}
|
|
24412
24654
|
|
|
24413
24655
|
// src/hooks/auto-update-checker/index.ts
|
|
24414
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, renameSync as
|
|
24656
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
24415
24657
|
import { dirname as dirname4, join as join11 } from "path";
|
|
24416
24658
|
|
|
24417
24659
|
// src/hooks/auto-update-checker/cache.ts
|
|
24418
24660
|
var import_comment_json3 = __toESM(require_src2(), 1);
|
|
24419
24661
|
import { spawn as spawn2 } from "child_process";
|
|
24420
|
-
import { cpSync, existsSync as existsSync7, mkdtempSync, readFileSync as readFileSync5, rmSync as
|
|
24662
|
+
import { cpSync, existsSync as existsSync7, mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
24421
24663
|
import { tmpdir as tmpdir2 } from "os";
|
|
24422
24664
|
import { basename, dirname as dirname3, join as join10 } from "path";
|
|
24423
24665
|
|
|
24424
24666
|
// src/hooks/auto-update-checker/checker.ts
|
|
24425
24667
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
24426
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, statSync as
|
|
24668
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
24427
24669
|
import { homedir as homedir7 } from "os";
|
|
24428
24670
|
import { dirname as dirname2, isAbsolute, join as join9, resolve as resolve2 } from "path";
|
|
24429
24671
|
import { fileURLToPath } from "url";
|
|
@@ -24550,7 +24792,7 @@ function getLocalDevPath(directory) {
|
|
|
24550
24792
|
}
|
|
24551
24793
|
function findPackageJsonUp(startPath) {
|
|
24552
24794
|
try {
|
|
24553
|
-
const stat =
|
|
24795
|
+
const stat = statSync3(startPath);
|
|
24554
24796
|
let dir = stat.isDirectory() ? startPath : dirname2(startPath);
|
|
24555
24797
|
for (let i = 0;i < 10; i++) {
|
|
24556
24798
|
const pkgPath = join9(dir, "package.json");
|
|
@@ -24690,19 +24932,19 @@ function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
|
|
|
24690
24932
|
function restoreAutoUpdateSnapshot(snapshot) {
|
|
24691
24933
|
try {
|
|
24692
24934
|
if (snapshot.packageJson === null)
|
|
24693
|
-
|
|
24935
|
+
rmSync3(snapshot.packageJsonPath, { force: true });
|
|
24694
24936
|
else
|
|
24695
24937
|
writeFileSync5(snapshot.packageJsonPath, snapshot.packageJson);
|
|
24696
24938
|
if (snapshot.lockfile === null)
|
|
24697
|
-
|
|
24939
|
+
rmSync3(snapshot.lockfilePath, { force: true });
|
|
24698
24940
|
else
|
|
24699
24941
|
writeFileSync5(snapshot.lockfilePath, snapshot.lockfile);
|
|
24700
|
-
|
|
24942
|
+
rmSync3(snapshot.packageDir, { recursive: true, force: true });
|
|
24701
24943
|
if (snapshot.stagedPackageDir) {
|
|
24702
24944
|
cpSync(snapshot.stagedPackageDir, snapshot.packageDir, { recursive: true });
|
|
24703
24945
|
}
|
|
24704
24946
|
} finally {
|
|
24705
|
-
|
|
24947
|
+
rmSync3(snapshot.tempDir, { recursive: true, force: true });
|
|
24706
24948
|
}
|
|
24707
24949
|
}
|
|
24708
24950
|
function stripPackageNameFromPath(pathValue, packageName) {
|
|
@@ -24767,7 +25009,7 @@ function removeInstalledPackage(installDir, packageName) {
|
|
|
24767
25009
|
const packageDir = join10(installDir, "node_modules", packageName);
|
|
24768
25010
|
if (!existsSync7(packageDir))
|
|
24769
25011
|
return false;
|
|
24770
|
-
|
|
25012
|
+
rmSync3(packageDir, { recursive: true, force: true });
|
|
24771
25013
|
log2(`[auto-update-checker] Package removed: ${packageDir}`);
|
|
24772
25014
|
return true;
|
|
24773
25015
|
}
|
|
@@ -24852,7 +25094,7 @@ async function runNpmInstallSafe(installDir, options = {}) {
|
|
|
24852
25094
|
if (!result && snapshot) {
|
|
24853
25095
|
restoreAutoUpdateSnapshot(snapshot);
|
|
24854
25096
|
} else if (snapshot) {
|
|
24855
|
-
|
|
25097
|
+
rmSync3(snapshot.tempDir, { recursive: true, force: true });
|
|
24856
25098
|
}
|
|
24857
25099
|
return result;
|
|
24858
25100
|
} catch (err) {
|
|
@@ -24936,7 +25178,7 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
24936
25178
|
mkdirSync5(dirname4(file2), { recursive: true });
|
|
24937
25179
|
const tmp = `${file2}.tmp.${process.pid}`;
|
|
24938
25180
|
writeFileSync6(tmp, JSON.stringify({ lastCheckedMs: Date.now() }), "utf-8");
|
|
24939
|
-
|
|
25181
|
+
renameSync4(tmp, file2);
|
|
24940
25182
|
return true;
|
|
24941
25183
|
} catch (err) {
|
|
24942
25184
|
warn2(`[auto-update-checker] Could not coordinate via timestamp file: ${String(err)}`);
|
|
@@ -25023,17 +25265,17 @@ function showToast(ctx, title, message, variant = "info", duration3 = 3000) {
|
|
|
25023
25265
|
|
|
25024
25266
|
// src/lsp-auto-install.ts
|
|
25025
25267
|
import { spawn as spawn3 } from "child_process";
|
|
25026
|
-
import { createHash as
|
|
25027
|
-
import { createReadStream, mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as
|
|
25268
|
+
import { createHash as createHash4 } from "crypto";
|
|
25269
|
+
import { createReadStream, mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync5 } from "fs";
|
|
25028
25270
|
import { join as join14 } from "path";
|
|
25029
25271
|
|
|
25030
25272
|
// src/lsp-cache.ts
|
|
25031
25273
|
import {
|
|
25032
|
-
closeSync as
|
|
25274
|
+
closeSync as closeSync3,
|
|
25033
25275
|
mkdirSync as mkdirSync6,
|
|
25034
|
-
openSync as
|
|
25276
|
+
openSync as openSync3,
|
|
25035
25277
|
readFileSync as readFileSync7,
|
|
25036
|
-
statSync as
|
|
25278
|
+
statSync as statSync4,
|
|
25037
25279
|
unlinkSync as unlinkSync5,
|
|
25038
25280
|
writeFileSync as writeFileSync7
|
|
25039
25281
|
} from "fs";
|
|
@@ -25066,7 +25308,7 @@ function lspBinDir(npmPackage) {
|
|
|
25066
25308
|
function isInstalled(npmPackage, binary) {
|
|
25067
25309
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
25068
25310
|
try {
|
|
25069
|
-
if (
|
|
25311
|
+
if (statSync4(join12(lspBinDir(npmPackage), candidate)).isFile())
|
|
25070
25312
|
return true;
|
|
25071
25313
|
} catch {}
|
|
25072
25314
|
}
|
|
@@ -25094,7 +25336,7 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
25094
25336
|
function readInstalledMetaIn(installDir) {
|
|
25095
25337
|
const path2 = join12(installDir, INSTALLED_META_FILE);
|
|
25096
25338
|
try {
|
|
25097
|
-
if (!
|
|
25339
|
+
if (!statSync4(path2).isFile())
|
|
25098
25340
|
return null;
|
|
25099
25341
|
const raw = readFileSync7(path2, "utf8");
|
|
25100
25342
|
const parsed = JSON.parse(raw);
|
|
@@ -25124,13 +25366,13 @@ function acquireInstallLock(lockKey) {
|
|
|
25124
25366
|
const lock = lockPath(lockKey);
|
|
25125
25367
|
const tryClaim = () => {
|
|
25126
25368
|
try {
|
|
25127
|
-
const fd =
|
|
25369
|
+
const fd = openSync3(lock, "wx");
|
|
25128
25370
|
try {
|
|
25129
25371
|
writeFileSync7(fd, `${process.pid}
|
|
25130
25372
|
${new Date().toISOString()}
|
|
25131
25373
|
`);
|
|
25132
25374
|
} finally {
|
|
25133
|
-
|
|
25375
|
+
closeSync3(fd);
|
|
25134
25376
|
}
|
|
25135
25377
|
return true;
|
|
25136
25378
|
} catch (err) {
|
|
@@ -25151,7 +25393,7 @@ ${new Date().toISOString()}
|
|
|
25151
25393
|
const parsed = Number.parseInt(firstLine, 10);
|
|
25152
25394
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
25153
25395
|
owningPid = parsed;
|
|
25154
|
-
lockMtimeMs =
|
|
25396
|
+
lockMtimeMs = statSync4(lock).mtimeMs;
|
|
25155
25397
|
} catch {
|
|
25156
25398
|
return tryClaim();
|
|
25157
25399
|
}
|
|
@@ -25692,7 +25934,7 @@ function hashInstalledBinary(spec) {
|
|
|
25692
25934
|
let pathToHash = null;
|
|
25693
25935
|
for (const p of candidates) {
|
|
25694
25936
|
try {
|
|
25695
|
-
if (
|
|
25937
|
+
if (statSync5(p).isFile()) {
|
|
25696
25938
|
pathToHash = p;
|
|
25697
25939
|
break;
|
|
25698
25940
|
}
|
|
@@ -25702,7 +25944,7 @@ function hashInstalledBinary(spec) {
|
|
|
25702
25944
|
reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
|
|
25703
25945
|
return;
|
|
25704
25946
|
}
|
|
25705
|
-
const hash2 =
|
|
25947
|
+
const hash2 = createHash4("sha256");
|
|
25706
25948
|
const stream = createReadStream(pathToHash);
|
|
25707
25949
|
stream.on("error", reject);
|
|
25708
25950
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
@@ -25718,14 +25960,14 @@ function installedBinaryPath(spec) {
|
|
|
25718
25960
|
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
25719
25961
|
for (const candidate of candidates) {
|
|
25720
25962
|
try {
|
|
25721
|
-
if (
|
|
25963
|
+
if (statSync5(candidate).isFile())
|
|
25722
25964
|
return candidate;
|
|
25723
25965
|
} catch {}
|
|
25724
25966
|
}
|
|
25725
25967
|
return null;
|
|
25726
25968
|
}
|
|
25727
25969
|
function sha256OfFileSync(path2) {
|
|
25728
|
-
return
|
|
25970
|
+
return createHash4("sha256").update(readFileSync8(path2)).digest("hex");
|
|
25729
25971
|
}
|
|
25730
25972
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
25731
25973
|
const packageDir = lspPackageDir(spec.npm);
|
|
@@ -25733,8 +25975,8 @@ function quarantineCachedNpmInstall(spec, reason) {
|
|
|
25733
25975
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
25734
25976
|
try {
|
|
25735
25977
|
mkdirSync7(join14(dest, ".."), { recursive: true });
|
|
25736
|
-
|
|
25737
|
-
|
|
25978
|
+
rmSync4(dest, { recursive: true, force: true });
|
|
25979
|
+
renameSync5(packageDir, dest);
|
|
25738
25980
|
} catch (err) {
|
|
25739
25981
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
25740
25982
|
}
|
|
@@ -25810,11 +26052,11 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
|
|
|
25810
26052
|
|
|
25811
26053
|
// src/lsp-github-install.ts
|
|
25812
26054
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
25813
|
-
import { createHash as
|
|
26055
|
+
import { createHash as createHash5, randomBytes } from "crypto";
|
|
25814
26056
|
import {
|
|
25815
26057
|
copyFileSync as copyFileSync3,
|
|
25816
26058
|
createReadStream as createReadStream2,
|
|
25817
|
-
createWriteStream as
|
|
26059
|
+
createWriteStream as createWriteStream3,
|
|
25818
26060
|
existsSync as existsSync10,
|
|
25819
26061
|
lstatSync as lstatSync2,
|
|
25820
26062
|
mkdirSync as mkdirSync8,
|
|
@@ -25822,14 +26064,15 @@ import {
|
|
|
25822
26064
|
readFileSync as readFileSync9,
|
|
25823
26065
|
readlinkSync as readlinkSync2,
|
|
25824
26066
|
realpathSync as realpathSync3,
|
|
25825
|
-
renameSync as
|
|
25826
|
-
rmSync as
|
|
25827
|
-
statSync as
|
|
25828
|
-
unlinkSync as unlinkSync6
|
|
26067
|
+
renameSync as renameSync6,
|
|
26068
|
+
rmSync as rmSync5,
|
|
26069
|
+
statSync as statSync6,
|
|
26070
|
+
unlinkSync as unlinkSync6,
|
|
26071
|
+
writeFileSync as writeFileSync8
|
|
25829
26072
|
} from "fs";
|
|
25830
26073
|
import { dirname as dirname5, join as join15, relative as relative2, resolve as resolve3 } from "path";
|
|
25831
|
-
import { Readable as
|
|
25832
|
-
import { pipeline as
|
|
26074
|
+
import { Readable as Readable3 } from "stream";
|
|
26075
|
+
import { pipeline as pipeline3 } from "stream/promises";
|
|
25833
26076
|
|
|
25834
26077
|
// src/lsp-github-table.ts
|
|
25835
26078
|
function exe(platform2, name) {
|
|
@@ -25928,6 +26171,7 @@ function ghBinDir(spec) {
|
|
|
25928
26171
|
function ghExtractDir(spec) {
|
|
25929
26172
|
return join15(ghPackageDir(spec), "extracted");
|
|
25930
26173
|
}
|
|
26174
|
+
var INSTALLED_META_FILE2 = ".aft-installed";
|
|
25931
26175
|
function ghBinaryPath(spec, platform2) {
|
|
25932
26176
|
const ext = platform2 === "win32" ? ".exe" : "";
|
|
25933
26177
|
return join15(ghBinDir(spec), `${spec.binary}${ext}`);
|
|
@@ -25935,7 +26179,7 @@ function ghBinaryPath(spec, platform2) {
|
|
|
25935
26179
|
function isGithubInstalled(spec, platform2) {
|
|
25936
26180
|
for (const candidate of ghBinaryCandidates(spec, platform2)) {
|
|
25937
26181
|
try {
|
|
25938
|
-
if (
|
|
26182
|
+
if (statSync6(join15(ghBinDir(spec), candidate)).isFile())
|
|
25939
26183
|
return true;
|
|
25940
26184
|
} catch {}
|
|
25941
26185
|
}
|
|
@@ -25946,11 +26190,45 @@ function ghBinaryCandidates(spec, platform2) {
|
|
|
25946
26190
|
return [spec.binary];
|
|
25947
26191
|
return [spec.binary, `${spec.binary}.cmd`, `${spec.binary}.exe`, `${spec.binary}.bat`];
|
|
25948
26192
|
}
|
|
25949
|
-
|
|
26193
|
+
function readGithubInstalledMetaIn(installDir) {
|
|
26194
|
+
try {
|
|
26195
|
+
const path2 = join15(installDir, INSTALLED_META_FILE2);
|
|
26196
|
+
if (!statSync6(path2).isFile())
|
|
26197
|
+
return null;
|
|
26198
|
+
const parsed = JSON.parse(readFileSync9(path2, "utf8"));
|
|
26199
|
+
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
26200
|
+
return null;
|
|
26201
|
+
return {
|
|
26202
|
+
version: parsed.version,
|
|
26203
|
+
installedAt: typeof parsed.installedAt === "string" ? parsed.installedAt : "",
|
|
26204
|
+
...typeof parsed.sha256 === "string" && parsed.sha256.length > 0 ? { sha256: parsed.sha256 } : {},
|
|
26205
|
+
...typeof parsed.binarySha256 === "string" && parsed.binarySha256.length > 0 ? { binarySha256: parsed.binarySha256 } : {},
|
|
26206
|
+
...typeof parsed.archiveSha256 === "string" && parsed.archiveSha256.length > 0 ? { archiveSha256: parsed.archiveSha256 } : {}
|
|
26207
|
+
};
|
|
26208
|
+
} catch {
|
|
26209
|
+
return null;
|
|
26210
|
+
}
|
|
26211
|
+
}
|
|
26212
|
+
function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveSha256) {
|
|
26213
|
+
try {
|
|
26214
|
+
mkdirSync8(installDir, { recursive: true });
|
|
26215
|
+
const meta3 = {
|
|
26216
|
+
version: version2,
|
|
26217
|
+
installedAt: new Date().toISOString(),
|
|
26218
|
+
sha256: binarySha256,
|
|
26219
|
+
binarySha256,
|
|
26220
|
+
...archiveSha256 ? { archiveSha256 } : {}
|
|
26221
|
+
};
|
|
26222
|
+
writeFileSync8(join15(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
|
|
26223
|
+
} catch (err) {
|
|
26224
|
+
warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
|
|
26225
|
+
}
|
|
26226
|
+
}
|
|
26227
|
+
var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
|
|
25950
26228
|
var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
|
|
25951
26229
|
function sha256OfFile(path2) {
|
|
25952
26230
|
return new Promise((resolve4, reject) => {
|
|
25953
|
-
const hash2 =
|
|
26231
|
+
const hash2 = createHash5("sha256");
|
|
25954
26232
|
const stream = createReadStream2(path2);
|
|
25955
26233
|
stream.on("error", reject);
|
|
25956
26234
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
@@ -25958,7 +26236,7 @@ function sha256OfFile(path2) {
|
|
|
25958
26236
|
});
|
|
25959
26237
|
}
|
|
25960
26238
|
function sha256OfFileSync2(path2) {
|
|
25961
|
-
return
|
|
26239
|
+
return createHash5("sha256").update(readFileSync9(path2)).digest("hex");
|
|
25962
26240
|
}
|
|
25963
26241
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
|
|
25964
26242
|
const candidates = [];
|
|
@@ -26121,8 +26399,8 @@ function assertAllowedDownloadUrl(rawUrl) {
|
|
|
26121
26399
|
return parsed;
|
|
26122
26400
|
}
|
|
26123
26401
|
async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
26124
|
-
if (assetSize !== undefined && assetSize >
|
|
26125
|
-
throw new Error(`asset size ${assetSize} exceeds max ${
|
|
26402
|
+
if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES3) {
|
|
26403
|
+
throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES3} (set lsp.versions to pin a smaller release if this is wrong)`);
|
|
26126
26404
|
}
|
|
26127
26405
|
const timeout = controlledTimeoutSignal(120000, signal);
|
|
26128
26406
|
try {
|
|
@@ -26131,24 +26409,24 @@ async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
|
26131
26409
|
throw new Error(`download failed (${res.status})`);
|
|
26132
26410
|
}
|
|
26133
26411
|
const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
|
|
26134
|
-
if (Number.isFinite(advertised) && advertised >
|
|
26135
|
-
throw new Error(`Content-Length ${advertised} exceeds max ${
|
|
26412
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
|
|
26413
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
|
|
26136
26414
|
}
|
|
26137
26415
|
mkdirSync8(dirname5(destPath), { recursive: true });
|
|
26138
26416
|
let bytesWritten = 0;
|
|
26139
26417
|
const guard = new TransformStream({
|
|
26140
26418
|
transform(chunk, controller) {
|
|
26141
26419
|
bytesWritten += chunk.byteLength;
|
|
26142
|
-
if (bytesWritten >
|
|
26143
|
-
controller.error(new Error(`download exceeded ${
|
|
26420
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES3) {
|
|
26421
|
+
controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES3} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
26144
26422
|
return;
|
|
26145
26423
|
}
|
|
26146
26424
|
controller.enqueue(chunk);
|
|
26147
26425
|
}
|
|
26148
26426
|
});
|
|
26149
26427
|
const guarded = res.body.pipeThrough(guard);
|
|
26150
|
-
const nodeStream =
|
|
26151
|
-
await
|
|
26428
|
+
const nodeStream = Readable3.fromWeb(guarded);
|
|
26429
|
+
await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
|
|
26152
26430
|
} catch (err) {
|
|
26153
26431
|
try {
|
|
26154
26432
|
unlinkSync6(destPath);
|
|
@@ -26228,7 +26506,7 @@ function validateExtraction(stagingRoot) {
|
|
|
26228
26506
|
};
|
|
26229
26507
|
walk(realStagingRoot);
|
|
26230
26508
|
}
|
|
26231
|
-
function
|
|
26509
|
+
function precheckArchiveContents(archivePath, archiveType) {
|
|
26232
26510
|
let totalBytes = 0;
|
|
26233
26511
|
if (archiveType === "zip") {
|
|
26234
26512
|
const out = execFileSync2("unzip", ["-l", archivePath], { encoding: "utf8" });
|
|
@@ -26239,6 +26517,9 @@ function precheckArchiveSize(archivePath, archiveType) {
|
|
|
26239
26517
|
const out = execFileSync2("tar", ["-tvf", archivePath], { encoding: "utf8" });
|
|
26240
26518
|
for (const line of out.split(`
|
|
26241
26519
|
`)) {
|
|
26520
|
+
if (line.startsWith("h")) {
|
|
26521
|
+
throw new Error(`archive contains hardlink entry: ${line.trim()}`);
|
|
26522
|
+
}
|
|
26242
26523
|
const parts = line.trim().split(/\s+/);
|
|
26243
26524
|
if (parts.length >= 6) {
|
|
26244
26525
|
const numeric = parts.map((part) => Number.parseInt(part, 10)).filter((value) => Number.isFinite(value) && value >= 0);
|
|
@@ -26255,20 +26536,20 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
26255
26536
|
const suffix = randomBytes(8).toString("hex");
|
|
26256
26537
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
26257
26538
|
try {
|
|
26258
|
-
|
|
26539
|
+
rmSync5(stagingDir, { recursive: true, force: true });
|
|
26259
26540
|
} catch {}
|
|
26260
26541
|
mkdirSync8(stagingDir, { recursive: true });
|
|
26261
26542
|
try {
|
|
26262
|
-
|
|
26543
|
+
precheckArchiveContents(archivePath, archiveType);
|
|
26263
26544
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
26264
26545
|
validateExtraction(stagingDir);
|
|
26265
26546
|
try {
|
|
26266
|
-
|
|
26547
|
+
rmSync5(destDir, { recursive: true, force: true });
|
|
26267
26548
|
} catch {}
|
|
26268
|
-
|
|
26549
|
+
renameSync6(stagingDir, destDir);
|
|
26269
26550
|
} catch (err) {
|
|
26270
26551
|
try {
|
|
26271
|
-
|
|
26552
|
+
rmSync5(stagingDir, { recursive: true, force: true });
|
|
26272
26553
|
} catch {}
|
|
26273
26554
|
throw err;
|
|
26274
26555
|
}
|
|
@@ -26279,28 +26560,44 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
26279
26560
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
26280
26561
|
try {
|
|
26281
26562
|
mkdirSync8(dirname5(dest), { recursive: true });
|
|
26282
|
-
|
|
26283
|
-
|
|
26563
|
+
rmSync5(dest, { recursive: true, force: true });
|
|
26564
|
+
renameSync6(packageDir, dest);
|
|
26284
26565
|
} catch (err) {
|
|
26285
26566
|
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
26286
26567
|
}
|
|
26287
26568
|
}
|
|
26288
26569
|
function validateCachedGithubInstall(spec, platform2) {
|
|
26289
|
-
const
|
|
26570
|
+
const packageDir = ghPackageDir(spec);
|
|
26571
|
+
const meta3 = readGithubInstalledMetaIn(packageDir);
|
|
26290
26572
|
const binaryPath = ghBinaryCandidates(spec, platform2).map((candidate) => join15(ghBinDir(spec), candidate)).find((candidate) => {
|
|
26291
26573
|
try {
|
|
26292
|
-
return
|
|
26574
|
+
return statSync6(candidate).isFile();
|
|
26293
26575
|
} catch {
|
|
26294
26576
|
return false;
|
|
26295
26577
|
}
|
|
26296
26578
|
});
|
|
26297
|
-
if (!meta3?.
|
|
26579
|
+
if (!meta3?.version || !isSafeVersion(meta3.version) || !binaryPath) {
|
|
26298
26580
|
quarantineCachedGithubInstall(spec, "missing/unsafe metadata or binary");
|
|
26299
26581
|
return false;
|
|
26300
26582
|
}
|
|
26301
26583
|
const currentHash = sha256OfFileSync2(binaryPath);
|
|
26302
|
-
|
|
26303
|
-
|
|
26584
|
+
const recordedBinaryHash = meta3.binarySha256 ?? meta3.sha256;
|
|
26585
|
+
if (recordedBinaryHash && currentHash === recordedBinaryHash) {
|
|
26586
|
+
if (meta3.sha256 !== currentHash || meta3.binarySha256 !== currentHash) {
|
|
26587
|
+
writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.archiveSha256);
|
|
26588
|
+
}
|
|
26589
|
+
return true;
|
|
26590
|
+
}
|
|
26591
|
+
if (meta3.sha256 && !meta3.binarySha256 && !meta3.archiveSha256) {
|
|
26592
|
+
writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.sha256);
|
|
26593
|
+
return true;
|
|
26594
|
+
}
|
|
26595
|
+
if (!recordedBinaryHash) {
|
|
26596
|
+
quarantineCachedGithubInstall(spec, "missing binary sha256 metadata");
|
|
26597
|
+
return false;
|
|
26598
|
+
}
|
|
26599
|
+
if (currentHash !== recordedBinaryHash) {
|
|
26600
|
+
quarantineCachedGithubInstall(spec, `recorded ${recordedBinaryHash}, current ${currentHash}`);
|
|
26304
26601
|
return false;
|
|
26305
26602
|
}
|
|
26306
26603
|
return true;
|
|
@@ -26369,10 +26666,11 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
26369
26666
|
return null;
|
|
26370
26667
|
}
|
|
26371
26668
|
log2(`[lsp] ${spec.id} ${tag} sha256=${archiveSha256}`);
|
|
26372
|
-
const previousMeta =
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26669
|
+
const previousMeta = readGithubInstalledMetaIn(ghPackageDir(spec));
|
|
26670
|
+
const previousArchiveSha256 = previousMeta?.archiveSha256 ?? (previousMeta?.binarySha256 ? undefined : previousMeta?.sha256);
|
|
26671
|
+
if (previousMeta && previousMeta.version === tag && previousArchiveSha256) {
|
|
26672
|
+
if (previousArchiveSha256 !== archiveSha256) {
|
|
26673
|
+
error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch \u2014 refusing install. ` + `Previously installed archive sha256=${previousArchiveSha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding. ` + `Run \`aft doctor --clear\` to wipe the cache and force a fresh install if you've verified the change.`);
|
|
26376
26674
|
try {
|
|
26377
26675
|
unlinkSync6(archivePath);
|
|
26378
26676
|
} catch {}
|
|
@@ -26407,7 +26705,9 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
26407
26705
|
return null;
|
|
26408
26706
|
}
|
|
26409
26707
|
log2(`[lsp] installed ${spec.id} ${tag} at ${targetBinary}`);
|
|
26410
|
-
|
|
26708
|
+
const binarySha256 = await sha256OfFile(targetBinary);
|
|
26709
|
+
log2(`[lsp] ${spec.id} ${tag} binary_sha256=${binarySha256}`);
|
|
26710
|
+
return { archiveSha256, binarySha256 };
|
|
26411
26711
|
}
|
|
26412
26712
|
async function ensureGithubInstalled(spec, config2, fetchImpl, platform2, arch, signal) {
|
|
26413
26713
|
const outcome = await withInstallLock(spec.githubRepo, async () => {
|
|
@@ -26433,14 +26733,14 @@ async function ensureGithubInstalled(spec, config2, fetchImpl, platform2, arch,
|
|
|
26433
26733
|
log2(`[lsp] reinstalling ${spec.id}@${tag}: no installed-version metadata recorded`);
|
|
26434
26734
|
}
|
|
26435
26735
|
}
|
|
26436
|
-
const
|
|
26736
|
+
const hashes = await downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl, signal).catch((err) => {
|
|
26437
26737
|
error2(`[lsp] github install ${spec.id} crashed: ${err}`);
|
|
26438
26738
|
return null;
|
|
26439
26739
|
});
|
|
26440
|
-
if (!
|
|
26740
|
+
if (!hashes) {
|
|
26441
26741
|
return { started: true, reason: "install failed (see plugin log)" };
|
|
26442
26742
|
}
|
|
26443
|
-
|
|
26743
|
+
writeGithubInstalledMetaIn(ghPackageDir(spec), tag, hashes.binarySha256, hashes.archiveSha256);
|
|
26444
26744
|
return { started: true };
|
|
26445
26745
|
});
|
|
26446
26746
|
if (outcome === null) {
|
|
@@ -26635,7 +26935,7 @@ function normalizeToolMap(tools) {
|
|
|
26635
26935
|
}
|
|
26636
26936
|
|
|
26637
26937
|
// src/notifications.ts
|
|
26638
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as
|
|
26938
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync7, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
26639
26939
|
import { homedir as homedir9, platform as platform2 } from "os";
|
|
26640
26940
|
import { join as join16 } from "path";
|
|
26641
26941
|
function isTuiMode() {
|
|
@@ -26742,7 +27042,7 @@ async function sendIgnoredMessage(client, sessionId, text) {
|
|
|
26742
27042
|
return true;
|
|
26743
27043
|
}
|
|
26744
27044
|
} catch (err) {
|
|
26745
|
-
|
|
27045
|
+
sessionLog(sessionId, `[aft-plugin] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26746
27046
|
}
|
|
26747
27047
|
return false;
|
|
26748
27048
|
}
|
|
@@ -26768,7 +27068,7 @@ async function sendWarning(opts, message) {
|
|
|
26768
27068
|
if (!sessionId)
|
|
26769
27069
|
return;
|
|
26770
27070
|
const text = `${WARNING_MARKER} ${message}`;
|
|
26771
|
-
|
|
27071
|
+
sessionLog(sessionId, `[aft-plugin] sending warning to session ${sessionId}`);
|
|
26772
27072
|
await sendIgnoredMessage(opts.client, sessionId, text);
|
|
26773
27073
|
}
|
|
26774
27074
|
async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
|
|
@@ -26791,13 +27091,13 @@ async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
|
|
|
26791
27091
|
return;
|
|
26792
27092
|
const text = [`${FEATURE_MARKER} v${version2}:`, ...features.map((f) => ` \u2022 ${f}`)].join(`
|
|
26793
27093
|
`);
|
|
26794
|
-
|
|
27094
|
+
sessionLog(sessionId, `[aft-plugin] sending feature announcement for v${version2}`);
|
|
26795
27095
|
await sendIgnoredMessage(opts.client, sessionId, text);
|
|
26796
27096
|
}
|
|
26797
27097
|
if (storageDir) {
|
|
26798
27098
|
try {
|
|
26799
27099
|
mkdirSync9(storageDir, { recursive: true });
|
|
26800
|
-
|
|
27100
|
+
writeFileSync9(join16(storageDir, "last_announced_version"), version2);
|
|
26801
27101
|
} catch {}
|
|
26802
27102
|
}
|
|
26803
27103
|
}
|
|
@@ -26825,9 +27125,9 @@ function writeWarnedTools(storageDir, warned) {
|
|
|
26825
27125
|
mkdirSync9(storageDir, { recursive: true });
|
|
26826
27126
|
const warnedToolsPath = join16(storageDir, WARNED_TOOLS_FILE);
|
|
26827
27127
|
const tmpPath = join16(storageDir, `${WARNED_TOOLS_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
26828
|
-
|
|
27128
|
+
writeFileSync9(tmpPath, `${JSON.stringify(warned, null, 2)}
|
|
26829
27129
|
`);
|
|
26830
|
-
|
|
27130
|
+
renameSync7(tmpPath, warnedToolsPath);
|
|
26831
27131
|
} catch {}
|
|
26832
27132
|
}
|
|
26833
27133
|
async function withWarnedToolsLock(storageDir, fn) {
|
|
@@ -26839,7 +27139,7 @@ async function withWarnedToolsLock(storageDir, fn) {
|
|
|
26839
27139
|
try {
|
|
26840
27140
|
return await fn();
|
|
26841
27141
|
} finally {
|
|
26842
|
-
|
|
27142
|
+
rmSync6(lockDir, { recursive: true, force: true });
|
|
26843
27143
|
}
|
|
26844
27144
|
} catch (err) {
|
|
26845
27145
|
const code = err.code;
|
|
@@ -26940,7 +27240,7 @@ async function cleanupWarnings(opts) {
|
|
|
26940
27240
|
}
|
|
26941
27241
|
if (warningIds.length === 0)
|
|
26942
27242
|
return;
|
|
26943
|
-
|
|
27243
|
+
sessionLog(sessionId, `[aft-plugin] cleaning up ${warningIds.length} stale warning(s)`);
|
|
26944
27244
|
for (const id of warningIds) {
|
|
26945
27245
|
await deleteMessage(effectiveServerUrl, sessionId, id);
|
|
26946
27246
|
}
|
|
@@ -26948,16 +27248,16 @@ async function cleanupWarnings(opts) {
|
|
|
26948
27248
|
|
|
26949
27249
|
// src/shared/rpc-server.ts
|
|
26950
27250
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
26951
|
-
import { mkdirSync as mkdirSync10, renameSync as
|
|
27251
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
26952
27252
|
import { createServer } from "http";
|
|
26953
27253
|
import { dirname as dirname6 } from "path";
|
|
26954
27254
|
|
|
26955
27255
|
// src/shared/rpc-utils.ts
|
|
26956
|
-
import { createHash as
|
|
27256
|
+
import { createHash as createHash6 } from "crypto";
|
|
26957
27257
|
import { join as join17 } from "path";
|
|
26958
27258
|
function projectHash(directory) {
|
|
26959
27259
|
const normalized = directory.replace(/\/+$/, "");
|
|
26960
|
-
return
|
|
27260
|
+
return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
26961
27261
|
}
|
|
26962
27262
|
function rpcPortFilePath(storageDir, directory) {
|
|
26963
27263
|
const hash2 = projectHash(directory);
|
|
@@ -26997,11 +27297,11 @@ class AftRpcServer {
|
|
|
26997
27297
|
const dir = dirname6(this.portFilePath);
|
|
26998
27298
|
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
26999
27299
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
27000
|
-
|
|
27300
|
+
writeFileSync10(tmpPath, JSON.stringify({ port: this.port, token: this.token }), {
|
|
27001
27301
|
encoding: "utf-8",
|
|
27002
27302
|
mode: 384
|
|
27003
27303
|
});
|
|
27004
|
-
|
|
27304
|
+
renameSync8(tmpPath, this.portFilePath);
|
|
27005
27305
|
log2(`RPC server listening on 127.0.0.1:${this.port}`);
|
|
27006
27306
|
} catch (err) {
|
|
27007
27307
|
warn2(`Failed to write RPC port file: ${err}`);
|
|
@@ -27104,7 +27404,7 @@ async function getSessionDirectory(client, sessionId, fallbackDirectory) {
|
|
|
27104
27404
|
dir = session.directory;
|
|
27105
27405
|
}
|
|
27106
27406
|
} catch (err) {
|
|
27107
|
-
|
|
27407
|
+
sessionWarn(sessionId, `[aft-plugin] session.get lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
27108
27408
|
return null;
|
|
27109
27409
|
}
|
|
27110
27410
|
setCache(sessionId, dir);
|
|
@@ -27288,7 +27588,7 @@ function formatStatusMarkdown(status) {
|
|
|
27288
27588
|
|
|
27289
27589
|
// src/shared/tui-config.ts
|
|
27290
27590
|
var import_comment_json4 = __toESM(require_src2(), 1);
|
|
27291
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync11, writeFileSync as
|
|
27591
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
27292
27592
|
import { dirname as dirname7, join as join19 } from "path";
|
|
27293
27593
|
|
|
27294
27594
|
// src/shared/opencode-config-dir.ts
|
|
@@ -27345,7 +27645,7 @@ function ensureTuiPluginEntry() {
|
|
|
27345
27645
|
plugins.push(PLUGIN_ENTRY);
|
|
27346
27646
|
config2.plugin = plugins;
|
|
27347
27647
|
mkdirSync11(dirname7(configPath), { recursive: true });
|
|
27348
|
-
|
|
27648
|
+
writeFileSync11(configPath, `${import_comment_json4.stringify(config2, null, 2)}
|
|
27349
27649
|
`);
|
|
27350
27650
|
log2(`[aft-plugin] added TUI plugin entry to ${configPath}`);
|
|
27351
27651
|
return true;
|
|
@@ -28224,7 +28524,7 @@ var CACHE_MAX_ENTRIES2 = 200;
|
|
|
28224
28524
|
var cache2 = new Map;
|
|
28225
28525
|
async function resolveIsSubagent(client, sessionId, _fallbackDirectory) {
|
|
28226
28526
|
if (!sessionId) {
|
|
28227
|
-
|
|
28527
|
+
sessionLog(undefined, "[subagent-detect] no sessionId provided \u2192 primary");
|
|
28228
28528
|
return false;
|
|
28229
28529
|
}
|
|
28230
28530
|
const cached2 = cache2.get(sessionId);
|
|
@@ -28236,11 +28536,11 @@ async function resolveIsSubagent(client, sessionId, _fallbackDirectory) {
|
|
|
28236
28536
|
const c = client;
|
|
28237
28537
|
const sessionApi = c?.session;
|
|
28238
28538
|
if (!sessionApi || typeof sessionApi.get !== "function") {
|
|
28239
|
-
|
|
28539
|
+
sessionLog(sessionId, `[subagent-detect] client.session.get unavailable (client=${typeof client}, session=${typeof sessionApi}, get=${typeof sessionApi?.get}) \u2192 caching as primary`);
|
|
28240
28540
|
setCache2(sessionId, false);
|
|
28241
28541
|
return false;
|
|
28242
28542
|
}
|
|
28243
|
-
|
|
28543
|
+
sessionLog(sessionId, `[subagent-detect] cache miss, calling client.session.get(id=${sessionId})`);
|
|
28244
28544
|
let isSubagent = false;
|
|
28245
28545
|
let parentIdRaw;
|
|
28246
28546
|
try {
|
|
@@ -28250,9 +28550,9 @@ async function resolveIsSubagent(client, sessionId, _fallbackDirectory) {
|
|
|
28250
28550
|
const session = result?.data ?? result;
|
|
28251
28551
|
parentIdRaw = session?.parentID;
|
|
28252
28552
|
isSubagent = session !== undefined && typeof session.parentID === "string" && session.parentID.length > 0;
|
|
28253
|
-
|
|
28553
|
+
sessionLog(sessionId, `[subagent-detect] SDK returned session=${session !== undefined ? "present" : "undefined"}, parentID=${JSON.stringify(parentIdRaw)} \u2192 isSubagent=${isSubagent}`);
|
|
28254
28554
|
} catch (err) {
|
|
28255
|
-
|
|
28555
|
+
sessionWarn(sessionId, `[subagent-detect] SDK lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
28256
28556
|
return false;
|
|
28257
28557
|
}
|
|
28258
28558
|
setCache2(sessionId, isSubagent);
|
|
@@ -28320,7 +28620,7 @@ function createBashTool(ctx) {
|
|
|
28320
28620
|
const requestedBackground = args.background === true;
|
|
28321
28621
|
const effectiveBackground = isSubagent ? false : requestedBackground;
|
|
28322
28622
|
if (isSubagent && requestedBackground) {
|
|
28323
|
-
|
|
28623
|
+
sessionLog(context.sessionID, "[bash] subagent + background:true \u2192 converting to foreground (subagent would lose task_id)");
|
|
28324
28624
|
}
|
|
28325
28625
|
const shellEnv = await ctx.plugin?.trigger?.("shell.env", { cwd, sessionID: context.sessionID, callID: getCallID(context) }, { env: {} });
|
|
28326
28626
|
const data = await withPermissionLoop(ctx, context, {
|
|
@@ -30098,7 +30398,7 @@ async function queryLspHints(client, symbolName, directory, sessionId) {
|
|
|
30098
30398
|
}
|
|
30099
30399
|
return { symbols: hints };
|
|
30100
30400
|
} catch (err) {
|
|
30101
|
-
|
|
30401
|
+
sessionWarn(sessionId, `LSP query failed for "${symbolName}": ${err.message}`);
|
|
30102
30402
|
return;
|
|
30103
30403
|
}
|
|
30104
30404
|
}
|
|
@@ -30887,31 +31187,37 @@ ${lines}
|
|
|
30887
31187
|
} catch (err) {
|
|
30888
31188
|
warn2(`[lsp] auto-install setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
30889
31189
|
}
|
|
30890
|
-
|
|
31190
|
+
const versionUpgradePromises = new Map;
|
|
30891
31191
|
const poolOptions = {
|
|
30892
31192
|
errorPrefix: "[aft-plugin]",
|
|
30893
31193
|
minVersion: PLUGIN_VERSION,
|
|
30894
31194
|
onVersionMismatch: async (binaryVersion, minVersion) => {
|
|
30895
|
-
|
|
30896
|
-
|
|
30897
|
-
|
|
31195
|
+
const existing = versionUpgradePromises.get(minVersion);
|
|
31196
|
+
if (existing) {
|
|
31197
|
+
log2(`Version ${binaryVersion} < ${minVersion}; awaiting in-flight compatible binary upgrade`);
|
|
31198
|
+
return existing;
|
|
30898
31199
|
}
|
|
30899
|
-
|
|
30900
|
-
|
|
30901
|
-
|
|
30902
|
-
|
|
30903
|
-
|
|
30904
|
-
|
|
31200
|
+
const upgradePromise = (async () => {
|
|
31201
|
+
warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
|
|
31202
|
+
try {
|
|
31203
|
+
const path7 = await ensureBinary(`v${minVersion}`);
|
|
31204
|
+
if (!path7) {
|
|
31205
|
+
warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
|
|
31206
|
+
return null;
|
|
31207
|
+
}
|
|
31208
|
+
log2(`Found/downloaded compatible binary at ${path7}. Replacing running bridges...`);
|
|
31209
|
+
const replaced = await pool.replaceBinary(path7);
|
|
31210
|
+
log2("Binary replaced successfully. New bridges will use the updated binary.");
|
|
31211
|
+
return replaced;
|
|
31212
|
+
} catch (err) {
|
|
31213
|
+
error2(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
|
|
30905
31214
|
return null;
|
|
31215
|
+
} finally {
|
|
31216
|
+
versionUpgradePromises.delete(minVersion);
|
|
30906
31217
|
}
|
|
30907
|
-
|
|
30908
|
-
|
|
30909
|
-
|
|
30910
|
-
return replaced;
|
|
30911
|
-
} catch (err) {
|
|
30912
|
-
error2(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
|
|
30913
|
-
return null;
|
|
30914
|
-
}
|
|
31218
|
+
})();
|
|
31219
|
+
versionUpgradePromises.set(minVersion, upgradePromise);
|
|
31220
|
+
return upgradePromise;
|
|
30915
31221
|
},
|
|
30916
31222
|
onConfigureWarnings: async ({ projectRoot, sessionId, client, warnings }) => {
|
|
30917
31223
|
const pendingWarnings = sessionId ? drainPendingEagerWarnings(projectRoot) : [];
|
|
@@ -31045,7 +31351,7 @@ ${lines}
|
|
|
31045
31351
|
if (storageDir && ANNOUNCEMENT_VERSION) {
|
|
31046
31352
|
try {
|
|
31047
31353
|
mkdirSync12(storageDir, { recursive: true });
|
|
31048
|
-
|
|
31354
|
+
writeFileSync12(join21(storageDir, "last_announced_version"), ANNOUNCEMENT_VERSION);
|
|
31049
31355
|
} catch {}
|
|
31050
31356
|
}
|
|
31051
31357
|
return { success: true };
|