@cortexkit/aft-pi 0.25.2 → 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 +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +702 -313
- package/dist/lsp-auto-install.d.ts +1 -1
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-github-install.d.ts +2 -2
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/tools/fs.d.ts.map +1 -1
- package/dist/tools/imports.d.ts +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -6889,7 +6889,7 @@ ${len.toString(16)}\r
|
|
|
6889
6889
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/dispatcher/client-h2.js
|
|
6890
6890
|
var require_client_h2 = __commonJS((exports, module) => {
|
|
6891
6891
|
var assert = __require("node:assert");
|
|
6892
|
-
var { pipeline:
|
|
6892
|
+
var { pipeline: pipeline3 } = __require("node:stream");
|
|
6893
6893
|
var util = require_util();
|
|
6894
6894
|
var {
|
|
6895
6895
|
RequestContentLengthMismatchError,
|
|
@@ -7658,7 +7658,7 @@ var require_client_h2 = __commonJS((exports, module) => {
|
|
|
7658
7658
|
}
|
|
7659
7659
|
function writeStream(abort, socket, expectsPayload, h2stream, body, client, request, contentLength) {
|
|
7660
7660
|
assert(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined");
|
|
7661
|
-
const pipe =
|
|
7661
|
+
const pipe = pipeline3(body, h2stream, (err) => {
|
|
7662
7662
|
if (err) {
|
|
7663
7663
|
util.destroy(pipe, err);
|
|
7664
7664
|
abort(err);
|
|
@@ -10422,7 +10422,7 @@ var require_h2c_client = __commonJS((exports, module) => {
|
|
|
10422
10422
|
var require_readable = __commonJS((exports, module) => {
|
|
10423
10423
|
var assert = __require("node:assert");
|
|
10424
10424
|
var { addAbortListener } = __require("node:events");
|
|
10425
|
-
var { Readable:
|
|
10425
|
+
var { Readable: Readable3 } = __require("node:stream");
|
|
10426
10426
|
var { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require_errors();
|
|
10427
10427
|
var util = require_util();
|
|
10428
10428
|
var { ReadableStreamFrom } = require_util();
|
|
@@ -10436,7 +10436,7 @@ var require_readable = __commonJS((exports, module) => {
|
|
|
10436
10436
|
var kBytesRead = Symbol("kBytesRead");
|
|
10437
10437
|
var noop = () => {};
|
|
10438
10438
|
|
|
10439
|
-
class BodyReadable extends
|
|
10439
|
+
class BodyReadable extends Readable3 {
|
|
10440
10440
|
constructor({
|
|
10441
10441
|
resume,
|
|
10442
10442
|
abort,
|
|
@@ -10723,7 +10723,7 @@ var require_readable = __commonJS((exports, module) => {
|
|
|
10723
10723
|
var require_api_request = __commonJS((exports, module) => {
|
|
10724
10724
|
var assert = __require("node:assert");
|
|
10725
10725
|
var { AsyncResource } = __require("node:async_hooks");
|
|
10726
|
-
var { Readable:
|
|
10726
|
+
var { Readable: Readable3 } = require_readable();
|
|
10727
10727
|
var { InvalidArgumentError, RequestAbortedError } = require_errors();
|
|
10728
10728
|
var util = require_util();
|
|
10729
10729
|
function noop() {}
|
|
@@ -10807,7 +10807,7 @@ var require_api_request = __commonJS((exports, module) => {
|
|
|
10807
10807
|
const parsedHeaders = headers;
|
|
10808
10808
|
const contentType = parsedHeaders?.["content-type"];
|
|
10809
10809
|
const contentLength = parsedHeaders?.["content-length"];
|
|
10810
|
-
const res = new
|
|
10810
|
+
const res = new Readable3({
|
|
10811
10811
|
resume: () => controller.resume(),
|
|
10812
10812
|
abort: (reason) => controller.abort(reason),
|
|
10813
10813
|
contentType,
|
|
@@ -11137,7 +11137,7 @@ var require_api_stream = __commonJS((exports, module) => {
|
|
|
11137
11137
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/api/api-pipeline.js
|
|
11138
11138
|
var require_api_pipeline = __commonJS((exports, module) => {
|
|
11139
11139
|
var {
|
|
11140
|
-
Readable:
|
|
11140
|
+
Readable: Readable3,
|
|
11141
11141
|
Duplex,
|
|
11142
11142
|
PassThrough
|
|
11143
11143
|
} = __require("node:stream");
|
|
@@ -11153,7 +11153,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
|
|
|
11153
11153
|
function noop() {}
|
|
11154
11154
|
var kResume = Symbol("resume");
|
|
11155
11155
|
|
|
11156
|
-
class PipelineRequest extends
|
|
11156
|
+
class PipelineRequest extends Readable3 {
|
|
11157
11157
|
constructor() {
|
|
11158
11158
|
super({ autoDestroy: true });
|
|
11159
11159
|
this[kResume] = null;
|
|
@@ -11171,7 +11171,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
|
|
|
11171
11171
|
}
|
|
11172
11172
|
}
|
|
11173
11173
|
|
|
11174
|
-
class PipelineResponse extends
|
|
11174
|
+
class PipelineResponse extends Readable3 {
|
|
11175
11175
|
constructor(resume) {
|
|
11176
11176
|
super({ autoDestroy: true });
|
|
11177
11177
|
this[kResume] = resume;
|
|
@@ -11326,7 +11326,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
|
|
|
11326
11326
|
util.destroy(ret, err);
|
|
11327
11327
|
}
|
|
11328
11328
|
}
|
|
11329
|
-
function
|
|
11329
|
+
function pipeline3(opts, handler) {
|
|
11330
11330
|
try {
|
|
11331
11331
|
const pipelineHandler = new PipelineHandler(opts, handler);
|
|
11332
11332
|
this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
|
|
@@ -11335,7 +11335,7 @@ var require_api_pipeline = __commonJS((exports, module) => {
|
|
|
11335
11335
|
return new PassThrough().destroy(err);
|
|
11336
11336
|
}
|
|
11337
11337
|
}
|
|
11338
|
-
module.exports =
|
|
11338
|
+
module.exports = pipeline3;
|
|
11339
11339
|
});
|
|
11340
11340
|
|
|
11341
11341
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/api/api-upgrade.js
|
|
@@ -15358,7 +15358,7 @@ var require_cache_revalidation_handler = __commonJS((exports, module) => {
|
|
|
15358
15358
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/interceptor/cache.js
|
|
15359
15359
|
var require_cache2 = __commonJS((exports, module) => {
|
|
15360
15360
|
var assert = __require("node:assert");
|
|
15361
|
-
var { Readable:
|
|
15361
|
+
var { Readable: Readable3 } = __require("node:stream");
|
|
15362
15362
|
var util = require_util();
|
|
15363
15363
|
var CacheHandler = require_cache_handler();
|
|
15364
15364
|
var MemoryCacheStore = require_memory_cache_store();
|
|
@@ -15454,7 +15454,7 @@ var require_cache2 = __commonJS((exports, module) => {
|
|
|
15454
15454
|
return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler));
|
|
15455
15455
|
}
|
|
15456
15456
|
function sendCachedValue(handler, opts, result, age, context, isStale2) {
|
|
15457
|
-
const stream = util.isStream(result.body) ? result.body :
|
|
15457
|
+
const stream = util.isStream(result.body) ? result.body : Readable3.from(result.body ?? []);
|
|
15458
15458
|
assert(!stream.destroyed, "stream should not be destroyed");
|
|
15459
15459
|
assert(!stream.readableDidRead, "stream should not be readableDidRead");
|
|
15460
15460
|
const controller = {
|
|
@@ -15668,7 +15668,7 @@ var require_cache2 = __commonJS((exports, module) => {
|
|
|
15668
15668
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/interceptor/decompress.js
|
|
15669
15669
|
var require_decompress = __commonJS((exports, module) => {
|
|
15670
15670
|
var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("node:zlib");
|
|
15671
|
-
var { pipeline:
|
|
15671
|
+
var { pipeline: pipeline3 } = __require("node:stream");
|
|
15672
15672
|
var DecoratorHandler = require_decorator_handler();
|
|
15673
15673
|
var supportedEncodings = {
|
|
15674
15674
|
gzip: createGunzip,
|
|
@@ -15743,7 +15743,7 @@ var require_decompress = __commonJS((exports, module) => {
|
|
|
15743
15743
|
#setupMultipleDecompressors(controller) {
|
|
15744
15744
|
const lastDecompressor = this.#decompressors[this.#decompressors.length - 1];
|
|
15745
15745
|
this.#setupDecompressorEvents(lastDecompressor, controller);
|
|
15746
|
-
|
|
15746
|
+
pipeline3(this.#decompressors, (err) => {
|
|
15747
15747
|
if (err) {
|
|
15748
15748
|
super.onResponseError(controller, err);
|
|
15749
15749
|
return;
|
|
@@ -18046,7 +18046,7 @@ var require_fetch = __commonJS((exports, module) => {
|
|
|
18046
18046
|
subresourceSet
|
|
18047
18047
|
} = require_constants3();
|
|
18048
18048
|
var EE = __require("node:events");
|
|
18049
|
-
var { Readable:
|
|
18049
|
+
var { Readable: Readable3, pipeline: pipeline3, finished, isErrored, isReadable } = __require("node:stream");
|
|
18050
18050
|
var { addAbortListener, bufferToLowerCasedHeaderName } = require_util();
|
|
18051
18051
|
var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url();
|
|
18052
18052
|
var { getGlobalDispatcher } = require_global2();
|
|
@@ -18937,7 +18937,7 @@ var require_fetch = __commonJS((exports, module) => {
|
|
|
18937
18937
|
const headersList = new HeadersList;
|
|
18938
18938
|
appendHeadersListFromResponseHeaders(headersList, headers, rawHeaders);
|
|
18939
18939
|
const location = headersList.get("location", true);
|
|
18940
|
-
this.body = new
|
|
18940
|
+
this.body = new Readable3({ read: () => controller.resume() });
|
|
18941
18941
|
const willFollow = location && request.redirect === "follow" && redirectStatusSet.has(status);
|
|
18942
18942
|
const decoders = [];
|
|
18943
18943
|
if (request.method !== "HEAD" && request.method !== "CONNECT" && !nullBodyStatus.includes(status) && !willFollow) {
|
|
@@ -18981,7 +18981,7 @@ var require_fetch = __commonJS((exports, module) => {
|
|
|
18981
18981
|
status,
|
|
18982
18982
|
statusText,
|
|
18983
18983
|
headersList,
|
|
18984
|
-
body: decoders.length ?
|
|
18984
|
+
body: decoders.length ? pipeline3(this.body, ...decoders, (err) => {
|
|
18985
18985
|
if (err) {
|
|
18986
18986
|
this.onResponseError(controller, err);
|
|
18987
18987
|
}
|
|
@@ -22363,7 +22363,7 @@ ${value}`;
|
|
|
22363
22363
|
|
|
22364
22364
|
// ../../node_modules/.bun/undici@8.2.0/node_modules/undici/lib/web/eventsource/eventsource.js
|
|
22365
22365
|
var require_eventsource = __commonJS((exports, module) => {
|
|
22366
|
-
var { pipeline:
|
|
22366
|
+
var { pipeline: pipeline3 } = __require("node:stream");
|
|
22367
22367
|
var { fetching } = require_fetch();
|
|
22368
22368
|
var { makeRequest } = require_request2();
|
|
22369
22369
|
var { webidl } = require_webidl();
|
|
@@ -22492,7 +22492,7 @@ var require_eventsource = __commonJS((exports, module) => {
|
|
|
22492
22492
|
this.dispatchEvent(createFastMessageEvent(event.type, event.options));
|
|
22493
22493
|
}
|
|
22494
22494
|
});
|
|
22495
|
-
|
|
22495
|
+
pipeline3(response.body.stream, eventSourceStream, (error2) => {
|
|
22496
22496
|
if (error2?.aborted === false) {
|
|
22497
22497
|
this.close();
|
|
22498
22498
|
this.dispatchEvent(new Event("error"));
|
|
@@ -30490,12 +30490,22 @@ function getActiveLogger() {
|
|
|
30490
30490
|
return loggerGlobal()[ACTIVE_LOGGER_SYMBOL];
|
|
30491
30491
|
}
|
|
30492
30492
|
function getLogFilePath() {
|
|
30493
|
-
|
|
30493
|
+
try {
|
|
30494
|
+
return getActiveLogger()?.getLogFilePath?.();
|
|
30495
|
+
} catch (err) {
|
|
30496
|
+
console.error(`[aft-bridge] ERROR: active logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30497
|
+
return;
|
|
30498
|
+
}
|
|
30494
30499
|
}
|
|
30495
30500
|
function log(message, meta) {
|
|
30496
30501
|
const active = getActiveLogger();
|
|
30497
30502
|
if (active) {
|
|
30498
|
-
|
|
30503
|
+
try {
|
|
30504
|
+
active.log(message, meta);
|
|
30505
|
+
} catch (err) {
|
|
30506
|
+
console.error(`[aft-bridge] ERROR: active logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30507
|
+
console.error(`[aft-bridge] ${message}`);
|
|
30508
|
+
}
|
|
30499
30509
|
} else {
|
|
30500
30510
|
console.error(`[aft-bridge] ${message}`);
|
|
30501
30511
|
}
|
|
@@ -30503,7 +30513,12 @@ function log(message, meta) {
|
|
|
30503
30513
|
function warn(message, meta) {
|
|
30504
30514
|
const active = getActiveLogger();
|
|
30505
30515
|
if (active) {
|
|
30506
|
-
|
|
30516
|
+
try {
|
|
30517
|
+
active.warn(message, meta);
|
|
30518
|
+
} catch (err) {
|
|
30519
|
+
console.error(`[aft-bridge] ERROR: active logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30520
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
30521
|
+
}
|
|
30507
30522
|
} else {
|
|
30508
30523
|
console.error(`[aft-bridge] WARN: ${message}`);
|
|
30509
30524
|
}
|
|
@@ -30511,20 +30526,16 @@ function warn(message, meta) {
|
|
|
30511
30526
|
function error(message, meta) {
|
|
30512
30527
|
const active = getActiveLogger();
|
|
30513
30528
|
if (active) {
|
|
30514
|
-
|
|
30529
|
+
try {
|
|
30530
|
+
active.error(message, meta);
|
|
30531
|
+
} catch (err) {
|
|
30532
|
+
console.error(`[aft-bridge] ERROR: active logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30533
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
30534
|
+
}
|
|
30515
30535
|
} else {
|
|
30516
30536
|
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
30517
30537
|
}
|
|
30518
30538
|
}
|
|
30519
|
-
function sessionLog(sessionId, message) {
|
|
30520
|
-
log(message, sessionId ? { sessionId } : undefined);
|
|
30521
|
-
}
|
|
30522
|
-
function sessionWarn(sessionId, message) {
|
|
30523
|
-
warn(message, sessionId ? { sessionId } : undefined);
|
|
30524
|
-
}
|
|
30525
|
-
function sessionError(sessionId, message) {
|
|
30526
|
-
error(message, sessionId ? { sessionId } : undefined);
|
|
30527
|
-
}
|
|
30528
30539
|
// ../aft-bridge/dist/bridge.js
|
|
30529
30540
|
import { spawn } from "node:child_process";
|
|
30530
30541
|
import { homedir } from "node:os";
|
|
@@ -30601,6 +30612,15 @@ function clampSemanticTimeout(configOverrides, bridgeTimeoutMs) {
|
|
|
30601
30612
|
};
|
|
30602
30613
|
}
|
|
30603
30614
|
|
|
30615
|
+
class BridgeReplacedDuringVersionCheck extends Error {
|
|
30616
|
+
newBinaryPath;
|
|
30617
|
+
constructor(newBinaryPath) {
|
|
30618
|
+
super(`Bridge binary replaced during version check: ${newBinaryPath}`);
|
|
30619
|
+
this.newBinaryPath = newBinaryPath;
|
|
30620
|
+
this.name = "BridgeReplacedDuringVersionCheck";
|
|
30621
|
+
}
|
|
30622
|
+
}
|
|
30623
|
+
|
|
30604
30624
|
class BinaryBridge {
|
|
30605
30625
|
static RESTART_RESET_MS = 5 * 60 * 1000;
|
|
30606
30626
|
static STDERR_TAIL_MAX = 20;
|
|
@@ -30610,6 +30630,7 @@ class BinaryBridge {
|
|
|
30610
30630
|
pending = new Map;
|
|
30611
30631
|
nextId = 1;
|
|
30612
30632
|
stdoutBuffer = "";
|
|
30633
|
+
stderrBuffer = "";
|
|
30613
30634
|
stderrTail = [];
|
|
30614
30635
|
_restartCount = 0;
|
|
30615
30636
|
_shuttingDown = false;
|
|
@@ -30645,24 +30666,62 @@ class BinaryBridge {
|
|
|
30645
30666
|
}
|
|
30646
30667
|
logVia(message, meta) {
|
|
30647
30668
|
const logger = this.logger ?? getActiveLogger();
|
|
30648
|
-
if (logger)
|
|
30649
|
-
|
|
30650
|
-
|
|
30669
|
+
if (logger) {
|
|
30670
|
+
try {
|
|
30671
|
+
logger.log(message, meta);
|
|
30672
|
+
} catch (err) {
|
|
30673
|
+
console.error(`[aft-bridge] ERROR: logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30674
|
+
console.error(`[aft-bridge] ${message}`);
|
|
30675
|
+
}
|
|
30676
|
+
} else {
|
|
30651
30677
|
log(message, meta);
|
|
30678
|
+
}
|
|
30652
30679
|
}
|
|
30653
30680
|
warnVia(message, meta) {
|
|
30654
30681
|
const logger = this.logger ?? getActiveLogger();
|
|
30655
|
-
if (logger)
|
|
30656
|
-
|
|
30657
|
-
|
|
30682
|
+
if (logger) {
|
|
30683
|
+
try {
|
|
30684
|
+
logger.warn(message, meta);
|
|
30685
|
+
} catch (err) {
|
|
30686
|
+
console.error(`[aft-bridge] ERROR: logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30687
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
30688
|
+
}
|
|
30689
|
+
} else {
|
|
30658
30690
|
warn(message, meta);
|
|
30691
|
+
}
|
|
30659
30692
|
}
|
|
30660
30693
|
errorVia(message, meta) {
|
|
30661
30694
|
const logger = this.logger ?? getActiveLogger();
|
|
30662
|
-
if (logger)
|
|
30663
|
-
|
|
30664
|
-
|
|
30695
|
+
if (logger) {
|
|
30696
|
+
try {
|
|
30697
|
+
logger.error(message, meta);
|
|
30698
|
+
} catch (err) {
|
|
30699
|
+
console.error(`[aft-bridge] ERROR: logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30700
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
30701
|
+
}
|
|
30702
|
+
} else {
|
|
30665
30703
|
error(message, meta);
|
|
30704
|
+
}
|
|
30705
|
+
}
|
|
30706
|
+
getLogFilePathVia() {
|
|
30707
|
+
if (this.logger?.getLogFilePath) {
|
|
30708
|
+
try {
|
|
30709
|
+
return this.logger.getLogFilePath();
|
|
30710
|
+
} catch (err) {
|
|
30711
|
+
console.error(`[aft-bridge] ERROR: logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30712
|
+
return;
|
|
30713
|
+
}
|
|
30714
|
+
}
|
|
30715
|
+
return getLogFilePath();
|
|
30716
|
+
}
|
|
30717
|
+
sessionLogVia(sessionId, message) {
|
|
30718
|
+
this.logVia(message, sessionId ? { sessionId } : undefined);
|
|
30719
|
+
}
|
|
30720
|
+
sessionWarnVia(sessionId, message) {
|
|
30721
|
+
this.warnVia(message, sessionId ? { sessionId } : undefined);
|
|
30722
|
+
}
|
|
30723
|
+
sessionErrorVia(sessionId, message) {
|
|
30724
|
+
this.errorVia(message, sessionId ? { sessionId } : undefined);
|
|
30666
30725
|
}
|
|
30667
30726
|
get restartCount() {
|
|
30668
30727
|
return this._restartCount;
|
|
@@ -30689,97 +30748,108 @@ class BinaryBridge {
|
|
|
30689
30748
|
this.cachedStatus = snapshot;
|
|
30690
30749
|
}
|
|
30691
30750
|
async send(command, params = {}, options) {
|
|
30692
|
-
|
|
30693
|
-
|
|
30694
|
-
|
|
30695
|
-
|
|
30696
|
-
|
|
30697
|
-
|
|
30698
|
-
|
|
30699
|
-
|
|
30700
|
-
|
|
30701
|
-
|
|
30702
|
-
|
|
30703
|
-
|
|
30704
|
-
if (
|
|
30705
|
-
|
|
30706
|
-
|
|
30707
|
-
|
|
30708
|
-
|
|
30709
|
-
|
|
30710
|
-
|
|
30711
|
-
|
|
30712
|
-
|
|
30713
|
-
|
|
30714
|
-
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30751
|
+
return this.sendWithVersionMismatchRetry(command, params, options, true);
|
|
30752
|
+
}
|
|
30753
|
+
async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
|
|
30754
|
+
try {
|
|
30755
|
+
if (this._shuttingDown) {
|
|
30756
|
+
throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
|
|
30757
|
+
}
|
|
30758
|
+
if (Object.hasOwn(params, "id")) {
|
|
30759
|
+
throw new Error("params cannot contain reserved key 'id'");
|
|
30760
|
+
}
|
|
30761
|
+
const requestSessionId = typeof params.session_id === "string" && params.session_id.length > 0 ? params.session_id : undefined;
|
|
30762
|
+
this.ensureSpawned(requestSessionId);
|
|
30763
|
+
if (requestSessionId && options?.configureWarningClient !== undefined) {
|
|
30764
|
+
this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
|
|
30765
|
+
}
|
|
30766
|
+
if (!this.configured) {
|
|
30767
|
+
if (command !== "configure" && command !== "version") {
|
|
30768
|
+
if (!this._configurePromise) {
|
|
30769
|
+
const sessionIdForConfigure = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
30770
|
+
this._configurePromise = (async () => {
|
|
30771
|
+
try {
|
|
30772
|
+
const configResult = await this.send("configure", {
|
|
30773
|
+
project_root: this.cwd,
|
|
30774
|
+
...this.configOverrides,
|
|
30775
|
+
...sessionIdForConfigure ? { session_id: sessionIdForConfigure } : {}
|
|
30776
|
+
});
|
|
30777
|
+
if (configResult.success === false) {
|
|
30778
|
+
throw new Error(`${this.errorPrefix} Configure failed: ${configResult.message ?? "unknown error"}`);
|
|
30779
|
+
}
|
|
30780
|
+
await this.deliverConfigureWarnings(configResult, params, options);
|
|
30781
|
+
await this.checkVersion();
|
|
30782
|
+
if (!this.isAlive()) {
|
|
30783
|
+
throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
|
|
30784
|
+
}
|
|
30785
|
+
this.configured = true;
|
|
30786
|
+
} finally {
|
|
30787
|
+
this._configurePromise = null;
|
|
30721
30788
|
}
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
}
|
|
30726
|
-
})();
|
|
30789
|
+
})();
|
|
30790
|
+
}
|
|
30791
|
+
await this._configurePromise;
|
|
30727
30792
|
}
|
|
30728
|
-
await this._configurePromise;
|
|
30729
30793
|
}
|
|
30730
|
-
|
|
30731
|
-
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30739
|
-
|
|
30794
|
+
const id = String(this.nextId++);
|
|
30795
|
+
let request;
|
|
30796
|
+
if (Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
|
|
30797
|
+
const nested = { ...params };
|
|
30798
|
+
const reserved = {};
|
|
30799
|
+
for (const key of ["session_id", "lsp_hints"]) {
|
|
30800
|
+
if (Object.hasOwn(nested, key)) {
|
|
30801
|
+
reserved[key] = nested[key];
|
|
30802
|
+
delete nested[key];
|
|
30803
|
+
}
|
|
30740
30804
|
}
|
|
30805
|
+
request = { id, command, ...reserved, params: nested };
|
|
30806
|
+
} else {
|
|
30807
|
+
request = { id, command, ...params };
|
|
30741
30808
|
}
|
|
30742
|
-
|
|
30743
|
-
} else {
|
|
30744
|
-
request = { id, command, ...params };
|
|
30745
|
-
}
|
|
30746
|
-
const line = `${JSON.stringify(request)}
|
|
30809
|
+
const line = `${JSON.stringify(request)}
|
|
30747
30810
|
`;
|
|
30748
|
-
|
|
30749
|
-
|
|
30750
|
-
|
|
30751
|
-
|
|
30752
|
-
|
|
30753
|
-
|
|
30754
|
-
|
|
30755
|
-
|
|
30756
|
-
|
|
30757
|
-
|
|
30758
|
-
|
|
30759
|
-
|
|
30760
|
-
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
}
|
|
30764
|
-
}, effectiveTimeoutMs);
|
|
30765
|
-
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress });
|
|
30766
|
-
if (!this.process?.stdin?.writable) {
|
|
30767
|
-
this.pending.delete(id);
|
|
30768
|
-
clearTimeout(timer);
|
|
30769
|
-
reject(new Error(`${this.errorPrefix} stdin not writable for command "${command}"`));
|
|
30770
|
-
return;
|
|
30771
|
-
}
|
|
30772
|
-
this.process.stdin.write(line, (err) => {
|
|
30773
|
-
if (err) {
|
|
30774
|
-
const entry = this.pending.get(id);
|
|
30775
|
-
if (entry) {
|
|
30776
|
-
this.pending.delete(id);
|
|
30777
|
-
clearTimeout(entry.timer);
|
|
30778
|
-
entry.reject(new Error(`${this.errorPrefix} Failed to write to stdin: ${err.message}`));
|
|
30811
|
+
const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
30812
|
+
const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
|
|
30813
|
+
return new Promise((resolve, reject) => {
|
|
30814
|
+
const timer = setTimeout(() => {
|
|
30815
|
+
this.pending.delete(id);
|
|
30816
|
+
const restartSuffix = keepBridgeOnTimeout ? "" : " — restarting bridge";
|
|
30817
|
+
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
30818
|
+
if (requestSessionId) {
|
|
30819
|
+
this.sessionWarnVia(requestSessionId, timeoutMsg);
|
|
30820
|
+
} else {
|
|
30821
|
+
this.warnVia(timeoutMsg);
|
|
30822
|
+
}
|
|
30823
|
+
reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
30824
|
+
if (!keepBridgeOnTimeout) {
|
|
30825
|
+
this.handleTimeout(requestSessionId);
|
|
30779
30826
|
}
|
|
30827
|
+
}, effectiveTimeoutMs);
|
|
30828
|
+
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress });
|
|
30829
|
+
if (!this.process?.stdin?.writable) {
|
|
30830
|
+
this.pending.delete(id);
|
|
30831
|
+
clearTimeout(timer);
|
|
30832
|
+
reject(new Error(`${this.errorPrefix} stdin not writable for command "${command}"`));
|
|
30833
|
+
return;
|
|
30780
30834
|
}
|
|
30835
|
+
this.process.stdin.write(line, (err) => {
|
|
30836
|
+
if (err) {
|
|
30837
|
+
const entry = this.pending.get(id);
|
|
30838
|
+
if (entry) {
|
|
30839
|
+
this.pending.delete(id);
|
|
30840
|
+
clearTimeout(entry.timer);
|
|
30841
|
+
entry.reject(new Error(`${this.errorPrefix} Failed to write to stdin: ${err.message}`));
|
|
30842
|
+
}
|
|
30843
|
+
}
|
|
30844
|
+
});
|
|
30781
30845
|
});
|
|
30782
|
-
})
|
|
30846
|
+
} catch (err) {
|
|
30847
|
+
if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
|
|
30848
|
+
this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
|
|
30849
|
+
return this.sendWithVersionMismatchRetry(command, params, options, false);
|
|
30850
|
+
}
|
|
30851
|
+
throw err;
|
|
30852
|
+
}
|
|
30783
30853
|
}
|
|
30784
30854
|
async deliverConfigureWarnings(configResult, params, options) {
|
|
30785
30855
|
if (!this.onConfigureWarnings || !Array.isArray(configResult.warnings))
|
|
@@ -30795,7 +30865,7 @@ class BinaryBridge {
|
|
|
30795
30865
|
warnings: configResult.warnings
|
|
30796
30866
|
});
|
|
30797
30867
|
} catch (err) {
|
|
30798
|
-
|
|
30868
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
30799
30869
|
} finally {
|
|
30800
30870
|
if (sessionId) {
|
|
30801
30871
|
this.configureWarningClients.delete(sessionId);
|
|
@@ -30829,7 +30899,7 @@ class BinaryBridge {
|
|
|
30829
30899
|
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
30830
30900
|
return;
|
|
30831
30901
|
this.cachedStatus = snapshot;
|
|
30832
|
-
|
|
30902
|
+
this.logVia("Received status_changed push frame; cached AFT status snapshot");
|
|
30833
30903
|
for (const listener of this.statusListeners) {
|
|
30834
30904
|
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
30835
30905
|
}
|
|
@@ -30838,7 +30908,7 @@ class BinaryBridge {
|
|
|
30838
30908
|
try {
|
|
30839
30909
|
listener(snapshot);
|
|
30840
30910
|
} catch (err) {
|
|
30841
|
-
|
|
30911
|
+
this.warnVia(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30842
30912
|
}
|
|
30843
30913
|
}
|
|
30844
30914
|
async shutdown() {
|
|
@@ -30856,7 +30926,7 @@ class BinaryBridge {
|
|
|
30856
30926
|
}, 5000);
|
|
30857
30927
|
proc.once("exit", () => {
|
|
30858
30928
|
clearTimeout(forceKillTimer);
|
|
30859
|
-
|
|
30929
|
+
this.logVia("Process exited during shutdown");
|
|
30860
30930
|
resolve();
|
|
30861
30931
|
});
|
|
30862
30932
|
proc.kill("SIGTERM");
|
|
@@ -30875,16 +30945,46 @@ class BinaryBridge {
|
|
|
30875
30945
|
if (typeof binaryVersion !== "string") {
|
|
30876
30946
|
throw new Error(`Binary did not report a version — likely too old (minVersion: ${this.minVersion})`);
|
|
30877
30947
|
}
|
|
30878
|
-
|
|
30948
|
+
this.logVia(`Binary version: ${binaryVersion}`);
|
|
30879
30949
|
if (compareSemver(binaryVersion, this.minVersion) < 0) {
|
|
30880
|
-
|
|
30881
|
-
this.onVersionMismatch?.(binaryVersion, this.minVersion);
|
|
30950
|
+
this.warnVia(`Binary version ${binaryVersion} is older than required ${this.minVersion}`);
|
|
30951
|
+
const replacementPath = await this.onVersionMismatch?.(binaryVersion, this.minVersion);
|
|
30952
|
+
if (replacementPath === undefined) {
|
|
30953
|
+
return;
|
|
30954
|
+
}
|
|
30955
|
+
if (replacementPath === null || replacementPath.length === 0) {
|
|
30956
|
+
throw new Error(`Binary version ${binaryVersion} is older than required ${this.minVersion}; no compatible replacement binary was provided`);
|
|
30957
|
+
}
|
|
30958
|
+
await this.replaceCurrentBinary(replacementPath);
|
|
30959
|
+
throw new BridgeReplacedDuringVersionCheck(replacementPath);
|
|
30882
30960
|
}
|
|
30883
30961
|
} catch (err) {
|
|
30884
|
-
|
|
30962
|
+
this.warnVia(`Version check failed: ${err.message}`);
|
|
30885
30963
|
throw err;
|
|
30886
30964
|
}
|
|
30887
30965
|
}
|
|
30966
|
+
async replaceCurrentBinary(newBinaryPath) {
|
|
30967
|
+
this.binaryPath = newBinaryPath;
|
|
30968
|
+
this.configured = false;
|
|
30969
|
+
this.clearRestartResetTimer();
|
|
30970
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge restarting with updated binary: ${newBinaryPath}`));
|
|
30971
|
+
if (!this.process)
|
|
30972
|
+
return;
|
|
30973
|
+
const proc = this.process;
|
|
30974
|
+
this.process = null;
|
|
30975
|
+
await new Promise((resolve) => {
|
|
30976
|
+
const forceKillTimer = setTimeout(() => {
|
|
30977
|
+
proc.kill("SIGKILL");
|
|
30978
|
+
resolve();
|
|
30979
|
+
}, 5000);
|
|
30980
|
+
proc.once("exit", () => {
|
|
30981
|
+
clearTimeout(forceKillTimer);
|
|
30982
|
+
this.logVia("Process exited during coordinated binary replacement");
|
|
30983
|
+
resolve();
|
|
30984
|
+
});
|
|
30985
|
+
proc.kill("SIGTERM");
|
|
30986
|
+
});
|
|
30987
|
+
}
|
|
30888
30988
|
ensureSpawned(triggeringSessionId) {
|
|
30889
30989
|
if (this.isAlive())
|
|
30890
30990
|
return;
|
|
@@ -30892,9 +30992,9 @@ class BinaryBridge {
|
|
|
30892
30992
|
}
|
|
30893
30993
|
spawnProcess(triggeringSessionId) {
|
|
30894
30994
|
if (triggeringSessionId) {
|
|
30895
|
-
|
|
30995
|
+
this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
30896
30996
|
} else {
|
|
30897
|
-
|
|
30997
|
+
this.logVia(`Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
30898
30998
|
}
|
|
30899
30999
|
const semantic = this.configOverrides.semantic;
|
|
30900
31000
|
const semanticBackend = (() => {
|
|
@@ -30941,11 +31041,12 @@ class BinaryBridge {
|
|
|
30941
31041
|
const remaining = stderrDecoder.end();
|
|
30942
31042
|
if (remaining)
|
|
30943
31043
|
this.onStderrData(remaining);
|
|
31044
|
+
this.flushStderrBuffer();
|
|
30944
31045
|
});
|
|
30945
31046
|
child.on("error", (err) => {
|
|
30946
31047
|
if (this.process !== currentChild)
|
|
30947
31048
|
return;
|
|
30948
|
-
|
|
31049
|
+
this.errorVia(`Process error: ${err.message}${this.formatStderrTail()}`);
|
|
30949
31050
|
this.handleCrash();
|
|
30950
31051
|
});
|
|
30951
31052
|
child.on("exit", (code, signal) => {
|
|
@@ -30953,7 +31054,7 @@ class BinaryBridge {
|
|
|
30953
31054
|
return;
|
|
30954
31055
|
if (this._shuttingDown)
|
|
30955
31056
|
return;
|
|
30956
|
-
|
|
31057
|
+
this.logVia(`Process exited: code=${code}, signal=${signal}`);
|
|
30957
31058
|
if (signal === "SIGTERM" || signal === "SIGKILL" || signal === "SIGHUP" || signal === "SIGINT") {
|
|
30958
31059
|
this.process = null;
|
|
30959
31060
|
this.configured = false;
|
|
@@ -30965,6 +31066,7 @@ class BinaryBridge {
|
|
|
30965
31066
|
});
|
|
30966
31067
|
this.process = child;
|
|
30967
31068
|
this.stdoutBuffer = "";
|
|
31069
|
+
this.stderrBuffer = "";
|
|
30968
31070
|
this.stderrTail = [];
|
|
30969
31071
|
}
|
|
30970
31072
|
pushStderrLine(line) {
|
|
@@ -30974,16 +31076,28 @@ class BinaryBridge {
|
|
|
30974
31076
|
}
|
|
30975
31077
|
}
|
|
30976
31078
|
onStderrData(data) {
|
|
30977
|
-
|
|
30978
|
-
|
|
30979
|
-
|
|
31079
|
+
this.stderrBuffer += data;
|
|
31080
|
+
let newlineIdx;
|
|
31081
|
+
while ((newlineIdx = this.stderrBuffer.indexOf(`
|
|
31082
|
+
`)) !== -1) {
|
|
31083
|
+
const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
31084
|
+
this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
|
|
30980
31085
|
if (!line)
|
|
30981
31086
|
continue;
|
|
30982
31087
|
const tagged = tagStderrLine(line);
|
|
30983
|
-
|
|
31088
|
+
this.logVia(tagged);
|
|
30984
31089
|
this.pushStderrLine(tagged);
|
|
30985
31090
|
}
|
|
30986
31091
|
}
|
|
31092
|
+
flushStderrBuffer() {
|
|
31093
|
+
const line = this.stderrBuffer.replace(/\r$/, "");
|
|
31094
|
+
this.stderrBuffer = "";
|
|
31095
|
+
if (!line)
|
|
31096
|
+
return;
|
|
31097
|
+
const tagged = tagStderrLine(line);
|
|
31098
|
+
this.logVia(tagged);
|
|
31099
|
+
this.pushStderrLine(tagged);
|
|
31100
|
+
}
|
|
30987
31101
|
formatStderrTail() {
|
|
30988
31102
|
if (this.stderrTail.length === 0)
|
|
30989
31103
|
return "";
|
|
@@ -31041,7 +31155,7 @@ class BinaryBridge {
|
|
|
31041
31155
|
}
|
|
31042
31156
|
if (response.type === "configure_warnings") {
|
|
31043
31157
|
this.handleConfigureWarningsFrame(response).catch((err) => {
|
|
31044
|
-
|
|
31158
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
31045
31159
|
});
|
|
31046
31160
|
continue;
|
|
31047
31161
|
}
|
|
@@ -31059,10 +31173,10 @@ class BinaryBridge {
|
|
|
31059
31173
|
this.scheduleRestartCountReset();
|
|
31060
31174
|
entry.resolve(response);
|
|
31061
31175
|
} else if (typeof response.type === "string") {
|
|
31062
|
-
|
|
31176
|
+
this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
|
|
31063
31177
|
}
|
|
31064
31178
|
} catch (_err) {
|
|
31065
|
-
|
|
31179
|
+
this.warnVia(`Failed to parse stdout line: ${line}`);
|
|
31066
31180
|
}
|
|
31067
31181
|
}
|
|
31068
31182
|
}
|
|
@@ -31076,17 +31190,17 @@ class BinaryBridge {
|
|
|
31076
31190
|
this.configured = false;
|
|
31077
31191
|
const tail = this.formatStderrTail();
|
|
31078
31192
|
this.stderrTail = [];
|
|
31079
|
-
const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${
|
|
31193
|
+
const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
|
|
31080
31194
|
if (tail) {
|
|
31081
31195
|
if (triggeringSessionId) {
|
|
31082
|
-
|
|
31196
|
+
this.sessionErrorVia(triggeringSessionId, killedMsg);
|
|
31083
31197
|
} else {
|
|
31084
|
-
|
|
31198
|
+
this.errorVia(killedMsg);
|
|
31085
31199
|
}
|
|
31086
31200
|
} else if (triggeringSessionId) {
|
|
31087
|
-
|
|
31201
|
+
this.sessionWarnVia(triggeringSessionId, killedMsg);
|
|
31088
31202
|
} else {
|
|
31089
|
-
|
|
31203
|
+
this.warnVia(killedMsg);
|
|
31090
31204
|
}
|
|
31091
31205
|
}
|
|
31092
31206
|
handleCrash(cause) {
|
|
@@ -31099,25 +31213,25 @@ class BinaryBridge {
|
|
|
31099
31213
|
this.configured = false;
|
|
31100
31214
|
const tail = this.formatStderrTail();
|
|
31101
31215
|
if (tail) {
|
|
31102
|
-
|
|
31216
|
+
this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
|
|
31103
31217
|
}
|
|
31104
|
-
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${
|
|
31218
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
|
|
31105
31219
|
if (this._restartCount < this.maxRestarts) {
|
|
31106
31220
|
const delay = 100 * 2 ** this._restartCount;
|
|
31107
31221
|
this._restartCount++;
|
|
31108
|
-
|
|
31222
|
+
this.logVia(`Auto-restart #${this._restartCount} in ${delay}ms`);
|
|
31109
31223
|
setTimeout(() => {
|
|
31110
31224
|
if (!this._shuttingDown && !this.isAlive()) {
|
|
31111
31225
|
try {
|
|
31112
31226
|
this.spawnProcess();
|
|
31113
31227
|
} catch (err) {
|
|
31114
|
-
|
|
31228
|
+
this.errorVia(`Failed to restart: ${err.message}`);
|
|
31115
31229
|
}
|
|
31116
31230
|
}
|
|
31117
31231
|
}, delay);
|
|
31118
31232
|
this.scheduleRestartCountReset();
|
|
31119
31233
|
} else {
|
|
31120
|
-
|
|
31234
|
+
this.errorVia(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${this.getLogFilePathVia()}${tail}`);
|
|
31121
31235
|
this.scheduleRestartCountReset();
|
|
31122
31236
|
}
|
|
31123
31237
|
}
|
|
@@ -31143,9 +31257,12 @@ class BinaryBridge {
|
|
|
31143
31257
|
}
|
|
31144
31258
|
}
|
|
31145
31259
|
// ../aft-bridge/dist/downloader.js
|
|
31146
|
-
import {
|
|
31260
|
+
import { createHash } from "node:crypto";
|
|
31261
|
+
import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, renameSync, rmSync, statSync, unlinkSync } from "node:fs";
|
|
31147
31262
|
import { homedir as homedir2 } from "node:os";
|
|
31148
31263
|
import { join as join2 } from "node:path";
|
|
31264
|
+
import { Readable } from "node:stream";
|
|
31265
|
+
import { pipeline } from "node:stream/promises";
|
|
31149
31266
|
|
|
31150
31267
|
// ../aft-bridge/dist/platform.js
|
|
31151
31268
|
var PLATFORM_ARCH_MAP = {
|
|
@@ -31163,6 +31280,11 @@ var PLATFORM_ASSET_MAP = {
|
|
|
31163
31280
|
|
|
31164
31281
|
// ../aft-bridge/dist/downloader.js
|
|
31165
31282
|
var REPO = "cortexkit/aft";
|
|
31283
|
+
var DOWNLOAD_TIMEOUT_MS = 300000;
|
|
31284
|
+
var LATEST_TAG_TIMEOUT_MS = 30000;
|
|
31285
|
+
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
31286
|
+
var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
|
|
31287
|
+
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
31166
31288
|
function getCacheDir() {
|
|
31167
31289
|
if (process.platform === "win32") {
|
|
31168
31290
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
@@ -31204,54 +31326,101 @@ async function downloadBinary(version) {
|
|
|
31204
31326
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
|
|
31205
31327
|
const checksumUrl = `https://github.com/${REPO}/releases/download/${tag}/checksums.sha256`;
|
|
31206
31328
|
log(`Downloading AFT binary (${tag}) for ${platformKey}...`);
|
|
31329
|
+
const lockPath = join2(versionedCacheDir, ".download.lock");
|
|
31330
|
+
let releaseLock = null;
|
|
31331
|
+
let binaryController = null;
|
|
31332
|
+
let checksumController = null;
|
|
31333
|
+
let binaryTimeout = null;
|
|
31334
|
+
let checksumTimeout = null;
|
|
31335
|
+
const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
31207
31336
|
try {
|
|
31208
31337
|
if (!existsSync(versionedCacheDir)) {
|
|
31209
31338
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
31210
31339
|
}
|
|
31340
|
+
releaseLock = await acquireDownloadLock(lockPath);
|
|
31341
|
+
if (existsSync(binaryPath)) {
|
|
31342
|
+
return binaryPath;
|
|
31343
|
+
}
|
|
31344
|
+
binaryController = new AbortController;
|
|
31345
|
+
checksumController = new AbortController;
|
|
31346
|
+
const activeBinaryController = binaryController;
|
|
31347
|
+
const activeChecksumController = checksumController;
|
|
31348
|
+
binaryTimeout = setTimeout(() => activeBinaryController.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
31349
|
+
checksumTimeout = setTimeout(() => activeChecksumController.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
31211
31350
|
const [binaryResponse, checksumResponse] = await Promise.all([
|
|
31212
|
-
fetch(downloadUrl, { redirect: "follow" }),
|
|
31213
|
-
fetch(checksumUrl, { redirect: "follow" })
|
|
31351
|
+
fetch(downloadUrl, { redirect: "follow", signal: activeBinaryController.signal }),
|
|
31352
|
+
fetch(checksumUrl, { redirect: "follow", signal: activeChecksumController.signal })
|
|
31214
31353
|
]);
|
|
31215
31354
|
if (!binaryResponse.ok) {
|
|
31216
31355
|
throw new Error(`HTTP ${binaryResponse.status}: ${binaryResponse.statusText} (${downloadUrl})`);
|
|
31217
31356
|
}
|
|
31218
|
-
|
|
31357
|
+
if (!binaryResponse.body) {
|
|
31358
|
+
throw new Error(`Download response for ${assetName} had no body`);
|
|
31359
|
+
}
|
|
31360
|
+
const advertised = Number.parseInt(binaryResponse.headers.get("content-length") ?? "", 10);
|
|
31361
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES) {
|
|
31362
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES}`);
|
|
31363
|
+
}
|
|
31219
31364
|
if (!checksumResponse.ok) {
|
|
31220
31365
|
warn(`Checksum verification failed: no checksums.sha256 found for ${tag}. ` + "Binary download aborted for security reasons.");
|
|
31221
31366
|
return null;
|
|
31222
31367
|
}
|
|
31223
31368
|
const checksumText = await checksumResponse.text();
|
|
31369
|
+
clearTimeout(checksumTimeout);
|
|
31370
|
+
checksumTimeout = null;
|
|
31224
31371
|
const expectedHash = parseChecksumForAsset(checksumText, assetName);
|
|
31225
31372
|
if (!expectedHash) {
|
|
31226
31373
|
warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
|
|
31227
31374
|
return null;
|
|
31228
31375
|
}
|
|
31229
|
-
const
|
|
31230
|
-
|
|
31376
|
+
const hash = createHash("sha256");
|
|
31377
|
+
let bytesWritten = 0;
|
|
31378
|
+
const guard = new TransformStream({
|
|
31379
|
+
transform(chunk, controller) {
|
|
31380
|
+
bytesWritten += chunk.byteLength;
|
|
31381
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES) {
|
|
31382
|
+
controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
31383
|
+
return;
|
|
31384
|
+
}
|
|
31385
|
+
hash.update(chunk);
|
|
31386
|
+
controller.enqueue(chunk);
|
|
31387
|
+
}
|
|
31388
|
+
});
|
|
31389
|
+
const guarded = binaryResponse.body.pipeThrough(guard);
|
|
31390
|
+
const nodeStream = Readable.fromWeb(guarded);
|
|
31391
|
+
await pipeline(nodeStream, createWriteStream(tmpPath), { signal: binaryController.signal });
|
|
31392
|
+
clearTimeout(binaryTimeout);
|
|
31393
|
+
binaryTimeout = null;
|
|
31394
|
+
const actualHash = hash.digest("hex");
|
|
31231
31395
|
if (actualHash !== expectedHash) {
|
|
31232
|
-
throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. The binary may have been tampered with
|
|
31396
|
+
throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. ` + "The binary may have been tampered with.");
|
|
31233
31397
|
}
|
|
31234
31398
|
log(`Checksum verified (SHA-256: ${actualHash.slice(0, 16)}...)`);
|
|
31235
|
-
const tmpPath = `${binaryPath}.tmp`;
|
|
31236
|
-
const { writeFileSync } = await import("node:fs");
|
|
31237
|
-
writeFileSync(tmpPath, Buffer.from(arrayBuffer));
|
|
31238
31399
|
if (process.platform !== "win32") {
|
|
31239
31400
|
chmodSync(tmpPath, 493);
|
|
31240
31401
|
}
|
|
31241
|
-
const { renameSync } = await import("node:fs");
|
|
31242
31402
|
renameSync(tmpPath, binaryPath);
|
|
31243
31403
|
log(`AFT binary ready at ${binaryPath}`);
|
|
31244
31404
|
return binaryPath;
|
|
31245
31405
|
} catch (err) {
|
|
31246
31406
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31247
31407
|
error(`Failed to download AFT binary: ${msg}`);
|
|
31248
|
-
const tmpPath = `${binaryPath}.tmp`;
|
|
31249
31408
|
if (existsSync(tmpPath)) {
|
|
31250
31409
|
try {
|
|
31251
31410
|
unlinkSync(tmpPath);
|
|
31252
31411
|
} catch {}
|
|
31253
31412
|
}
|
|
31254
31413
|
return null;
|
|
31414
|
+
} finally {
|
|
31415
|
+
if (binaryTimeout) {
|
|
31416
|
+
binaryController?.abort();
|
|
31417
|
+
clearTimeout(binaryTimeout);
|
|
31418
|
+
}
|
|
31419
|
+
if (checksumTimeout) {
|
|
31420
|
+
checksumController?.abort();
|
|
31421
|
+
clearTimeout(checksumTimeout);
|
|
31422
|
+
}
|
|
31423
|
+
releaseLock?.();
|
|
31255
31424
|
}
|
|
31256
31425
|
}
|
|
31257
31426
|
async function ensureBinary(version) {
|
|
@@ -31268,6 +31437,39 @@ async function ensureBinary(version) {
|
|
|
31268
31437
|
log("No cached binary found, downloading latest...");
|
|
31269
31438
|
return downloadBinary();
|
|
31270
31439
|
}
|
|
31440
|
+
async function acquireDownloadLock(lockPath) {
|
|
31441
|
+
const startedAt = Date.now();
|
|
31442
|
+
while (true) {
|
|
31443
|
+
try {
|
|
31444
|
+
const fd = openSync(lockPath, "wx");
|
|
31445
|
+
return () => {
|
|
31446
|
+
try {
|
|
31447
|
+
closeSync(fd);
|
|
31448
|
+
} catch {}
|
|
31449
|
+
try {
|
|
31450
|
+
rmSync(lockPath, { force: true });
|
|
31451
|
+
} catch {}
|
|
31452
|
+
};
|
|
31453
|
+
} catch (err) {
|
|
31454
|
+
const code = err.code;
|
|
31455
|
+
if (code !== "EEXIST")
|
|
31456
|
+
throw err;
|
|
31457
|
+
try {
|
|
31458
|
+
const ageMs = Date.now() - statSync(lockPath).mtimeMs;
|
|
31459
|
+
if (ageMs > DOWNLOAD_LOCK_STALE_MS) {
|
|
31460
|
+
rmSync(lockPath, { force: true });
|
|
31461
|
+
continue;
|
|
31462
|
+
}
|
|
31463
|
+
} catch {
|
|
31464
|
+
continue;
|
|
31465
|
+
}
|
|
31466
|
+
if (Date.now() - startedAt > DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
31467
|
+
throw new Error(`Timed out waiting for download lock: ${lockPath}`);
|
|
31468
|
+
}
|
|
31469
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
31470
|
+
}
|
|
31471
|
+
}
|
|
31472
|
+
}
|
|
31271
31473
|
function parseChecksumForAsset(checksumText, assetName) {
|
|
31272
31474
|
for (const line of checksumText.split(`
|
|
31273
31475
|
`)) {
|
|
@@ -31282,9 +31484,12 @@ function parseChecksumForAsset(checksumText, assetName) {
|
|
|
31282
31484
|
return null;
|
|
31283
31485
|
}
|
|
31284
31486
|
async function fetchLatestTag() {
|
|
31487
|
+
const controller = new AbortController;
|
|
31488
|
+
const timeout = setTimeout(() => controller.abort(), LATEST_TAG_TIMEOUT_MS);
|
|
31285
31489
|
try {
|
|
31286
31490
|
const response = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
|
|
31287
|
-
headers: { Accept: "application/vnd.github.v3+json" }
|
|
31491
|
+
headers: { Accept: "application/vnd.github.v3+json" },
|
|
31492
|
+
signal: controller.signal
|
|
31288
31493
|
});
|
|
31289
31494
|
if (!response.ok)
|
|
31290
31495
|
return null;
|
|
@@ -31292,18 +31497,20 @@ async function fetchLatestTag() {
|
|
|
31292
31497
|
return data.tag_name ?? null;
|
|
31293
31498
|
} catch {
|
|
31294
31499
|
return null;
|
|
31500
|
+
} finally {
|
|
31501
|
+
clearTimeout(timeout);
|
|
31295
31502
|
}
|
|
31296
31503
|
}
|
|
31297
31504
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
31298
31505
|
import { execFileSync } from "node:child_process";
|
|
31299
|
-
import { createHash } from "node:crypto";
|
|
31300
|
-
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 "node:fs";
|
|
31506
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
31507
|
+
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 "node:fs";
|
|
31301
31508
|
import { dirname, join as join3, relative, resolve } from "node:path";
|
|
31302
|
-
import { Readable } from "node:stream";
|
|
31303
|
-
import { pipeline } from "node:stream/promises";
|
|
31509
|
+
import { Readable as Readable2 } from "node:stream";
|
|
31510
|
+
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
31304
31511
|
var ORT_VERSION = "1.24.4";
|
|
31305
31512
|
var ORT_REPO = "microsoft/onnxruntime";
|
|
31306
|
-
var
|
|
31513
|
+
var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
|
|
31307
31514
|
var MAX_EXTRACT_BYTES = 1 * 1024 * 1024 * 1024;
|
|
31308
31515
|
var ONNX_LOCK_FILE = ".aft-onnx-installing";
|
|
31309
31516
|
var ONNX_INSTALLED_META_FILE = ".aft-onnx-installed";
|
|
@@ -31421,7 +31628,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
31421
31628
|
if (Number.isFinite(pid) && pid > 0) {
|
|
31422
31629
|
if (process.platform === "win32") {
|
|
31423
31630
|
try {
|
|
31424
|
-
const ageMs = Date.now() -
|
|
31631
|
+
const ageMs = Date.now() - statSync2(stagingDir).mtimeMs;
|
|
31425
31632
|
abandoned = ageMs > STALE_LOCK_MS;
|
|
31426
31633
|
} catch {
|
|
31427
31634
|
abandoned = true;
|
|
@@ -31435,7 +31642,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
31435
31642
|
if (abandoned) {
|
|
31436
31643
|
log(`[onnx] removing abandoned staging dir ${stagingDir}`);
|
|
31437
31644
|
try {
|
|
31438
|
-
|
|
31645
|
+
rmSync2(stagingDir, { recursive: true, force: true });
|
|
31439
31646
|
} catch (err) {
|
|
31440
31647
|
warn(`[onnx] failed to remove ${stagingDir}: ${err}`);
|
|
31441
31648
|
}
|
|
@@ -31447,7 +31654,7 @@ function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
|
31447
31654
|
try {
|
|
31448
31655
|
if (existsSync2(ortDir) && !existsSync2(join3(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
31449
31656
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
31450
|
-
|
|
31657
|
+
rmSync2(ortDir, { recursive: true, force: true });
|
|
31451
31658
|
}
|
|
31452
31659
|
} catch (err) {
|
|
31453
31660
|
warn(`[onnx] failed to sweep ${ortDir}: ${err}`);
|
|
@@ -31527,24 +31734,24 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
31527
31734
|
throw new Error(`download failed (HTTP ${res.status})`);
|
|
31528
31735
|
}
|
|
31529
31736
|
const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
|
|
31530
|
-
if (Number.isFinite(advertised) && advertised >
|
|
31531
|
-
throw new Error(`Content-Length ${advertised} exceeds max ${
|
|
31737
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
31738
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
31532
31739
|
}
|
|
31533
31740
|
mkdirSync2(dirname(destPath), { recursive: true });
|
|
31534
31741
|
let bytesWritten = 0;
|
|
31535
31742
|
const guard = new TransformStream({
|
|
31536
31743
|
transform(chunk, transformController) {
|
|
31537
31744
|
bytesWritten += chunk.byteLength;
|
|
31538
|
-
if (bytesWritten >
|
|
31539
|
-
transformController.error(new Error(`download exceeded ${
|
|
31745
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES2) {
|
|
31746
|
+
transformController.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES2} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
31540
31747
|
return;
|
|
31541
31748
|
}
|
|
31542
31749
|
transformController.enqueue(chunk);
|
|
31543
31750
|
}
|
|
31544
31751
|
});
|
|
31545
31752
|
const guarded = res.body.pipeThrough(guard);
|
|
31546
|
-
const nodeStream =
|
|
31547
|
-
await
|
|
31753
|
+
const nodeStream = Readable2.fromWeb(guarded);
|
|
31754
|
+
await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
|
|
31548
31755
|
} catch (err) {
|
|
31549
31756
|
try {
|
|
31550
31757
|
unlinkSync2(destPath);
|
|
@@ -31643,16 +31850,16 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
31643
31850
|
warn(`Could not hash newly-installed ONNX library at ${libPath}: ${err}`);
|
|
31644
31851
|
}
|
|
31645
31852
|
writeOnnxInstalledMeta(targetDir, ORT_VERSION, libHash, archiveSha256);
|
|
31646
|
-
|
|
31853
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
31647
31854
|
log(`ONNX Runtime v${ORT_VERSION} installed to ${targetDir}`);
|
|
31648
31855
|
return targetDir;
|
|
31649
31856
|
} catch (err) {
|
|
31650
31857
|
error(`Failed to download ONNX Runtime: ${err}`);
|
|
31651
31858
|
try {
|
|
31652
|
-
|
|
31859
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
31653
31860
|
} catch {}
|
|
31654
31861
|
try {
|
|
31655
|
-
|
|
31862
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
31656
31863
|
} catch {}
|
|
31657
31864
|
return null;
|
|
31658
31865
|
}
|
|
@@ -31669,7 +31876,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
31669
31876
|
}
|
|
31670
31877
|
} catch (copyErr) {
|
|
31671
31878
|
if (requiredLibs.has(libFile)) {
|
|
31672
|
-
|
|
31879
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
31673
31880
|
throw copyErr;
|
|
31674
31881
|
}
|
|
31675
31882
|
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
@@ -31684,7 +31891,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
31684
31891
|
symlinkSync(link.target, dst);
|
|
31685
31892
|
} catch (symlinkErr) {
|
|
31686
31893
|
if (requiredLibs.has(link.name)) {
|
|
31687
|
-
|
|
31894
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
31688
31895
|
throw symlinkErr;
|
|
31689
31896
|
}
|
|
31690
31897
|
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
@@ -31692,7 +31899,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
31692
31899
|
}
|
|
31693
31900
|
const requiredPath = join3(targetDir, info.libName);
|
|
31694
31901
|
if (!existsSync2(requiredPath)) {
|
|
31695
|
-
|
|
31902
|
+
rmSync2(targetDir, { recursive: true, force: true });
|
|
31696
31903
|
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
31697
31904
|
}
|
|
31698
31905
|
}
|
|
@@ -31725,7 +31932,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
31725
31932
|
function readOnnxInstalledMeta(installDir) {
|
|
31726
31933
|
const path = join3(installDir, ONNX_INSTALLED_META_FILE);
|
|
31727
31934
|
try {
|
|
31728
|
-
if (!
|
|
31935
|
+
if (!statSync2(path).isFile())
|
|
31729
31936
|
return null;
|
|
31730
31937
|
const raw = readFileSync(path, "utf8");
|
|
31731
31938
|
const parsed = JSON.parse(raw);
|
|
@@ -31742,20 +31949,20 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
31742
31949
|
}
|
|
31743
31950
|
}
|
|
31744
31951
|
function sha256File(path) {
|
|
31745
|
-
const hash =
|
|
31952
|
+
const hash = createHash2("sha256");
|
|
31746
31953
|
hash.update(readFileSync(path));
|
|
31747
31954
|
return hash.digest("hex");
|
|
31748
31955
|
}
|
|
31749
31956
|
function acquireLock(lockPath) {
|
|
31750
31957
|
const tryClaim = () => {
|
|
31751
31958
|
try {
|
|
31752
|
-
const fd =
|
|
31959
|
+
const fd = openSync2(lockPath, "wx");
|
|
31753
31960
|
try {
|
|
31754
31961
|
writeFileSync(fd, `${process.pid}
|
|
31755
31962
|
${new Date().toISOString()}
|
|
31756
31963
|
`);
|
|
31757
31964
|
} finally {
|
|
31758
|
-
|
|
31965
|
+
closeSync2(fd);
|
|
31759
31966
|
}
|
|
31760
31967
|
return true;
|
|
31761
31968
|
} catch (err) {
|
|
@@ -31776,7 +31983,7 @@ ${new Date().toISOString()}
|
|
|
31776
31983
|
const parsed = Number.parseInt(firstLine, 10);
|
|
31777
31984
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
31778
31985
|
owningPid = parsed;
|
|
31779
|
-
lockMtimeMs =
|
|
31986
|
+
lockMtimeMs = statSync2(lockPath).mtimeMs;
|
|
31780
31987
|
} catch {
|
|
31781
31988
|
return tryClaim();
|
|
31782
31989
|
}
|
|
@@ -31893,7 +32100,9 @@ class BridgePool {
|
|
|
31893
32100
|
onVersionMismatch: options.onVersionMismatch,
|
|
31894
32101
|
onConfigureWarnings: options.onConfigureWarnings,
|
|
31895
32102
|
onBashCompletion: options.onBashCompletion,
|
|
31896
|
-
onBashLongRunning: options.onBashLongRunning
|
|
32103
|
+
onBashLongRunning: options.onBashLongRunning,
|
|
32104
|
+
errorPrefix: options.errorPrefix,
|
|
32105
|
+
logger: options.logger
|
|
31897
32106
|
};
|
|
31898
32107
|
this.configOverrides = configOverrides;
|
|
31899
32108
|
if (Number.isFinite(this.idleTimeoutMs)) {
|
|
@@ -31965,23 +32174,32 @@ class BridgePool {
|
|
|
31965
32174
|
}
|
|
31966
32175
|
async replaceBinary(newPath) {
|
|
31967
32176
|
this.binaryPath = newPath;
|
|
31968
|
-
const shutdowns = Array.from(this.bridges.values()).map((entry) => entry.bridge.shutdown());
|
|
31969
32177
|
this.bridges.clear();
|
|
31970
|
-
await Promise.allSettled(shutdowns);
|
|
31971
32178
|
this.log(`Binary path updated to ${newPath}. All bridges cleared — next calls will use the new binary.`);
|
|
32179
|
+
return newPath;
|
|
31972
32180
|
}
|
|
31973
32181
|
log(message, meta) {
|
|
31974
32182
|
const logger = this.logger ?? getActiveLogger();
|
|
31975
|
-
if (logger)
|
|
31976
|
-
|
|
31977
|
-
|
|
32183
|
+
if (logger) {
|
|
32184
|
+
try {
|
|
32185
|
+
logger.log(message, meta);
|
|
32186
|
+
} catch (err) {
|
|
32187
|
+
console.error(`[aft-bridge] ERROR: pool logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
32188
|
+
console.error(`[aft-bridge] ${message}`);
|
|
32189
|
+
}
|
|
32190
|
+
} else
|
|
31978
32191
|
log(message, meta);
|
|
31979
32192
|
}
|
|
31980
32193
|
error(message, meta) {
|
|
31981
32194
|
const logger = this.logger ?? getActiveLogger();
|
|
31982
|
-
if (logger)
|
|
31983
|
-
|
|
31984
|
-
|
|
32195
|
+
if (logger) {
|
|
32196
|
+
try {
|
|
32197
|
+
logger.error(message, meta);
|
|
32198
|
+
} catch (err) {
|
|
32199
|
+
console.error(`[aft-bridge] ERROR: pool logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
32200
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
32201
|
+
}
|
|
32202
|
+
} else
|
|
31985
32203
|
error(message, meta);
|
|
31986
32204
|
}
|
|
31987
32205
|
setConfigureOverride(key, value) {
|
|
@@ -32008,7 +32226,7 @@ function normalizeKey(projectRoot) {
|
|
|
32008
32226
|
}
|
|
32009
32227
|
// ../aft-bridge/dist/resolver.js
|
|
32010
32228
|
import { execSync, spawnSync } from "node:child_process";
|
|
32011
|
-
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync } from "node:fs";
|
|
32229
|
+
import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync2 } from "node:fs";
|
|
32012
32230
|
import { createRequire as createRequire2 } from "node:module";
|
|
32013
32231
|
import { homedir as homedir4 } from "node:os";
|
|
32014
32232
|
import { join as join4 } from "node:path";
|
|
@@ -32019,7 +32237,9 @@ function readBinaryVersion(binaryPath) {
|
|
|
32019
32237
|
stdio: ["pipe", "pipe", "pipe"],
|
|
32020
32238
|
timeout: 5000
|
|
32021
32239
|
});
|
|
32022
|
-
const
|
|
32240
|
+
const stdoutVersion = result.stdout?.trim();
|
|
32241
|
+
const stderrVersion = result.stderr?.trim();
|
|
32242
|
+
const rawVersion = stdoutVersion || stderrVersion;
|
|
32023
32243
|
if (!rawVersion)
|
|
32024
32244
|
return null;
|
|
32025
32245
|
return rawVersion.replace(/^aft\s+/, "");
|
|
@@ -32037,15 +32257,19 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
32037
32257
|
const versionedDir = join4(cacheDir, tag);
|
|
32038
32258
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
32039
32259
|
const cachedPath = join4(versionedDir, `aft${ext}`);
|
|
32040
|
-
if (existsSync3(cachedPath))
|
|
32041
|
-
|
|
32260
|
+
if (existsSync3(cachedPath)) {
|
|
32261
|
+
const cachedVersion = readBinaryVersion(cachedPath);
|
|
32262
|
+
if (cachedVersion === version)
|
|
32263
|
+
return cachedPath;
|
|
32264
|
+
warn(`Cached binary at ${cachedPath} reports ${cachedVersion ?? "no version"}, expected ${version}; refreshing from npm package`);
|
|
32265
|
+
}
|
|
32042
32266
|
mkdirSync3(versionedDir, { recursive: true });
|
|
32043
|
-
const tmpPath = `${cachedPath}.tmp`;
|
|
32267
|
+
const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
|
|
32044
32268
|
copyFileSync2(npmBinaryPath, tmpPath);
|
|
32045
32269
|
if (process.platform !== "win32") {
|
|
32046
32270
|
chmodSync3(tmpPath, 493);
|
|
32047
32271
|
}
|
|
32048
|
-
|
|
32272
|
+
renameSync2(tmpPath, cachedPath);
|
|
32049
32273
|
log(`Copied npm binary to versioned cache: ${cachedPath}`);
|
|
32050
32274
|
return cachedPath;
|
|
32051
32275
|
} catch (err) {
|
|
@@ -32053,6 +32277,17 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
32053
32277
|
return null;
|
|
32054
32278
|
}
|
|
32055
32279
|
}
|
|
32280
|
+
function normalizeBareVersion(version) {
|
|
32281
|
+
return version.startsWith("v") ? version.slice(1) : version;
|
|
32282
|
+
}
|
|
32283
|
+
function isExpectedCachedBinary(binaryPath, expectedVersion) {
|
|
32284
|
+
const expected = normalizeBareVersion(expectedVersion);
|
|
32285
|
+
const actual = readBinaryVersion(binaryPath);
|
|
32286
|
+
if (actual === expected)
|
|
32287
|
+
return true;
|
|
32288
|
+
warn(`Cached binary at ${binaryPath} reports ${actual ?? "no version"}, expected ${expected}; skipping cache candidate`);
|
|
32289
|
+
return false;
|
|
32290
|
+
}
|
|
32056
32291
|
function platformKey(platform = process.platform, arch = process.arch) {
|
|
32057
32292
|
const archMap = PLATFORM_ARCH_MAP[platform];
|
|
32058
32293
|
if (!archMap) {
|
|
@@ -32077,7 +32312,7 @@ function findBinarySync(expectedVersion) {
|
|
|
32077
32312
|
if (pluginVersion) {
|
|
32078
32313
|
const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
|
|
32079
32314
|
const versionCached = getCachedBinaryPath(tag);
|
|
32080
|
-
if (versionCached)
|
|
32315
|
+
if (versionCached && isExpectedCachedBinary(versionCached, pluginVersion))
|
|
32081
32316
|
return versionCached;
|
|
32082
32317
|
}
|
|
32083
32318
|
try {
|
|
@@ -32087,10 +32322,12 @@ function findBinarySync(expectedVersion) {
|
|
|
32087
32322
|
const resolved = req.resolve(packageBin);
|
|
32088
32323
|
if (existsSync3(resolved)) {
|
|
32089
32324
|
const npmVersion = readBinaryVersion(resolved);
|
|
32090
|
-
if (
|
|
32325
|
+
if (npmVersion === null) {
|
|
32326
|
+
warn(`npm platform package binary at ${resolved} did not report a version; skipping (continuing to PATH lookup)`);
|
|
32327
|
+
} else if (pluginVersion && npmVersion !== normalizeBareVersion(pluginVersion)) {
|
|
32091
32328
|
warn(`npm platform package binary v${npmVersion} does not match plugin v${pluginVersion}; skipping (continuing to PATH lookup)`);
|
|
32092
32329
|
} else {
|
|
32093
|
-
const copied = copyToVersionedCache(resolved, npmVersion
|
|
32330
|
+
const copied = copyToVersionedCache(resolved, npmVersion);
|
|
32094
32331
|
return copied ?? resolved;
|
|
32095
32332
|
}
|
|
32096
32333
|
}
|
|
@@ -32139,7 +32376,7 @@ async function findBinary(expectedVersion) {
|
|
|
32139
32376
|
`));
|
|
32140
32377
|
}
|
|
32141
32378
|
// ../aft-bridge/dist/url-fetch.js
|
|
32142
|
-
import { createHash as
|
|
32379
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
32143
32380
|
import { lookup } from "node:dns/promises";
|
|
32144
32381
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
32145
32382
|
import { isIP } from "node:net";
|
|
@@ -32296,7 +32533,7 @@ function cacheDir(storageDir) {
|
|
|
32296
32533
|
return join5(storageDir, "url_cache");
|
|
32297
32534
|
}
|
|
32298
32535
|
function hashUrl(url) {
|
|
32299
|
-
return
|
|
32536
|
+
return createHash3("sha256").update(url).digest("hex").slice(0, 16);
|
|
32300
32537
|
}
|
|
32301
32538
|
function metaPath(storageDir, hash) {
|
|
32302
32539
|
return join5(cacheDir(storageDir), `${hash}.meta.json`);
|
|
@@ -32498,6 +32735,7 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
32498
32735
|
throw new Error(`Only http:// and https:// URLs are supported, got: ${parsed.protocol}`);
|
|
32499
32736
|
}
|
|
32500
32737
|
const allowPrivate = options.allowPrivate === true;
|
|
32738
|
+
await assertPublicUrl(parsed, allowPrivate, options.lookup);
|
|
32501
32739
|
const dir = cacheDir(storageDir);
|
|
32502
32740
|
mkdirSync4(dir, { recursive: true });
|
|
32503
32741
|
const hash = hashUrl(url);
|
|
@@ -32553,8 +32791,8 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
32553
32791
|
const contentFile = contentPath(storageDir, hash, extension);
|
|
32554
32792
|
const tmpContent = `${contentFile}.tmp-${process.pid}`;
|
|
32555
32793
|
writeFileSync2(tmpContent, body);
|
|
32556
|
-
const { renameSync:
|
|
32557
|
-
|
|
32794
|
+
const { renameSync: renameSync3 } = await import("node:fs");
|
|
32795
|
+
renameSync3(tmpContent, contentFile);
|
|
32558
32796
|
const meta = {
|
|
32559
32797
|
url,
|
|
32560
32798
|
contentType,
|
|
@@ -32563,7 +32801,7 @@ async function fetchUrlToTempFile(url, storageDir, options = {}) {
|
|
|
32563
32801
|
};
|
|
32564
32802
|
const tmpMeta = `${metaFile}.tmp-${process.pid}`;
|
|
32565
32803
|
writeFileSync2(tmpMeta, JSON.stringify(meta));
|
|
32566
|
-
|
|
32804
|
+
renameSync3(tmpMeta, metaFile);
|
|
32567
32805
|
log(`URL cached (${total} bytes): ${url}`);
|
|
32568
32806
|
return contentFile;
|
|
32569
32807
|
}
|
|
@@ -32681,31 +32919,31 @@ function warn2(message, data) {
|
|
|
32681
32919
|
function error2(message, data) {
|
|
32682
32920
|
write("ERROR", message, data);
|
|
32683
32921
|
}
|
|
32684
|
-
function
|
|
32922
|
+
function sessionLog(sessionId, message, data) {
|
|
32685
32923
|
write("INFO", message, data, sessionId);
|
|
32686
32924
|
}
|
|
32687
|
-
function
|
|
32925
|
+
function sessionWarn(sessionId, message, data) {
|
|
32688
32926
|
write("WARN", message, data, sessionId);
|
|
32689
32927
|
}
|
|
32690
|
-
function
|
|
32928
|
+
function sessionError(sessionId, message, data) {
|
|
32691
32929
|
write("ERROR", message, data, sessionId);
|
|
32692
32930
|
}
|
|
32693
32931
|
var bridgeLogger = {
|
|
32694
32932
|
log(message, meta) {
|
|
32695
32933
|
if (meta?.sessionId)
|
|
32696
|
-
|
|
32934
|
+
sessionLog(meta.sessionId, message);
|
|
32697
32935
|
else
|
|
32698
32936
|
write("INFO", message);
|
|
32699
32937
|
},
|
|
32700
32938
|
warn(message, meta) {
|
|
32701
32939
|
if (meta?.sessionId)
|
|
32702
|
-
|
|
32940
|
+
sessionWarn(meta.sessionId, message);
|
|
32703
32941
|
else
|
|
32704
32942
|
write("WARN", message);
|
|
32705
32943
|
},
|
|
32706
32944
|
error(message, meta) {
|
|
32707
32945
|
if (meta?.sessionId)
|
|
32708
|
-
|
|
32946
|
+
sessionError(meta.sessionId, message);
|
|
32709
32947
|
else
|
|
32710
32948
|
write("ERROR", message);
|
|
32711
32949
|
},
|
|
@@ -32717,6 +32955,7 @@ var sessionBgStates = new Map;
|
|
|
32717
32955
|
var SESSION_BG_STATE_IDLE_TTL_MS = 60 * 60 * 1000;
|
|
32718
32956
|
var DEBOUNCE_STEP_MS = 200;
|
|
32719
32957
|
var DEBOUNCE_CAP_MS = 1000;
|
|
32958
|
+
var MAX_WAKE_SEND_ATTEMPTS = 5;
|
|
32720
32959
|
var UNKNOWN_COMPLETION_TTL_MS = 5000;
|
|
32721
32960
|
var UNKNOWN_COMPLETION_CAP = 32;
|
|
32722
32961
|
var DEFAULT_SESSION_ID = "__default__";
|
|
@@ -32766,16 +33005,22 @@ async function handlePushedBgLongRunning(drainContext, reminder) {
|
|
|
32766
33005
|
}
|
|
32767
33006
|
async function appendToolResultBgCompletions(drainContext, content) {
|
|
32768
33007
|
const state = stateFor(drainContext.sessionID);
|
|
33008
|
+
if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
33009
|
+
await drainCompletions(drainContext);
|
|
32769
33010
|
if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
32770
33011
|
return;
|
|
32771
|
-
if (state.outstandingTaskIds.size > 0) {
|
|
33012
|
+
if (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted) {
|
|
32772
33013
|
await drainCompletions(drainContext);
|
|
32773
33014
|
}
|
|
32774
33015
|
if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
32775
33016
|
return;
|
|
33017
|
+
const deliveredCompletions = [...state.pendingCompletions];
|
|
32776
33018
|
const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning);
|
|
32777
33019
|
state.pendingCompletions = [];
|
|
32778
33020
|
state.pendingLongRunning = [];
|
|
33021
|
+
state.wakeRetryAttempts = 0;
|
|
33022
|
+
state.wakeHardStopped = false;
|
|
33023
|
+
await ackCompletions(drainContext, deliveredCompletions);
|
|
32779
33024
|
if (state.debounceTimer) {
|
|
32780
33025
|
clearTimeout(state.debounceTimer);
|
|
32781
33026
|
state.debounceTimer = null;
|
|
@@ -32790,15 +33035,16 @@ async function handleTurnEndBgCompletions(drainContext) {
|
|
|
32790
33035
|
}
|
|
32791
33036
|
async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
32792
33037
|
const state = stateFor(drainContext.sessionID);
|
|
32793
|
-
if (!skipDrain && state.outstandingTaskIds.size > 0) {
|
|
33038
|
+
if (!skipDrain && (state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
|
|
32794
33039
|
await drainCompletions(drainContext);
|
|
32795
33040
|
}
|
|
32796
33041
|
if (state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0)
|
|
32797
33042
|
return;
|
|
32798
|
-
scheduleWake(state, async (reminder) => {
|
|
33043
|
+
scheduleWake(state, async (reminder, deliveredCompletions) => {
|
|
32799
33044
|
drainContext.runtime.sendUserMessage(reminder, { deliverAs: "steer" });
|
|
32800
|
-
|
|
32801
|
-
|
|
33045
|
+
await ackCompletions(drainContext, deliveredCompletions);
|
|
33046
|
+
}, (err, hardStopped) => {
|
|
33047
|
+
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)}`);
|
|
32802
33048
|
});
|
|
32803
33049
|
}
|
|
32804
33050
|
function formatSystemReminder(completions) {
|
|
@@ -32831,20 +33077,39 @@ function formatCombinedSystemReminder(completions, longRunning) {
|
|
|
32831
33077
|
${formatLongRunningReminder(longRunning)}`;
|
|
32832
33078
|
}
|
|
32833
33079
|
async function drainCompletions({ ctx, directory, sessionID }) {
|
|
33080
|
+
const state = stateFor(sessionID);
|
|
32834
33081
|
try {
|
|
32835
33082
|
const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
|
|
32836
33083
|
const params = sessionID ? { session_id: sessionID } : {};
|
|
32837
33084
|
const response = await bridge.send("bash_drain_completions", params);
|
|
32838
33085
|
if (response.success === false) {
|
|
32839
|
-
|
|
33086
|
+
sessionWarn(sessionID ?? "", `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
|
|
32840
33087
|
return;
|
|
32841
33088
|
}
|
|
32842
|
-
|
|
33089
|
+
state.forcedDrainCompleted = true;
|
|
33090
|
+
ingestDrainedBgCompletions(sessionID, response.bg_completions);
|
|
33091
|
+
} catch (err) {
|
|
33092
|
+
sessionWarn(sessionID ?? "", `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
33093
|
+
}
|
|
33094
|
+
}
|
|
33095
|
+
async function ackCompletions({ ctx, directory, sessionID }, completions) {
|
|
33096
|
+
const taskIds = [...new Set(completions.map((completion) => completion.task_id))];
|
|
33097
|
+
if (taskIds.length === 0)
|
|
33098
|
+
return;
|
|
33099
|
+
try {
|
|
33100
|
+
const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
|
|
33101
|
+
const params = sessionID ? { session_id: sessionID, task_ids: taskIds } : { task_ids: taskIds };
|
|
33102
|
+
const response = await bridge.send("bash_ack_completions", params);
|
|
33103
|
+
if (response.success === false) {
|
|
33104
|
+
sessionWarn(sessionID ?? "", `${LOG_PREFIX} ack failed: ${String(response.message ?? "unknown error")}`);
|
|
33105
|
+
}
|
|
32843
33106
|
} catch (err) {
|
|
32844
|
-
|
|
33107
|
+
sessionWarn(sessionID ?? "", `${LOG_PREFIX} ack failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
32845
33108
|
}
|
|
32846
33109
|
}
|
|
32847
33110
|
function scheduleWake(state, sendWake, onSendFailure) {
|
|
33111
|
+
if (state.wakeHardStopped)
|
|
33112
|
+
return;
|
|
32848
33113
|
const now = Date.now();
|
|
32849
33114
|
const pendingCount = state.pendingCompletions.length + state.pendingLongRunning.length;
|
|
32850
33115
|
if (state.debounceTimer && pendingCount <= state.scheduledCompletionCount) {
|
|
@@ -32873,13 +33138,22 @@ function scheduleWake(state, sendWake, onSendFailure) {
|
|
|
32873
33138
|
const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
|
|
32874
33139
|
state.pendingCompletions = [];
|
|
32875
33140
|
state.pendingLongRunning = [];
|
|
32876
|
-
sendWake(reminder).then(() => {
|
|
33141
|
+
sendWake(reminder, pending).then(() => {
|
|
32877
33142
|
state.retryDelayMs = null;
|
|
33143
|
+
state.wakeRetryAttempts = 0;
|
|
33144
|
+
state.wakeHardStopped = false;
|
|
32878
33145
|
}).catch((err) => {
|
|
32879
33146
|
state.pendingCompletions = [...pending, ...state.pendingCompletions];
|
|
32880
33147
|
state.pendingLongRunning = [...pendingLongRunning, ...state.pendingLongRunning];
|
|
33148
|
+
state.wakeRetryAttempts += 1;
|
|
33149
|
+
if (state.wakeRetryAttempts >= MAX_WAKE_SEND_ATTEMPTS) {
|
|
33150
|
+
state.retryDelayMs = null;
|
|
33151
|
+
state.wakeHardStopped = true;
|
|
33152
|
+
onSendFailure(err, true);
|
|
33153
|
+
return;
|
|
33154
|
+
}
|
|
32881
33155
|
state.retryDelayMs = Math.min((delay || DEBOUNCE_STEP_MS) * 2, DEBOUNCE_CAP_MS);
|
|
32882
|
-
onSendFailure(err);
|
|
33156
|
+
onSendFailure(err, false);
|
|
32883
33157
|
scheduleWake(state, sendWake, onSendFailure);
|
|
32884
33158
|
});
|
|
32885
33159
|
}, delay);
|
|
@@ -32900,6 +33174,9 @@ function stateFor(sessionID) {
|
|
|
32900
33174
|
scheduledFireAt: null,
|
|
32901
33175
|
scheduledCompletionCount: 0,
|
|
32902
33176
|
retryDelayMs: null,
|
|
33177
|
+
wakeRetryAttempts: 0,
|
|
33178
|
+
wakeHardStopped: false,
|
|
33179
|
+
forcedDrainCompleted: false,
|
|
32903
33180
|
unknownCompletions: [],
|
|
32904
33181
|
lastSeenAt: now
|
|
32905
33182
|
};
|
|
@@ -32909,6 +33186,22 @@ function stateFor(sessionID) {
|
|
|
32909
33186
|
}
|
|
32910
33187
|
return state;
|
|
32911
33188
|
}
|
|
33189
|
+
function ingestDrainedBgCompletions(sessionID, completions) {
|
|
33190
|
+
if (!Array.isArray(completions) || completions.length === 0)
|
|
33191
|
+
return [];
|
|
33192
|
+
const state = stateFor(sessionID);
|
|
33193
|
+
const accepted = [];
|
|
33194
|
+
for (const completion of completions) {
|
|
33195
|
+
if (!isBgCompletion(completion))
|
|
33196
|
+
continue;
|
|
33197
|
+
state.outstandingTaskIds.delete(completion.task_id);
|
|
33198
|
+
if (!state.pendingCompletions.some((pending) => pending.task_id === completion.task_id) && !accepted.some((pending) => pending.task_id === completion.task_id)) {
|
|
33199
|
+
accepted.push(completion);
|
|
33200
|
+
}
|
|
33201
|
+
}
|
|
33202
|
+
state.pendingCompletions.push(...accepted);
|
|
33203
|
+
return accepted;
|
|
33204
|
+
}
|
|
32912
33205
|
function cleanupIdleSessionStates(now = Date.now()) {
|
|
32913
33206
|
const cutoff = now - SESSION_BG_STATE_IDLE_TTL_MS;
|
|
32914
33207
|
for (const [sessionID, state] of sessionBgStates) {
|
|
@@ -33001,7 +33294,7 @@ import {
|
|
|
33001
33294
|
// package.json
|
|
33002
33295
|
var package_default = {
|
|
33003
33296
|
name: "@cortexkit/aft-pi",
|
|
33004
|
-
version: "0.
|
|
33297
|
+
version: "0.26.1",
|
|
33005
33298
|
type: "module",
|
|
33006
33299
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
33007
33300
|
main: "dist/index.js",
|
|
@@ -33023,18 +33316,18 @@ var package_default = {
|
|
|
33023
33316
|
prepublishOnly: "bun run build"
|
|
33024
33317
|
},
|
|
33025
33318
|
dependencies: {
|
|
33026
|
-
"@cortexkit/aft-bridge": "0.
|
|
33319
|
+
"@cortexkit/aft-bridge": "0.26.1",
|
|
33027
33320
|
typebox: "^1.1.24",
|
|
33028
33321
|
"comment-json": "^5.0.0",
|
|
33029
33322
|
diff: "^8.0.4",
|
|
33030
33323
|
zod: "^4.1.8"
|
|
33031
33324
|
},
|
|
33032
33325
|
optionalDependencies: {
|
|
33033
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
33034
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
33035
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
33036
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
33037
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
33326
|
+
"@cortexkit/aft-darwin-arm64": "0.26.1",
|
|
33327
|
+
"@cortexkit/aft-darwin-x64": "0.26.1",
|
|
33328
|
+
"@cortexkit/aft-linux-arm64": "0.26.1",
|
|
33329
|
+
"@cortexkit/aft-linux-x64": "0.26.1",
|
|
33330
|
+
"@cortexkit/aft-win32-x64": "0.26.1"
|
|
33038
33331
|
},
|
|
33039
33332
|
devDependencies: {
|
|
33040
33333
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -33519,7 +33812,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
33519
33812
|
|
|
33520
33813
|
// src/config.ts
|
|
33521
33814
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
33522
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as
|
|
33815
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
33523
33816
|
import { homedir as homedir5 } from "node:os";
|
|
33524
33817
|
import { join as join7 } from "node:path";
|
|
33525
33818
|
|
|
@@ -47294,7 +47587,7 @@ function migrateAftConfigFile(configPath, logger = { log: log2, warn: warn2 }) {
|
|
|
47294
47587
|
${serialized}` : serialized;
|
|
47295
47588
|
tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
47296
47589
|
writeFileSync3(tmpPath, nextContent, "utf-8");
|
|
47297
|
-
|
|
47590
|
+
renameSync3(tmpPath, configPath);
|
|
47298
47591
|
logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
|
|
47299
47592
|
return { migrated: true, oldKeys };
|
|
47300
47593
|
} catch (err) {
|
|
@@ -47509,17 +47802,17 @@ function loadAftConfig(projectDirectory) {
|
|
|
47509
47802
|
|
|
47510
47803
|
// src/lsp-auto-install.ts
|
|
47511
47804
|
import { spawn as spawn2 } from "node:child_process";
|
|
47512
|
-
import { createHash as
|
|
47513
|
-
import { createReadStream, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as
|
|
47805
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
47806
|
+
import { createReadStream, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
|
|
47514
47807
|
import { join as join10 } from "node:path";
|
|
47515
47808
|
|
|
47516
47809
|
// src/lsp-cache.ts
|
|
47517
47810
|
import {
|
|
47518
|
-
closeSync as
|
|
47811
|
+
closeSync as closeSync3,
|
|
47519
47812
|
mkdirSync as mkdirSync5,
|
|
47520
|
-
openSync as
|
|
47813
|
+
openSync as openSync3,
|
|
47521
47814
|
readFileSync as readFileSync4,
|
|
47522
|
-
statSync as
|
|
47815
|
+
statSync as statSync3,
|
|
47523
47816
|
unlinkSync as unlinkSync5,
|
|
47524
47817
|
writeFileSync as writeFileSync4
|
|
47525
47818
|
} from "node:fs";
|
|
@@ -47552,7 +47845,7 @@ function lspBinDir(npmPackage) {
|
|
|
47552
47845
|
function isInstalled(npmPackage, binary) {
|
|
47553
47846
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
47554
47847
|
try {
|
|
47555
|
-
if (
|
|
47848
|
+
if (statSync3(join8(lspBinDir(npmPackage), candidate)).isFile())
|
|
47556
47849
|
return true;
|
|
47557
47850
|
} catch {}
|
|
47558
47851
|
}
|
|
@@ -47580,7 +47873,7 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
47580
47873
|
function readInstalledMetaIn(installDir) {
|
|
47581
47874
|
const path2 = join8(installDir, INSTALLED_META_FILE);
|
|
47582
47875
|
try {
|
|
47583
|
-
if (!
|
|
47876
|
+
if (!statSync3(path2).isFile())
|
|
47584
47877
|
return null;
|
|
47585
47878
|
const raw = readFileSync4(path2, "utf8");
|
|
47586
47879
|
const parsed = JSON.parse(raw);
|
|
@@ -47610,13 +47903,13 @@ function acquireInstallLock(lockKey) {
|
|
|
47610
47903
|
const lock = lockPath(lockKey);
|
|
47611
47904
|
const tryClaim = () => {
|
|
47612
47905
|
try {
|
|
47613
|
-
const fd =
|
|
47906
|
+
const fd = openSync3(lock, "wx");
|
|
47614
47907
|
try {
|
|
47615
47908
|
writeFileSync4(fd, `${process.pid}
|
|
47616
47909
|
${new Date().toISOString()}
|
|
47617
47910
|
`);
|
|
47618
47911
|
} finally {
|
|
47619
|
-
|
|
47912
|
+
closeSync3(fd);
|
|
47620
47913
|
}
|
|
47621
47914
|
return true;
|
|
47622
47915
|
} catch (err) {
|
|
@@ -47637,7 +47930,7 @@ ${new Date().toISOString()}
|
|
|
47637
47930
|
const parsed = Number.parseInt(firstLine, 10);
|
|
47638
47931
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
47639
47932
|
owningPid = parsed;
|
|
47640
|
-
lockMtimeMs =
|
|
47933
|
+
lockMtimeMs = statSync3(lock).mtimeMs;
|
|
47641
47934
|
} catch {
|
|
47642
47935
|
return tryClaim();
|
|
47643
47936
|
}
|
|
@@ -48050,8 +48343,9 @@ function runInstall(spec, version2, cwd, signal) {
|
|
|
48050
48343
|
resolve2(false);
|
|
48051
48344
|
return;
|
|
48052
48345
|
}
|
|
48053
|
-
const child = spawn2("
|
|
48054
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
48346
|
+
const child = spawn2("npm", ["install", "--no-save", "--ignore-scripts", "--silent", target], {
|
|
48347
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
48348
|
+
cwd
|
|
48055
48349
|
});
|
|
48056
48350
|
child.unref();
|
|
48057
48351
|
let stderrBuf = "";
|
|
@@ -48178,7 +48472,7 @@ function hashInstalledBinary(spec) {
|
|
|
48178
48472
|
let pathToHash = null;
|
|
48179
48473
|
for (const p of candidates) {
|
|
48180
48474
|
try {
|
|
48181
|
-
if (
|
|
48475
|
+
if (statSync4(p).isFile()) {
|
|
48182
48476
|
pathToHash = p;
|
|
48183
48477
|
break;
|
|
48184
48478
|
}
|
|
@@ -48188,7 +48482,7 @@ function hashInstalledBinary(spec) {
|
|
|
48188
48482
|
reject(new Error(`installed binary not found at any of: ${candidates.join(", ")}`));
|
|
48189
48483
|
return;
|
|
48190
48484
|
}
|
|
48191
|
-
const hash2 =
|
|
48485
|
+
const hash2 = createHash4("sha256");
|
|
48192
48486
|
const stream = createReadStream(pathToHash);
|
|
48193
48487
|
stream.on("error", reject);
|
|
48194
48488
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
@@ -48204,14 +48498,14 @@ function installedBinaryPath(spec) {
|
|
|
48204
48498
|
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
48205
48499
|
for (const candidate of candidates) {
|
|
48206
48500
|
try {
|
|
48207
|
-
if (
|
|
48501
|
+
if (statSync4(candidate).isFile())
|
|
48208
48502
|
return candidate;
|
|
48209
48503
|
} catch {}
|
|
48210
48504
|
}
|
|
48211
48505
|
return null;
|
|
48212
48506
|
}
|
|
48213
48507
|
function sha256OfFileSync(path2) {
|
|
48214
|
-
return
|
|
48508
|
+
return createHash4("sha256").update(readFileSync5(path2)).digest("hex");
|
|
48215
48509
|
}
|
|
48216
48510
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
48217
48511
|
const packageDir = lspPackageDir(spec.npm);
|
|
@@ -48219,8 +48513,8 @@ function quarantineCachedNpmInstall(spec, reason) {
|
|
|
48219
48513
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
48220
48514
|
try {
|
|
48221
48515
|
mkdirSync6(join10(dest, ".."), { recursive: true });
|
|
48222
|
-
|
|
48223
|
-
|
|
48516
|
+
rmSync3(dest, { recursive: true, force: true });
|
|
48517
|
+
renameSync4(packageDir, dest);
|
|
48224
48518
|
} catch (err) {
|
|
48225
48519
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
48226
48520
|
}
|
|
@@ -48296,11 +48590,11 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
|
|
|
48296
48590
|
|
|
48297
48591
|
// src/lsp-github-install.ts
|
|
48298
48592
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
48299
|
-
import { createHash as
|
|
48593
|
+
import { createHash as createHash5, randomBytes } from "node:crypto";
|
|
48300
48594
|
import {
|
|
48301
48595
|
copyFileSync as copyFileSync3,
|
|
48302
48596
|
createReadStream as createReadStream2,
|
|
48303
|
-
createWriteStream as
|
|
48597
|
+
createWriteStream as createWriteStream3,
|
|
48304
48598
|
existsSync as existsSync7,
|
|
48305
48599
|
lstatSync as lstatSync2,
|
|
48306
48600
|
mkdirSync as mkdirSync7,
|
|
@@ -48308,14 +48602,15 @@ import {
|
|
|
48308
48602
|
readFileSync as readFileSync6,
|
|
48309
48603
|
readlinkSync as readlinkSync2,
|
|
48310
48604
|
realpathSync as realpathSync3,
|
|
48311
|
-
renameSync as
|
|
48312
|
-
rmSync as
|
|
48313
|
-
statSync as
|
|
48314
|
-
unlinkSync as unlinkSync6
|
|
48605
|
+
renameSync as renameSync5,
|
|
48606
|
+
rmSync as rmSync4,
|
|
48607
|
+
statSync as statSync5,
|
|
48608
|
+
unlinkSync as unlinkSync6,
|
|
48609
|
+
writeFileSync as writeFileSync5
|
|
48315
48610
|
} from "node:fs";
|
|
48316
48611
|
import { dirname as dirname2, join as join11, relative as relative2, resolve as resolve2 } from "node:path";
|
|
48317
|
-
import { Readable as
|
|
48318
|
-
import { pipeline as
|
|
48612
|
+
import { Readable as Readable3 } from "node:stream";
|
|
48613
|
+
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
48319
48614
|
|
|
48320
48615
|
// src/lsp-github-table.ts
|
|
48321
48616
|
function exe(platform, name) {
|
|
@@ -48414,6 +48709,7 @@ function ghBinDir(spec) {
|
|
|
48414
48709
|
function ghExtractDir(spec) {
|
|
48415
48710
|
return join11(ghPackageDir(spec), "extracted");
|
|
48416
48711
|
}
|
|
48712
|
+
var INSTALLED_META_FILE2 = ".aft-installed";
|
|
48417
48713
|
function ghBinaryPath(spec, platform) {
|
|
48418
48714
|
const ext = platform === "win32" ? ".exe" : "";
|
|
48419
48715
|
return join11(ghBinDir(spec), `${spec.binary}${ext}`);
|
|
@@ -48421,7 +48717,7 @@ function ghBinaryPath(spec, platform) {
|
|
|
48421
48717
|
function isGithubInstalled(spec, platform) {
|
|
48422
48718
|
for (const candidate of ghBinaryCandidates(spec, platform)) {
|
|
48423
48719
|
try {
|
|
48424
|
-
if (
|
|
48720
|
+
if (statSync5(join11(ghBinDir(spec), candidate)).isFile())
|
|
48425
48721
|
return true;
|
|
48426
48722
|
} catch {}
|
|
48427
48723
|
}
|
|
@@ -48432,11 +48728,45 @@ function ghBinaryCandidates(spec, platform) {
|
|
|
48432
48728
|
return [spec.binary];
|
|
48433
48729
|
return [spec.binary, `${spec.binary}.cmd`, `${spec.binary}.exe`, `${spec.binary}.bat`];
|
|
48434
48730
|
}
|
|
48435
|
-
|
|
48731
|
+
function readGithubInstalledMetaIn(installDir) {
|
|
48732
|
+
try {
|
|
48733
|
+
const path2 = join11(installDir, INSTALLED_META_FILE2);
|
|
48734
|
+
if (!statSync5(path2).isFile())
|
|
48735
|
+
return null;
|
|
48736
|
+
const parsed = JSON.parse(readFileSync6(path2, "utf8"));
|
|
48737
|
+
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
48738
|
+
return null;
|
|
48739
|
+
return {
|
|
48740
|
+
version: parsed.version,
|
|
48741
|
+
installedAt: typeof parsed.installedAt === "string" ? parsed.installedAt : "",
|
|
48742
|
+
...typeof parsed.sha256 === "string" && parsed.sha256.length > 0 ? { sha256: parsed.sha256 } : {},
|
|
48743
|
+
...typeof parsed.binarySha256 === "string" && parsed.binarySha256.length > 0 ? { binarySha256: parsed.binarySha256 } : {},
|
|
48744
|
+
...typeof parsed.archiveSha256 === "string" && parsed.archiveSha256.length > 0 ? { archiveSha256: parsed.archiveSha256 } : {}
|
|
48745
|
+
};
|
|
48746
|
+
} catch {
|
|
48747
|
+
return null;
|
|
48748
|
+
}
|
|
48749
|
+
}
|
|
48750
|
+
function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveSha256) {
|
|
48751
|
+
try {
|
|
48752
|
+
mkdirSync7(installDir, { recursive: true });
|
|
48753
|
+
const meta3 = {
|
|
48754
|
+
version: version2,
|
|
48755
|
+
installedAt: new Date().toISOString(),
|
|
48756
|
+
sha256: binarySha256,
|
|
48757
|
+
binarySha256,
|
|
48758
|
+
...archiveSha256 ? { archiveSha256 } : {}
|
|
48759
|
+
};
|
|
48760
|
+
writeFileSync5(join11(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
|
|
48761
|
+
} catch (err) {
|
|
48762
|
+
warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
|
|
48763
|
+
}
|
|
48764
|
+
}
|
|
48765
|
+
var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
|
|
48436
48766
|
var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
|
|
48437
48767
|
function sha256OfFile(path2) {
|
|
48438
48768
|
return new Promise((resolve3, reject) => {
|
|
48439
|
-
const hash2 =
|
|
48769
|
+
const hash2 = createHash5("sha256");
|
|
48440
48770
|
const stream = createReadStream2(path2);
|
|
48441
48771
|
stream.on("error", reject);
|
|
48442
48772
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
@@ -48444,7 +48774,7 @@ function sha256OfFile(path2) {
|
|
|
48444
48774
|
});
|
|
48445
48775
|
}
|
|
48446
48776
|
function sha256OfFileSync2(path2) {
|
|
48447
|
-
return
|
|
48777
|
+
return createHash5("sha256").update(readFileSync6(path2)).digest("hex");
|
|
48448
48778
|
}
|
|
48449
48779
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
|
|
48450
48780
|
const candidates = [];
|
|
@@ -48607,8 +48937,8 @@ function assertAllowedDownloadUrl(rawUrl) {
|
|
|
48607
48937
|
return parsed;
|
|
48608
48938
|
}
|
|
48609
48939
|
async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
48610
|
-
if (assetSize !== undefined && assetSize >
|
|
48611
|
-
throw new Error(`asset size ${assetSize} exceeds max ${
|
|
48940
|
+
if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES3) {
|
|
48941
|
+
throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES3} (set lsp.versions to pin a smaller release if this is wrong)`);
|
|
48612
48942
|
}
|
|
48613
48943
|
const timeout = controlledTimeoutSignal(120000, signal);
|
|
48614
48944
|
try {
|
|
@@ -48617,24 +48947,24 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
|
48617
48947
|
throw new Error(`download failed (${res.status})`);
|
|
48618
48948
|
}
|
|
48619
48949
|
const advertised = Number.parseInt(res.headers.get("content-length") ?? "", 10);
|
|
48620
|
-
if (Number.isFinite(advertised) && advertised >
|
|
48621
|
-
throw new Error(`Content-Length ${advertised} exceeds max ${
|
|
48950
|
+
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
|
|
48951
|
+
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
|
|
48622
48952
|
}
|
|
48623
48953
|
mkdirSync7(dirname2(destPath), { recursive: true });
|
|
48624
48954
|
let bytesWritten = 0;
|
|
48625
48955
|
const guard = new TransformStream({
|
|
48626
48956
|
transform(chunk, controller) {
|
|
48627
48957
|
bytesWritten += chunk.byteLength;
|
|
48628
|
-
if (bytesWritten >
|
|
48629
|
-
controller.error(new Error(`download exceeded ${
|
|
48958
|
+
if (bytesWritten > MAX_DOWNLOAD_BYTES3) {
|
|
48959
|
+
controller.error(new Error(`download exceeded ${MAX_DOWNLOAD_BYTES3} bytes after streaming (server lied about size or sent unbounded body)`));
|
|
48630
48960
|
return;
|
|
48631
48961
|
}
|
|
48632
48962
|
controller.enqueue(chunk);
|
|
48633
48963
|
}
|
|
48634
48964
|
});
|
|
48635
48965
|
const guarded = res.body.pipeThrough(guard);
|
|
48636
|
-
const nodeStream =
|
|
48637
|
-
await
|
|
48966
|
+
const nodeStream = Readable3.fromWeb(guarded);
|
|
48967
|
+
await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
|
|
48638
48968
|
} catch (err) {
|
|
48639
48969
|
try {
|
|
48640
48970
|
unlinkSync6(destPath);
|
|
@@ -48714,7 +49044,7 @@ function validateExtraction(stagingRoot) {
|
|
|
48714
49044
|
};
|
|
48715
49045
|
walk(realStagingRoot);
|
|
48716
49046
|
}
|
|
48717
|
-
function
|
|
49047
|
+
function precheckArchiveContents(archivePath, archiveType) {
|
|
48718
49048
|
let totalBytes = 0;
|
|
48719
49049
|
if (archiveType === "zip") {
|
|
48720
49050
|
const out = execFileSync2("unzip", ["-l", archivePath], { encoding: "utf8" });
|
|
@@ -48725,6 +49055,9 @@ function precheckArchiveSize(archivePath, archiveType) {
|
|
|
48725
49055
|
const out = execFileSync2("tar", ["-tvf", archivePath], { encoding: "utf8" });
|
|
48726
49056
|
for (const line of out.split(`
|
|
48727
49057
|
`)) {
|
|
49058
|
+
if (line.startsWith("h")) {
|
|
49059
|
+
throw new Error(`archive contains hardlink entry: ${line.trim()}`);
|
|
49060
|
+
}
|
|
48728
49061
|
const parts = line.trim().split(/\s+/);
|
|
48729
49062
|
if (parts.length >= 6) {
|
|
48730
49063
|
const numeric = parts.map((part) => Number.parseInt(part, 10)).filter((value) => Number.isFinite(value) && value >= 0);
|
|
@@ -48741,20 +49074,20 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
48741
49074
|
const suffix = randomBytes(8).toString("hex");
|
|
48742
49075
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
48743
49076
|
try {
|
|
48744
|
-
|
|
49077
|
+
rmSync4(stagingDir, { recursive: true, force: true });
|
|
48745
49078
|
} catch {}
|
|
48746
49079
|
mkdirSync7(stagingDir, { recursive: true });
|
|
48747
49080
|
try {
|
|
48748
|
-
|
|
49081
|
+
precheckArchiveContents(archivePath, archiveType);
|
|
48749
49082
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
48750
49083
|
validateExtraction(stagingDir);
|
|
48751
49084
|
try {
|
|
48752
|
-
|
|
49085
|
+
rmSync4(destDir, { recursive: true, force: true });
|
|
48753
49086
|
} catch {}
|
|
48754
|
-
|
|
49087
|
+
renameSync5(stagingDir, destDir);
|
|
48755
49088
|
} catch (err) {
|
|
48756
49089
|
try {
|
|
48757
|
-
|
|
49090
|
+
rmSync4(stagingDir, { recursive: true, force: true });
|
|
48758
49091
|
} catch {}
|
|
48759
49092
|
throw err;
|
|
48760
49093
|
}
|
|
@@ -48765,28 +49098,44 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
48765
49098
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
48766
49099
|
try {
|
|
48767
49100
|
mkdirSync7(dirname2(dest), { recursive: true });
|
|
48768
|
-
|
|
48769
|
-
|
|
49101
|
+
rmSync4(dest, { recursive: true, force: true });
|
|
49102
|
+
renameSync5(packageDir, dest);
|
|
48770
49103
|
} catch (err) {
|
|
48771
49104
|
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
48772
49105
|
}
|
|
48773
49106
|
}
|
|
48774
49107
|
function validateCachedGithubInstall(spec, platform) {
|
|
48775
|
-
const
|
|
49108
|
+
const packageDir = ghPackageDir(spec);
|
|
49109
|
+
const meta3 = readGithubInstalledMetaIn(packageDir);
|
|
48776
49110
|
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join11(ghBinDir(spec), candidate)).find((candidate) => {
|
|
48777
49111
|
try {
|
|
48778
|
-
return
|
|
49112
|
+
return statSync5(candidate).isFile();
|
|
48779
49113
|
} catch {
|
|
48780
49114
|
return false;
|
|
48781
49115
|
}
|
|
48782
49116
|
});
|
|
48783
|
-
if (!meta3?.
|
|
49117
|
+
if (!meta3?.version || !isSafeVersion(meta3.version) || !binaryPath) {
|
|
48784
49118
|
quarantineCachedGithubInstall(spec, "missing/unsafe metadata or binary");
|
|
48785
49119
|
return false;
|
|
48786
49120
|
}
|
|
48787
49121
|
const currentHash = sha256OfFileSync2(binaryPath);
|
|
48788
|
-
|
|
48789
|
-
|
|
49122
|
+
const recordedBinaryHash = meta3.binarySha256 ?? meta3.sha256;
|
|
49123
|
+
if (recordedBinaryHash && currentHash === recordedBinaryHash) {
|
|
49124
|
+
if (meta3.sha256 !== currentHash || meta3.binarySha256 !== currentHash) {
|
|
49125
|
+
writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.archiveSha256);
|
|
49126
|
+
}
|
|
49127
|
+
return true;
|
|
49128
|
+
}
|
|
49129
|
+
if (meta3.sha256 && !meta3.binarySha256 && !meta3.archiveSha256) {
|
|
49130
|
+
writeGithubInstalledMetaIn(packageDir, meta3.version, currentHash, meta3.sha256);
|
|
49131
|
+
return true;
|
|
49132
|
+
}
|
|
49133
|
+
if (!recordedBinaryHash) {
|
|
49134
|
+
quarantineCachedGithubInstall(spec, "missing binary sha256 metadata");
|
|
49135
|
+
return false;
|
|
49136
|
+
}
|
|
49137
|
+
if (currentHash !== recordedBinaryHash) {
|
|
49138
|
+
quarantineCachedGithubInstall(spec, `recorded ${recordedBinaryHash}, current ${currentHash}`);
|
|
48790
49139
|
return false;
|
|
48791
49140
|
}
|
|
48792
49141
|
return true;
|
|
@@ -48855,10 +49204,11 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
|
|
|
48855
49204
|
return null;
|
|
48856
49205
|
}
|
|
48857
49206
|
log2(`[lsp] ${spec.id} ${tag} sha256=${archiveSha256}`);
|
|
48858
|
-
const previousMeta =
|
|
48859
|
-
|
|
48860
|
-
|
|
48861
|
-
|
|
49207
|
+
const previousMeta = readGithubInstalledMetaIn(ghPackageDir(spec));
|
|
49208
|
+
const previousArchiveSha256 = previousMeta?.archiveSha256 ?? (previousMeta?.binarySha256 ? undefined : previousMeta?.sha256);
|
|
49209
|
+
if (previousMeta && previousMeta.version === tag && previousArchiveSha256) {
|
|
49210
|
+
if (previousArchiveSha256 !== archiveSha256) {
|
|
49211
|
+
error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch — refusing install. ` + `Previously installed archive sha256=${previousArchiveSha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding.`);
|
|
48862
49212
|
try {
|
|
48863
49213
|
unlinkSync6(archivePath);
|
|
48864
49214
|
} catch {}
|
|
@@ -48893,7 +49243,9 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
|
|
|
48893
49243
|
return null;
|
|
48894
49244
|
}
|
|
48895
49245
|
log2(`[lsp] installed ${spec.id} ${tag} at ${targetBinary}`);
|
|
48896
|
-
|
|
49246
|
+
const binarySha256 = await sha256OfFile(targetBinary);
|
|
49247
|
+
log2(`[lsp] ${spec.id} ${tag} binary_sha256=${binarySha256}`);
|
|
49248
|
+
return { archiveSha256, binarySha256 };
|
|
48897
49249
|
}
|
|
48898
49250
|
async function ensureGithubInstalled(spec, config2, fetchImpl2, platform, arch, signal) {
|
|
48899
49251
|
const outcome = await withInstallLock(spec.githubRepo, async () => {
|
|
@@ -48919,14 +49271,14 @@ async function ensureGithubInstalled(spec, config2, fetchImpl2, platform, arch,
|
|
|
48919
49271
|
log2(`[lsp] reinstalling ${spec.id}@${tag}: no installed-version metadata recorded`);
|
|
48920
49272
|
}
|
|
48921
49273
|
}
|
|
48922
|
-
const
|
|
49274
|
+
const hashes = await downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2, signal).catch((err) => {
|
|
48923
49275
|
error2(`[lsp] github install ${spec.id} crashed: ${err}`);
|
|
48924
49276
|
return null;
|
|
48925
49277
|
});
|
|
48926
|
-
if (!
|
|
49278
|
+
if (!hashes) {
|
|
48927
49279
|
return { started: true, reason: "install failed (see plugin log)" };
|
|
48928
49280
|
}
|
|
48929
|
-
|
|
49281
|
+
writeGithubInstalledMetaIn(ghPackageDir(spec), tag, hashes.binarySha256, hashes.archiveSha256);
|
|
48930
49282
|
return { started: true };
|
|
48931
49283
|
});
|
|
48932
49284
|
if (outcome === null) {
|
|
@@ -49056,7 +49408,7 @@ function discoverRelevantGithubServers(projectRoot) {
|
|
|
49056
49408
|
}
|
|
49057
49409
|
|
|
49058
49410
|
// src/notifications.ts
|
|
49059
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as
|
|
49411
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
49060
49412
|
import { join as join12 } from "node:path";
|
|
49061
49413
|
var WARNING_MARKER = "\uD83D\uDD27 AFT: ⚠️";
|
|
49062
49414
|
var FEATURE_MARKER = "\uD83D\uDD27 AFT: ✨";
|
|
@@ -49069,7 +49421,7 @@ function sendIgnoredMessage(client, sessionId, text) {
|
|
|
49069
49421
|
typedClient.ui.notify(text, "warning");
|
|
49070
49422
|
return true;
|
|
49071
49423
|
} catch (err) {
|
|
49072
|
-
|
|
49424
|
+
sessionLog(sessionId, `[aft-pi] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
49073
49425
|
return false;
|
|
49074
49426
|
}
|
|
49075
49427
|
}
|
|
@@ -49097,9 +49449,9 @@ function writeWarnedTools(storageDir, warned) {
|
|
|
49097
49449
|
mkdirSync8(storageDir, { recursive: true });
|
|
49098
49450
|
const warnedToolsPath = join12(storageDir, WARNED_TOOLS_FILE);
|
|
49099
49451
|
const tmpPath = join12(storageDir, `${WARNED_TOOLS_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
49100
|
-
|
|
49452
|
+
writeFileSync6(tmpPath, `${JSON.stringify(warned, null, 2)}
|
|
49101
49453
|
`);
|
|
49102
|
-
|
|
49454
|
+
renameSync6(tmpPath, warnedToolsPath);
|
|
49103
49455
|
} catch {}
|
|
49104
49456
|
}
|
|
49105
49457
|
async function withWarnedToolsLock(storageDir, fn) {
|
|
@@ -49111,7 +49463,7 @@ async function withWarnedToolsLock(storageDir, fn) {
|
|
|
49111
49463
|
try {
|
|
49112
49464
|
return await fn();
|
|
49113
49465
|
} finally {
|
|
49114
|
-
|
|
49466
|
+
rmSync5(lockDir, { recursive: true, force: true });
|
|
49115
49467
|
}
|
|
49116
49468
|
} catch (err) {
|
|
49117
49469
|
const code = err.code;
|
|
@@ -49198,7 +49550,7 @@ function sendFeatureAnnouncement(version2, features, storageDir) {
|
|
|
49198
49550
|
`));
|
|
49199
49551
|
try {
|
|
49200
49552
|
mkdirSync8(storageDir, { recursive: true });
|
|
49201
|
-
|
|
49553
|
+
writeFileSync6(versionFile, version2);
|
|
49202
49554
|
} catch {}
|
|
49203
49555
|
}
|
|
49204
49556
|
|
|
@@ -50099,12 +50451,10 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
50099
50451
|
parameters: DeleteParams,
|
|
50100
50452
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50101
50453
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
50102
|
-
const
|
|
50103
|
-
const response = await bridge.send("delete_file", {
|
|
50454
|
+
const response = await callBridge(bridge, "delete_file", {
|
|
50104
50455
|
files: params.files,
|
|
50105
|
-
recursive: params.recursive === true
|
|
50106
|
-
|
|
50107
|
-
});
|
|
50456
|
+
recursive: params.recursive === true
|
|
50457
|
+
}, extCtx);
|
|
50108
50458
|
const deletedEntries = response.deleted ?? [];
|
|
50109
50459
|
const skipped = response.skipped_files ?? [];
|
|
50110
50460
|
const deleted = deletedEntries.map((entry) => entry.file);
|
|
@@ -51736,7 +52086,7 @@ function buildWorkflowHints(opts) {
|
|
|
51736
52086
|
const hasNavigate = opts.toolSurface === "all" && !opts.absentTools.has("aft_navigate");
|
|
51737
52087
|
const hasBgBash = opts.bashBackgroundEnabled && !opts.absentTools.has(bashName) && !opts.absentTools.has("bash_status");
|
|
51738
52088
|
if (hasOutline && hasZoom) {
|
|
51739
|
-
sections.push(`**Web/URL access**: \`aft_outline({ url })\` first for structure, then \`aft_zoom({ url, symbol: "<heading>" })\` for the specific section.`);
|
|
52089
|
+
sections.push(`**Web/URL access**: \`aft_outline({ target: "<url>" })\` first for structure, then \`aft_zoom({ target: "<url>", symbol: "<heading>" })\` for the specific section.`);
|
|
51740
52090
|
}
|
|
51741
52091
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
51742
52092
|
const locator = hasGrep ? `\`${grepName}\`` : "`aft_search`";
|
|
@@ -51753,7 +52103,7 @@ function buildWorkflowHints(opts) {
|
|
|
51753
52103
|
`));
|
|
51754
52104
|
}
|
|
51755
52105
|
if (hasBgBash) {
|
|
51756
|
-
sections.push(`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns immediately with a \`
|
|
52106
|
+
sections.push(`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns immediately with a \`task_id\`. A completion reminder is delivered automatically — do not poll \`bash_status({ task_id })\`. Use \`bash_status\` only after the reminder arrives, or to inspect a task you already know is complete.`);
|
|
51757
52107
|
}
|
|
51758
52108
|
if (sections.length === 0) {
|
|
51759
52109
|
return null;
|
|
@@ -51802,6 +52152,42 @@ ${hintsBlock}` };
|
|
|
51802
52152
|
|
|
51803
52153
|
// src/index.ts
|
|
51804
52154
|
setActiveLogger(bridgeLogger);
|
|
52155
|
+
function createVersionMismatchHandler(getPool, ensureCompatibleBinary = ensureBinary) {
|
|
52156
|
+
const versionUpgradePromises = new Map;
|
|
52157
|
+
return async (binaryVersion, minVersion) => {
|
|
52158
|
+
const existing = versionUpgradePromises.get(minVersion);
|
|
52159
|
+
if (existing) {
|
|
52160
|
+
log2(`Version ${binaryVersion} < ${minVersion}; awaiting in-flight compatible binary upgrade`);
|
|
52161
|
+
return existing;
|
|
52162
|
+
}
|
|
52163
|
+
const upgradePromise = (async () => {
|
|
52164
|
+
warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
|
|
52165
|
+
try {
|
|
52166
|
+
const path2 = await ensureCompatibleBinary(`v${minVersion}`);
|
|
52167
|
+
if (!path2) {
|
|
52168
|
+
warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
|
|
52169
|
+
return null;
|
|
52170
|
+
}
|
|
52171
|
+
const pool = getPool();
|
|
52172
|
+
if (!pool) {
|
|
52173
|
+
warn2(`Found/downloaded compatible binary at ${path2}, but bridge pool is not ready.`);
|
|
52174
|
+
return null;
|
|
52175
|
+
}
|
|
52176
|
+
log2(`Found/downloaded compatible binary at ${path2}. Replacing running bridges...`);
|
|
52177
|
+
const replaced = await pool.replaceBinary(path2);
|
|
52178
|
+
log2("Binary replaced successfully. New bridges will use the updated binary.");
|
|
52179
|
+
return replaced;
|
|
52180
|
+
} catch (err) {
|
|
52181
|
+
error2(`Auto-download failed: ${err.message}. Install manually: cargo install agent-file-tools@${minVersion}`);
|
|
52182
|
+
return null;
|
|
52183
|
+
} finally {
|
|
52184
|
+
versionUpgradePromises.delete(minVersion);
|
|
52185
|
+
}
|
|
52186
|
+
})();
|
|
52187
|
+
versionUpgradePromises.set(minVersion, upgradePromise);
|
|
52188
|
+
return upgradePromise;
|
|
52189
|
+
};
|
|
52190
|
+
}
|
|
51805
52191
|
var PLUGIN_VERSION = (() => {
|
|
51806
52192
|
try {
|
|
51807
52193
|
const req = createRequire3(import.meta.url);
|
|
@@ -52043,9 +52429,11 @@ ${lines}
|
|
|
52043
52429
|
} catch (err) {
|
|
52044
52430
|
warn2(`[lsp] auto-install setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
52045
52431
|
}
|
|
52432
|
+
let pool;
|
|
52046
52433
|
const poolOptions = {
|
|
52047
52434
|
errorPrefix: "[aft-pi]",
|
|
52048
52435
|
minVersion: PLUGIN_VERSION,
|
|
52436
|
+
onVersionMismatch: createVersionMismatchHandler(() => pool),
|
|
52049
52437
|
onConfigureWarnings: async ({ projectRoot, sessionId, client, warnings }) => {
|
|
52050
52438
|
const pendingWarnings = sessionId ? drainPendingEagerWarnings(projectRoot) : [];
|
|
52051
52439
|
await handleConfigureWarningsForSession({
|
|
@@ -52074,7 +52462,7 @@ ${lines}
|
|
|
52074
52462
|
}, reminder);
|
|
52075
52463
|
}
|
|
52076
52464
|
};
|
|
52077
|
-
|
|
52465
|
+
pool = new BridgePool(binaryPath, poolOptions, configOverrides);
|
|
52078
52466
|
const ctx = { pool, config: config2, storageDir };
|
|
52079
52467
|
if (onnxRuntimePromise) {
|
|
52080
52468
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
@@ -52190,7 +52578,8 @@ ${lines}
|
|
|
52190
52578
|
var __test__2 = {
|
|
52191
52579
|
resolveToolSurface,
|
|
52192
52580
|
handleConfigureWarningsForSession,
|
|
52193
|
-
shouldPrepareOnnxRuntime
|
|
52581
|
+
shouldPrepareOnnxRuntime,
|
|
52582
|
+
createVersionMismatchHandler
|
|
52194
52583
|
};
|
|
52195
52584
|
export {
|
|
52196
52585
|
src_default as default,
|