@cortexkit/aft-pi 0.20.1 → 0.22.0
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/README.md +1 -1
- package/dist/bg-notifications.d.ts +0 -2
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +419 -172
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-github-install.d.ts +12 -1
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/tools/imports.d.ts +0 -1
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactor.d.ts +0 -1
- package/dist/tools/refactor.d.ts.map +1 -1
- package/dist/tools/structure.d.ts +0 -1
- package/dist/tools/structure.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -18761,7 +18761,7 @@ var require_fetch = __commonJS((exports, module) => {
|
|
|
18761
18761
|
request.cache = "no-store";
|
|
18762
18762
|
}
|
|
18763
18763
|
const newConnection = forceNewConnection ? "yes" : "no";
|
|
18764
|
-
if (request.mode === "websocket") {}
|
|
18764
|
+
if (request.mode === "websocket") {}
|
|
18765
18765
|
let requestBody = null;
|
|
18766
18766
|
if (request.body == null && fetchParams.processRequestEndOfBody) {
|
|
18767
18767
|
queueMicrotask(() => fetchParams.processRequestEndOfBody());
|
|
@@ -30476,17 +30476,24 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
30476
30476
|
// src/index.ts
|
|
30477
30477
|
import { createRequire as createRequire3 } from "node:module";
|
|
30478
30478
|
import { homedir as homedir9 } from "node:os";
|
|
30479
|
-
import { join as
|
|
30479
|
+
import { join as join13 } from "node:path";
|
|
30480
30480
|
|
|
30481
30481
|
// ../aft-bridge/dist/active-logger.js
|
|
30482
|
-
var active;
|
|
30482
|
+
var ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
|
|
30483
|
+
function loggerGlobal() {
|
|
30484
|
+
return globalThis;
|
|
30485
|
+
}
|
|
30483
30486
|
function setActiveLogger(logger) {
|
|
30484
|
-
|
|
30487
|
+
loggerGlobal()[ACTIVE_LOGGER_SYMBOL] = logger;
|
|
30488
|
+
}
|
|
30489
|
+
function getActiveLogger() {
|
|
30490
|
+
return loggerGlobal()[ACTIVE_LOGGER_SYMBOL];
|
|
30485
30491
|
}
|
|
30486
30492
|
function getLogFilePath() {
|
|
30487
|
-
return
|
|
30493
|
+
return getActiveLogger()?.getLogFilePath?.();
|
|
30488
30494
|
}
|
|
30489
30495
|
function log(message, meta) {
|
|
30496
|
+
const active = getActiveLogger();
|
|
30490
30497
|
if (active) {
|
|
30491
30498
|
active.log(message, meta);
|
|
30492
30499
|
} else {
|
|
@@ -30494,6 +30501,7 @@ function log(message, meta) {
|
|
|
30494
30501
|
}
|
|
30495
30502
|
}
|
|
30496
30503
|
function warn(message, meta) {
|
|
30504
|
+
const active = getActiveLogger();
|
|
30497
30505
|
if (active) {
|
|
30498
30506
|
active.warn(message, meta);
|
|
30499
30507
|
} else {
|
|
@@ -30501,6 +30509,7 @@ function warn(message, meta) {
|
|
|
30501
30509
|
}
|
|
30502
30510
|
}
|
|
30503
30511
|
function error(message, meta) {
|
|
30512
|
+
const active = getActiveLogger();
|
|
30504
30513
|
if (active) {
|
|
30505
30514
|
active.error(message, meta);
|
|
30506
30515
|
} else {
|
|
@@ -30520,6 +30529,7 @@ function sessionError(sessionId, message) {
|
|
|
30520
30529
|
import { spawn } from "node:child_process";
|
|
30521
30530
|
import { homedir } from "node:os";
|
|
30522
30531
|
import { join } from "node:path";
|
|
30532
|
+
import { StringDecoder } from "node:string_decoder";
|
|
30523
30533
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
30524
30534
|
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
30525
30535
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
@@ -30616,6 +30626,7 @@ class BinaryBridge {
|
|
|
30616
30626
|
configureWarningClients = new Map;
|
|
30617
30627
|
restartResetTimer = null;
|
|
30618
30628
|
errorPrefix;
|
|
30629
|
+
logger;
|
|
30619
30630
|
constructor(binaryPath, cwd, options, configOverrides) {
|
|
30620
30631
|
this.binaryPath = binaryPath;
|
|
30621
30632
|
this.cwd = cwd;
|
|
@@ -30628,6 +30639,28 @@ class BinaryBridge {
|
|
|
30628
30639
|
this.onBashCompletion = options?.onBashCompletion;
|
|
30629
30640
|
this.onBashLongRunning = options?.onBashLongRunning;
|
|
30630
30641
|
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
30642
|
+
this.logger = options?.logger;
|
|
30643
|
+
}
|
|
30644
|
+
logVia(message, meta) {
|
|
30645
|
+
const logger = this.logger ?? getActiveLogger();
|
|
30646
|
+
if (logger)
|
|
30647
|
+
logger.log(message, meta);
|
|
30648
|
+
else
|
|
30649
|
+
log(message, meta);
|
|
30650
|
+
}
|
|
30651
|
+
warnVia(message, meta) {
|
|
30652
|
+
const logger = this.logger ?? getActiveLogger();
|
|
30653
|
+
if (logger)
|
|
30654
|
+
logger.warn(message, meta);
|
|
30655
|
+
else
|
|
30656
|
+
warn(message, meta);
|
|
30657
|
+
}
|
|
30658
|
+
errorVia(message, meta) {
|
|
30659
|
+
const logger = this.logger ?? getActiveLogger();
|
|
30660
|
+
if (logger)
|
|
30661
|
+
logger.error(message, meta);
|
|
30662
|
+
else
|
|
30663
|
+
error(message, meta);
|
|
30631
30664
|
}
|
|
30632
30665
|
get restartCount() {
|
|
30633
30666
|
return this._restartCount;
|
|
@@ -30736,8 +30769,8 @@ class BinaryBridge {
|
|
|
30736
30769
|
return;
|
|
30737
30770
|
if (configResult.warnings.length === 0)
|
|
30738
30771
|
return;
|
|
30772
|
+
const sessionId = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
30739
30773
|
try {
|
|
30740
|
-
const sessionId = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
30741
30774
|
await this.onConfigureWarnings({
|
|
30742
30775
|
projectRoot: this.cwd,
|
|
30743
30776
|
sessionId,
|
|
@@ -30746,6 +30779,10 @@ class BinaryBridge {
|
|
|
30746
30779
|
});
|
|
30747
30780
|
} catch (err) {
|
|
30748
30781
|
warn(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
30782
|
+
} finally {
|
|
30783
|
+
if (sessionId) {
|
|
30784
|
+
this.configureWarningClients.delete(sessionId);
|
|
30785
|
+
}
|
|
30749
30786
|
}
|
|
30750
30787
|
}
|
|
30751
30788
|
async handleConfigureWarningsFrame(frame) {
|
|
@@ -30757,16 +30794,23 @@ class BinaryBridge {
|
|
|
30757
30794
|
const projectRoot = typeof frame.project_root === "string" ? frame.project_root : this.cwd;
|
|
30758
30795
|
const rawSessionId = frame.session_id;
|
|
30759
30796
|
const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : null;
|
|
30760
|
-
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
|
|
30765
|
-
|
|
30797
|
+
try {
|
|
30798
|
+
await this.onConfigureWarnings({
|
|
30799
|
+
projectRoot,
|
|
30800
|
+
sessionId,
|
|
30801
|
+
client: sessionId ? this.configureWarningClients.get(sessionId) : undefined,
|
|
30802
|
+
warnings
|
|
30803
|
+
});
|
|
30804
|
+
} finally {
|
|
30805
|
+
if (sessionId) {
|
|
30806
|
+
this.configureWarningClients.delete(sessionId);
|
|
30807
|
+
}
|
|
30808
|
+
}
|
|
30766
30809
|
}
|
|
30767
30810
|
async shutdown() {
|
|
30768
30811
|
this._shuttingDown = true;
|
|
30769
30812
|
this.clearRestartResetTimer();
|
|
30813
|
+
this.configureWarningClients.clear();
|
|
30770
30814
|
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge shutting down`));
|
|
30771
30815
|
if (this.process) {
|
|
30772
30816
|
const proc = this.process;
|
|
@@ -30790,10 +30834,12 @@ class BinaryBridge {
|
|
|
30790
30834
|
return;
|
|
30791
30835
|
try {
|
|
30792
30836
|
const resp = await this.send("version");
|
|
30837
|
+
if (resp.success === false) {
|
|
30838
|
+
throw new Error(`Binary version check failed: ${String(resp.code ?? "unknown")} — likely too old`);
|
|
30839
|
+
}
|
|
30793
30840
|
const binaryVersion = resp.version;
|
|
30794
|
-
if (
|
|
30795
|
-
|
|
30796
|
-
return;
|
|
30841
|
+
if (typeof binaryVersion !== "string") {
|
|
30842
|
+
throw new Error(`Binary did not report a version — likely too old (minVersion: ${this.minVersion})`);
|
|
30797
30843
|
}
|
|
30798
30844
|
log(`Binary version: ${binaryVersion}`);
|
|
30799
30845
|
if (compareSemver(binaryVersion, this.minVersion) < 0) {
|
|
@@ -30802,6 +30848,7 @@ class BinaryBridge {
|
|
|
30802
30848
|
}
|
|
30803
30849
|
} catch (err) {
|
|
30804
30850
|
warn(`Version check failed: ${err.message}`);
|
|
30851
|
+
throw err;
|
|
30805
30852
|
}
|
|
30806
30853
|
}
|
|
30807
30854
|
ensureSpawned(triggeringSessionId) {
|
|
@@ -30843,19 +30890,23 @@ class BinaryBridge {
|
|
|
30843
30890
|
env
|
|
30844
30891
|
});
|
|
30845
30892
|
const currentChild = child;
|
|
30893
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
30846
30894
|
child.stdout?.on("data", (chunk) => {
|
|
30847
|
-
this.onStdoutData(
|
|
30895
|
+
this.onStdoutData(stdoutDecoder.write(chunk));
|
|
30848
30896
|
});
|
|
30897
|
+
child.stdout?.on("end", () => {
|
|
30898
|
+
const remaining = stdoutDecoder.end();
|
|
30899
|
+
if (remaining)
|
|
30900
|
+
this.onStdoutData(remaining);
|
|
30901
|
+
});
|
|
30902
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
30849
30903
|
child.stderr?.on("data", (chunk) => {
|
|
30850
|
-
|
|
30851
|
-
|
|
30852
|
-
|
|
30853
|
-
|
|
30854
|
-
|
|
30855
|
-
|
|
30856
|
-
log(tagged);
|
|
30857
|
-
this.pushStderrLine(tagged);
|
|
30858
|
-
}
|
|
30904
|
+
this.onStderrData(stderrDecoder.write(chunk));
|
|
30905
|
+
});
|
|
30906
|
+
child.stderr?.on("end", () => {
|
|
30907
|
+
const remaining = stderrDecoder.end();
|
|
30908
|
+
if (remaining)
|
|
30909
|
+
this.onStderrData(remaining);
|
|
30859
30910
|
});
|
|
30860
30911
|
child.on("error", (err) => {
|
|
30861
30912
|
if (this.process !== currentChild)
|
|
@@ -30888,6 +30939,17 @@ class BinaryBridge {
|
|
|
30888
30939
|
this.stderrTail.shift();
|
|
30889
30940
|
}
|
|
30890
30941
|
}
|
|
30942
|
+
onStderrData(data) {
|
|
30943
|
+
const lines = data.trimEnd().split(`
|
|
30944
|
+
`);
|
|
30945
|
+
for (const line of lines) {
|
|
30946
|
+
if (!line)
|
|
30947
|
+
continue;
|
|
30948
|
+
const tagged = tagStderrLine(line);
|
|
30949
|
+
log(tagged);
|
|
30950
|
+
this.pushStderrLine(tagged);
|
|
30951
|
+
}
|
|
30952
|
+
}
|
|
30891
30953
|
formatStderrTail() {
|
|
30892
30954
|
if (this.stderrTail.length === 0)
|
|
30893
30955
|
return "";
|
|
@@ -30967,6 +31029,7 @@ class BinaryBridge {
|
|
|
30967
31029
|
}
|
|
30968
31030
|
}
|
|
30969
31031
|
handleTimeout(triggeringSessionId) {
|
|
31032
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
30970
31033
|
if (this.process) {
|
|
30971
31034
|
this.process.kill("SIGKILL");
|
|
30972
31035
|
this.process = null;
|
|
@@ -31292,23 +31355,23 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
31292
31355
|
const onnxBaseDir = join3(storageDir, "onnxruntime");
|
|
31293
31356
|
mkdirSync2(onnxBaseDir, { recursive: true });
|
|
31294
31357
|
const lockPath = join3(onnxBaseDir, ONNX_LOCK_FILE);
|
|
31295
|
-
|
|
31358
|
+
cleanupAbandonedStagingDirs(onnxBaseDir);
|
|
31296
31359
|
if (!acquireLock(lockPath)) {
|
|
31297
31360
|
warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
|
|
31298
31361
|
return null;
|
|
31299
31362
|
}
|
|
31300
31363
|
try {
|
|
31364
|
+
cleanupIncompleteTargetIfUnowned(ortDir);
|
|
31301
31365
|
return await downloadOnnxRuntime(info, ortDir);
|
|
31302
31366
|
} finally {
|
|
31303
31367
|
releaseLock(lockPath);
|
|
31304
31368
|
}
|
|
31305
31369
|
}
|
|
31306
|
-
function
|
|
31370
|
+
function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
31307
31371
|
try {
|
|
31308
31372
|
const entries = readdirSync(onnxBaseDir);
|
|
31309
|
-
const ortDirBaseName = ortDir.slice(onnxBaseDir.length + 1);
|
|
31310
31373
|
for (const entry of entries) {
|
|
31311
|
-
if (!entry.startsWith(`${
|
|
31374
|
+
if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
|
|
31312
31375
|
continue;
|
|
31313
31376
|
const stagingDir = join3(onnxBaseDir, entry);
|
|
31314
31377
|
const parts = entry.split(".");
|
|
@@ -31339,6 +31402,8 @@ function cleanupAbandonedOnnxAttempts(onnxBaseDir, ortDir) {
|
|
|
31339
31402
|
}
|
|
31340
31403
|
}
|
|
31341
31404
|
} catch {}
|
|
31405
|
+
}
|
|
31406
|
+
function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
31342
31407
|
try {
|
|
31343
31408
|
if (existsSync2(ortDir) && !existsSync2(join3(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
31344
31409
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
@@ -31529,25 +31594,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
31529
31594
|
realFiles.push(libFile);
|
|
31530
31595
|
}
|
|
31531
31596
|
}
|
|
31532
|
-
|
|
31533
|
-
const src = join3(extractedDir, libFile);
|
|
31534
|
-
const dst = join3(targetDir, libFile);
|
|
31535
|
-
try {
|
|
31536
|
-
copyFileSync(src, dst);
|
|
31537
|
-
if (process.platform !== "win32") {
|
|
31538
|
-
chmodSync2(dst, 493);
|
|
31539
|
-
}
|
|
31540
|
-
} catch (copyErr) {
|
|
31541
|
-
log(`ORT extract: failed to copy ${libFile}: ${copyErr}`);
|
|
31542
|
-
}
|
|
31543
|
-
}
|
|
31544
|
-
for (const link of symlinks) {
|
|
31545
|
-
const dst = join3(targetDir, link.name);
|
|
31546
|
-
try {
|
|
31547
|
-
unlinkSync2(dst);
|
|
31548
|
-
} catch {}
|
|
31549
|
-
symlinkSync(link.target, dst);
|
|
31550
|
-
}
|
|
31597
|
+
copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
|
|
31551
31598
|
const libPath = join3(targetDir, info.libName);
|
|
31552
31599
|
let libHash = null;
|
|
31553
31600
|
try {
|
|
@@ -31570,6 +31617,45 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
31570
31617
|
return null;
|
|
31571
31618
|
}
|
|
31572
31619
|
}
|
|
31620
|
+
function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync) {
|
|
31621
|
+
const requiredLibs = new Set([info.libName]);
|
|
31622
|
+
for (const libFile of realFiles) {
|
|
31623
|
+
const src = join3(extractedDir, libFile);
|
|
31624
|
+
const dst = join3(targetDir, libFile);
|
|
31625
|
+
try {
|
|
31626
|
+
copyFile(src, dst);
|
|
31627
|
+
if (process.platform !== "win32") {
|
|
31628
|
+
chmodSync2(dst, 493);
|
|
31629
|
+
}
|
|
31630
|
+
} catch (copyErr) {
|
|
31631
|
+
if (requiredLibs.has(libFile)) {
|
|
31632
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
31633
|
+
throw copyErr;
|
|
31634
|
+
}
|
|
31635
|
+
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
31636
|
+
}
|
|
31637
|
+
}
|
|
31638
|
+
for (const link of symlinks) {
|
|
31639
|
+
const dst = join3(targetDir, link.name);
|
|
31640
|
+
try {
|
|
31641
|
+
unlinkSync2(dst);
|
|
31642
|
+
} catch {}
|
|
31643
|
+
try {
|
|
31644
|
+
symlinkSync(link.target, dst);
|
|
31645
|
+
} catch (symlinkErr) {
|
|
31646
|
+
if (requiredLibs.has(link.name)) {
|
|
31647
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
31648
|
+
throw symlinkErr;
|
|
31649
|
+
}
|
|
31650
|
+
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
31651
|
+
}
|
|
31652
|
+
}
|
|
31653
|
+
const requiredPath = join3(targetDir, info.libName);
|
|
31654
|
+
if (!existsSync2(requiredPath)) {
|
|
31655
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
31656
|
+
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
31657
|
+
}
|
|
31658
|
+
}
|
|
31573
31659
|
async function extractZipArchive(archivePath, destinationDir) {
|
|
31574
31660
|
if (process.platform === "win32") {
|
|
31575
31661
|
execFileSync("tar.exe", ["-xf", archivePath, "-C", destinationDir], {
|
|
@@ -31753,11 +31839,13 @@ class BridgePool {
|
|
|
31753
31839
|
idleTimeoutMs;
|
|
31754
31840
|
bridgeOptions;
|
|
31755
31841
|
configOverrides;
|
|
31842
|
+
logger;
|
|
31756
31843
|
cleanupTimer = null;
|
|
31757
31844
|
constructor(binaryPath, options = {}, configOverrides = {}) {
|
|
31758
31845
|
this.binaryPath = binaryPath;
|
|
31759
31846
|
this.maxPoolSize = options.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE;
|
|
31760
31847
|
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
31848
|
+
this.logger = options.logger;
|
|
31761
31849
|
this.bridgeOptions = {
|
|
31762
31850
|
timeoutMs: options.timeoutMs,
|
|
31763
31851
|
maxRestarts: options.maxRestarts,
|
|
@@ -31801,8 +31889,10 @@ class BridgePool {
|
|
|
31801
31889
|
cleanup() {
|
|
31802
31890
|
const now = Date.now();
|
|
31803
31891
|
for (const [dir, entry] of this.bridges) {
|
|
31892
|
+
if (entry.bridge.hasPendingRequests())
|
|
31893
|
+
continue;
|
|
31804
31894
|
if (now - entry.lastUsed > this.idleTimeoutMs) {
|
|
31805
|
-
entry.bridge.shutdown().catch((err) => error("cleanup shutdown failed:", err));
|
|
31895
|
+
entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
|
|
31806
31896
|
this.bridges.delete(dir);
|
|
31807
31897
|
}
|
|
31808
31898
|
}
|
|
@@ -31811,6 +31901,8 @@ class BridgePool {
|
|
|
31811
31901
|
let oldestDir = null;
|
|
31812
31902
|
let oldestTime = Infinity;
|
|
31813
31903
|
for (const [dir, entry] of this.bridges) {
|
|
31904
|
+
if (entry.bridge.hasPendingRequests())
|
|
31905
|
+
continue;
|
|
31814
31906
|
if (entry.lastUsed < oldestTime) {
|
|
31815
31907
|
oldestTime = entry.lastUsed;
|
|
31816
31908
|
oldestDir = dir;
|
|
@@ -31818,7 +31910,7 @@ class BridgePool {
|
|
|
31818
31910
|
}
|
|
31819
31911
|
if (oldestDir) {
|
|
31820
31912
|
const entry = this.bridges.get(oldestDir);
|
|
31821
|
-
entry?.bridge.shutdown().catch((err) => error("eviction shutdown failed:", err));
|
|
31913
|
+
entry?.bridge.shutdown().catch((err) => this.error("eviction shutdown failed:", err));
|
|
31822
31914
|
this.bridges.delete(oldestDir);
|
|
31823
31915
|
}
|
|
31824
31916
|
}
|
|
@@ -31836,7 +31928,21 @@ class BridgePool {
|
|
|
31836
31928
|
const shutdowns = Array.from(this.bridges.values()).map((entry) => entry.bridge.shutdown());
|
|
31837
31929
|
this.bridges.clear();
|
|
31838
31930
|
await Promise.allSettled(shutdowns);
|
|
31839
|
-
log(`Binary path updated to ${newPath}. All bridges cleared — next calls will use the new binary.`);
|
|
31931
|
+
this.log(`Binary path updated to ${newPath}. All bridges cleared — next calls will use the new binary.`);
|
|
31932
|
+
}
|
|
31933
|
+
log(message, meta) {
|
|
31934
|
+
const logger = this.logger ?? getActiveLogger();
|
|
31935
|
+
if (logger)
|
|
31936
|
+
logger.log(message, meta);
|
|
31937
|
+
else
|
|
31938
|
+
log(message, meta);
|
|
31939
|
+
}
|
|
31940
|
+
error(message, meta) {
|
|
31941
|
+
const logger = this.logger ?? getActiveLogger();
|
|
31942
|
+
if (logger)
|
|
31943
|
+
logger.error(message, meta);
|
|
31944
|
+
else
|
|
31945
|
+
error(message, meta);
|
|
31840
31946
|
}
|
|
31841
31947
|
setConfigureOverride(key, value) {
|
|
31842
31948
|
if (value === undefined) {
|
|
@@ -31956,7 +32062,7 @@ async function findBinary(expectedVersion) {
|
|
|
31956
32062
|
return syncResult;
|
|
31957
32063
|
}
|
|
31958
32064
|
log("Binary not found locally, attempting auto-download...");
|
|
31959
|
-
const downloaded = await ensureBinary();
|
|
32065
|
+
const downloaded = await ensureBinary(expectedVersion);
|
|
31960
32066
|
if (downloaded)
|
|
31961
32067
|
return downloaded;
|
|
31962
32068
|
throw new Error([
|
|
@@ -32616,6 +32722,13 @@ async function appendToolResultBgCompletions(drainContext, content) {
|
|
|
32616
32722
|
const reminder = formatCombinedSystemReminder(state.pendingCompletions, state.pendingLongRunning);
|
|
32617
32723
|
state.pendingCompletions = [];
|
|
32618
32724
|
state.pendingLongRunning = [];
|
|
32725
|
+
if (state.debounceTimer) {
|
|
32726
|
+
clearTimeout(state.debounceTimer);
|
|
32727
|
+
state.debounceTimer = null;
|
|
32728
|
+
state.firstCompletionAt = null;
|
|
32729
|
+
state.scheduledFireAt = null;
|
|
32730
|
+
state.scheduledCompletionCount = 0;
|
|
32731
|
+
}
|
|
32619
32732
|
return [...content, { type: "text", text: reminder }];
|
|
32620
32733
|
}
|
|
32621
32734
|
async function handleTurnEndBgCompletions(drainContext) {
|
|
@@ -32623,8 +32736,6 @@ async function handleTurnEndBgCompletions(drainContext) {
|
|
|
32623
32736
|
}
|
|
32624
32737
|
async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
32625
32738
|
const state = stateFor(drainContext.sessionID);
|
|
32626
|
-
if (state.wakeFiredThisIdle)
|
|
32627
|
-
return;
|
|
32628
32739
|
if (drainContext.isActive?.())
|
|
32629
32740
|
return;
|
|
32630
32741
|
if (!skipDrain && state.outstandingTaskIds.size > 0) {
|
|
@@ -32638,9 +32749,6 @@ async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
|
32638
32749
|
sessionWarn2(drainContext.sessionID ?? "", `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
32639
32750
|
});
|
|
32640
32751
|
}
|
|
32641
|
-
function resetBgWake(sessionID) {
|
|
32642
|
-
stateFor(sessionID).wakeFiredThisIdle = false;
|
|
32643
|
-
}
|
|
32644
32752
|
function formatSystemReminder(completions) {
|
|
32645
32753
|
const bullets = completions.map((completion) => formatCompletion(completion)).join(`
|
|
32646
32754
|
`);
|
|
@@ -32705,16 +32813,17 @@ function scheduleWake(state, sendWake, onSendFailure) {
|
|
|
32705
32813
|
state.debounceTimer = setTimeout(() => {
|
|
32706
32814
|
const pending = state.pendingCompletions;
|
|
32707
32815
|
const pendingLongRunning = state.pendingLongRunning;
|
|
32708
|
-
const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
|
|
32709
|
-
state.pendingCompletions = [];
|
|
32710
|
-
state.pendingLongRunning = [];
|
|
32711
32816
|
state.debounceTimer = null;
|
|
32712
32817
|
state.firstCompletionAt = null;
|
|
32713
32818
|
state.scheduledFireAt = null;
|
|
32714
32819
|
state.scheduledCompletionCount = 0;
|
|
32820
|
+
if (pending.length === 0 && pendingLongRunning.length === 0)
|
|
32821
|
+
return;
|
|
32822
|
+
const reminder = formatCombinedSystemReminder(pending, pendingLongRunning);
|
|
32823
|
+
state.pendingCompletions = [];
|
|
32824
|
+
state.pendingLongRunning = [];
|
|
32715
32825
|
sendWake(reminder).then(() => {
|
|
32716
32826
|
state.retryDelayMs = null;
|
|
32717
|
-
state.wakeFiredThisIdle = true;
|
|
32718
32827
|
}).catch((err) => {
|
|
32719
32828
|
state.pendingCompletions = [...pending, ...state.pendingCompletions];
|
|
32720
32829
|
state.pendingLongRunning = [...pendingLongRunning, ...state.pendingLongRunning];
|
|
@@ -32736,7 +32845,6 @@ function stateFor(sessionID) {
|
|
|
32736
32845
|
pendingCompletions: [],
|
|
32737
32846
|
pendingLongRunning: [],
|
|
32738
32847
|
debounceTimer: null,
|
|
32739
|
-
wakeFiredThisIdle: false,
|
|
32740
32848
|
firstCompletionAt: null,
|
|
32741
32849
|
scheduledFireAt: null,
|
|
32742
32850
|
scheduledCompletionCount: 0,
|
|
@@ -43989,7 +44097,7 @@ function finalize(ctx, schema) {
|
|
|
43989
44097
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
43990
44098
|
} else if (ctx.target === "draft-04") {
|
|
43991
44099
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
43992
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
44100
|
+
} else if (ctx.target === "openapi-3.0") {}
|
|
43993
44101
|
if (ctx.external?.uri) {
|
|
43994
44102
|
const id = ctx.external.registry.get(schema)?.id;
|
|
43995
44103
|
if (!id)
|
|
@@ -44237,7 +44345,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
44237
44345
|
if (val === undefined) {
|
|
44238
44346
|
if (ctx.unrepresentable === "throw") {
|
|
44239
44347
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
44240
|
-
}
|
|
44348
|
+
}
|
|
44241
44349
|
} else if (typeof val === "bigint") {
|
|
44242
44350
|
if (ctx.unrepresentable === "throw") {
|
|
44243
44351
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -47037,7 +47145,8 @@ function loadAftConfig(projectDirectory) {
|
|
|
47037
47145
|
// src/lsp-auto-install.ts
|
|
47038
47146
|
import { spawn as spawn2 } from "node:child_process";
|
|
47039
47147
|
import { createHash as createHash3 } from "node:crypto";
|
|
47040
|
-
import { createReadStream, statSync as statSync3 } from "node:fs";
|
|
47148
|
+
import { createReadStream, mkdirSync as mkdirSync6, readFileSync as readFileSync5, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync3 } from "node:fs";
|
|
47149
|
+
import { join as join10 } from "node:path";
|
|
47041
47150
|
|
|
47042
47151
|
// src/lsp-cache.ts
|
|
47043
47152
|
import {
|
|
@@ -47721,6 +47830,50 @@ function hashInstalledBinary(spec) {
|
|
|
47721
47830
|
stream.on("end", () => resolve2(hash2.digest("hex")));
|
|
47722
47831
|
});
|
|
47723
47832
|
}
|
|
47833
|
+
function installedBinaryPath(spec) {
|
|
47834
|
+
const candidates = process.platform === "win32" ? [
|
|
47835
|
+
lspBinaryPath(spec.npm, spec.binary),
|
|
47836
|
+
lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
|
|
47837
|
+
lspBinaryPath(spec.npm, `${spec.binary}.exe`),
|
|
47838
|
+
lspBinaryPath(spec.npm, `${spec.binary}.bat`)
|
|
47839
|
+
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
47840
|
+
for (const candidate of candidates) {
|
|
47841
|
+
try {
|
|
47842
|
+
if (statSync3(candidate).isFile())
|
|
47843
|
+
return candidate;
|
|
47844
|
+
} catch {}
|
|
47845
|
+
}
|
|
47846
|
+
return null;
|
|
47847
|
+
}
|
|
47848
|
+
function sha256OfFileSync(path2) {
|
|
47849
|
+
return createHash3("sha256").update(readFileSync5(path2)).digest("hex");
|
|
47850
|
+
}
|
|
47851
|
+
function quarantineCachedNpmInstall(spec, reason) {
|
|
47852
|
+
const packageDir = lspPackageDir(spec.npm);
|
|
47853
|
+
const dest = join10(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
|
|
47854
|
+
warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
47855
|
+
try {
|
|
47856
|
+
mkdirSync6(join10(dest, ".."), { recursive: true });
|
|
47857
|
+
rmSync2(dest, { recursive: true, force: true });
|
|
47858
|
+
renameSync3(packageDir, dest);
|
|
47859
|
+
} catch (err) {
|
|
47860
|
+
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
47861
|
+
}
|
|
47862
|
+
}
|
|
47863
|
+
function validateCachedNpmInstall(spec) {
|
|
47864
|
+
const meta3 = readInstalledMeta(spec.npm);
|
|
47865
|
+
const binaryPath = installedBinaryPath(spec);
|
|
47866
|
+
if (!meta3?.sha256 || !meta3.version || !isSafeVersion(meta3.version) || !binaryPath) {
|
|
47867
|
+
quarantineCachedNpmInstall(spec, "missing/unsafe metadata or binary");
|
|
47868
|
+
return false;
|
|
47869
|
+
}
|
|
47870
|
+
const currentHash = sha256OfFileSync(binaryPath);
|
|
47871
|
+
if (currentHash !== meta3.sha256) {
|
|
47872
|
+
quarantineCachedNpmInstall(spec, `recorded ${meta3.sha256}, current ${currentHash}`);
|
|
47873
|
+
return false;
|
|
47874
|
+
}
|
|
47875
|
+
return true;
|
|
47876
|
+
}
|
|
47724
47877
|
function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
|
|
47725
47878
|
const cachedBinDirs = [];
|
|
47726
47879
|
const skipped = [];
|
|
@@ -47733,7 +47886,7 @@ function runAutoInstall(projectRoot, config2, fetchImpl2 = fetch) {
|
|
|
47733
47886
|
return projectExtensions;
|
|
47734
47887
|
};
|
|
47735
47888
|
for (const spec of NPM_LSP_TABLE) {
|
|
47736
|
-
if (isInstalled(spec.npm, spec.binary)) {
|
|
47889
|
+
if (isInstalled(spec.npm, spec.binary) && validateCachedNpmInstall(spec)) {
|
|
47737
47890
|
cachedBinDirs.push(lspBinDir(spec.npm));
|
|
47738
47891
|
}
|
|
47739
47892
|
if (config2.disabled.has(spec.id)) {
|
|
@@ -47785,16 +47938,17 @@ import {
|
|
|
47785
47938
|
createWriteStream as createWriteStream2,
|
|
47786
47939
|
existsSync as existsSync7,
|
|
47787
47940
|
lstatSync as lstatSync2,
|
|
47788
|
-
mkdirSync as
|
|
47941
|
+
mkdirSync as mkdirSync7,
|
|
47789
47942
|
readdirSync as readdirSync4,
|
|
47943
|
+
readFileSync as readFileSync6,
|
|
47790
47944
|
readlinkSync as readlinkSync2,
|
|
47791
47945
|
realpathSync as realpathSync3,
|
|
47792
|
-
renameSync as
|
|
47793
|
-
rmSync as
|
|
47946
|
+
renameSync as renameSync4,
|
|
47947
|
+
rmSync as rmSync3,
|
|
47794
47948
|
statSync as statSync4,
|
|
47795
47949
|
unlinkSync as unlinkSync6
|
|
47796
47950
|
} from "node:fs";
|
|
47797
|
-
import { dirname as dirname2, join as
|
|
47951
|
+
import { dirname as dirname2, join as join11, relative as relative2, resolve as resolve2 } from "node:path";
|
|
47798
47952
|
import { Readable as Readable2 } from "node:stream";
|
|
47799
47953
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
47800
47954
|
|
|
@@ -47884,25 +48038,25 @@ function detectHostPlatform() {
|
|
|
47884
48038
|
|
|
47885
48039
|
// src/lsp-github-install.ts
|
|
47886
48040
|
function ghCacheRoot() {
|
|
47887
|
-
return
|
|
48041
|
+
return join11(aftCacheBase(), "lsp-binaries");
|
|
47888
48042
|
}
|
|
47889
48043
|
function ghPackageDir(spec) {
|
|
47890
|
-
return
|
|
48044
|
+
return join11(ghCacheRoot(), spec.id);
|
|
47891
48045
|
}
|
|
47892
48046
|
function ghBinDir(spec) {
|
|
47893
|
-
return
|
|
48047
|
+
return join11(ghPackageDir(spec), "bin");
|
|
47894
48048
|
}
|
|
47895
48049
|
function ghExtractDir(spec) {
|
|
47896
|
-
return
|
|
48050
|
+
return join11(ghPackageDir(spec), "extracted");
|
|
47897
48051
|
}
|
|
47898
48052
|
function ghBinaryPath(spec, platform) {
|
|
47899
48053
|
const ext = platform === "win32" ? ".exe" : "";
|
|
47900
|
-
return
|
|
48054
|
+
return join11(ghBinDir(spec), `${spec.binary}${ext}`);
|
|
47901
48055
|
}
|
|
47902
48056
|
function isGithubInstalled(spec, platform) {
|
|
47903
48057
|
for (const candidate of ghBinaryCandidates(spec, platform)) {
|
|
47904
48058
|
try {
|
|
47905
|
-
if (statSync4(
|
|
48059
|
+
if (statSync4(join11(ghBinDir(spec), candidate)).isFile())
|
|
47906
48060
|
return true;
|
|
47907
48061
|
} catch {}
|
|
47908
48062
|
}
|
|
@@ -47924,6 +48078,9 @@ function sha256OfFile(path2) {
|
|
|
47924
48078
|
stream.on("end", () => resolve3(hash2.digest("hex")));
|
|
47925
48079
|
});
|
|
47926
48080
|
}
|
|
48081
|
+
function sha256OfFileSync2(path2) {
|
|
48082
|
+
return createHash4("sha256").update(readFileSync6(path2)).digest("hex");
|
|
48083
|
+
}
|
|
47927
48084
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
|
|
47928
48085
|
const candidates = [];
|
|
47929
48086
|
candidates.push(tag);
|
|
@@ -47944,11 +48101,7 @@ async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
|
|
|
47944
48101
|
const url2 = `https://api.github.com/repos/${githubRepo}/releases/tags/${encodeURIComponent(candidate)}`;
|
|
47945
48102
|
const timeout = controlledTimeoutSignal(15000, signal);
|
|
47946
48103
|
try {
|
|
47947
|
-
const res = await
|
|
47948
|
-
headers,
|
|
47949
|
-
redirect: "follow",
|
|
47950
|
-
signal: timeout.signal
|
|
47951
|
-
});
|
|
48104
|
+
const res = await fetchJsonFollowingRedirects(url2, headers, fetchImpl2, timeout.signal);
|
|
47952
48105
|
if (res.status === 404)
|
|
47953
48106
|
continue;
|
|
47954
48107
|
if (!res.ok) {
|
|
@@ -47978,6 +48131,23 @@ async function fetchReleaseByTag(githubRepo, tag, fetchImpl2, signal) {
|
|
|
47978
48131
|
}
|
|
47979
48132
|
return null;
|
|
47980
48133
|
}
|
|
48134
|
+
async function fetchJsonFollowingRedirects(url2, headers, fetchImpl2, signal) {
|
|
48135
|
+
const maxRedirects = 5;
|
|
48136
|
+
let currentUrl = url2;
|
|
48137
|
+
for (let i = 0;i <= maxRedirects; i += 1) {
|
|
48138
|
+
assertAllowedDownloadUrl(currentUrl);
|
|
48139
|
+
const res = await fetchImpl2(currentUrl, { headers, redirect: "manual", signal });
|
|
48140
|
+
if (res.status >= 300 && res.status < 400) {
|
|
48141
|
+
const location = res.headers.get("location");
|
|
48142
|
+
if (!location)
|
|
48143
|
+
throw new Error(`redirect status ${res.status} without Location`);
|
|
48144
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
48145
|
+
continue;
|
|
48146
|
+
}
|
|
48147
|
+
return res;
|
|
48148
|
+
}
|
|
48149
|
+
throw new Error(`too many redirects (>${maxRedirects})`);
|
|
48150
|
+
}
|
|
47981
48151
|
async function resolveTargetTag(spec, config2, fetchImpl2, signal) {
|
|
47982
48152
|
const pinned = config2.versions[spec.githubRepo];
|
|
47983
48153
|
if (pinned) {
|
|
@@ -48072,17 +48242,12 @@ function assertAllowedDownloadUrl(rawUrl) {
|
|
|
48072
48242
|
return parsed;
|
|
48073
48243
|
}
|
|
48074
48244
|
async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
48075
|
-
assertAllowedDownloadUrl(url2);
|
|
48076
48245
|
if (assetSize !== undefined && assetSize > MAX_DOWNLOAD_BYTES2) {
|
|
48077
48246
|
throw new Error(`asset size ${assetSize} exceeds max ${MAX_DOWNLOAD_BYTES2} (set lsp.versions to pin a smaller release if this is wrong)`);
|
|
48078
48247
|
}
|
|
48079
48248
|
const timeout = controlledTimeoutSignal(120000, signal);
|
|
48080
48249
|
try {
|
|
48081
|
-
const res = await
|
|
48082
|
-
headers: { accept: "application/octet-stream" },
|
|
48083
|
-
redirect: "follow",
|
|
48084
|
-
signal: timeout.signal
|
|
48085
|
-
});
|
|
48250
|
+
const res = await fetchFollowingRedirects(url2, fetchImpl2, timeout.signal);
|
|
48086
48251
|
if (!res.ok || !res.body) {
|
|
48087
48252
|
throw new Error(`download failed (${res.status})`);
|
|
48088
48253
|
}
|
|
@@ -48090,7 +48255,7 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
|
48090
48255
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
48091
48256
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
48092
48257
|
}
|
|
48093
|
-
|
|
48258
|
+
mkdirSync7(dirname2(destPath), { recursive: true });
|
|
48094
48259
|
let bytesWritten = 0;
|
|
48095
48260
|
const guard = new TransformStream({
|
|
48096
48261
|
transform(chunk, controller) {
|
|
@@ -48114,6 +48279,27 @@ async function downloadFile(url2, destPath, fetchImpl2, assetSize, signal) {
|
|
|
48114
48279
|
timeout.cleanup();
|
|
48115
48280
|
}
|
|
48116
48281
|
}
|
|
48282
|
+
async function fetchFollowingRedirects(url2, fetchImpl2, signal) {
|
|
48283
|
+
const maxRedirects = 5;
|
|
48284
|
+
let currentUrl = url2;
|
|
48285
|
+
for (let i = 0;i <= maxRedirects; i += 1) {
|
|
48286
|
+
assertAllowedDownloadUrl(currentUrl);
|
|
48287
|
+
const res = await fetchImpl2(currentUrl, {
|
|
48288
|
+
headers: { accept: "application/octet-stream" },
|
|
48289
|
+
redirect: "manual",
|
|
48290
|
+
signal
|
|
48291
|
+
});
|
|
48292
|
+
if (res.status >= 300 && res.status < 400) {
|
|
48293
|
+
const location = res.headers.get("location");
|
|
48294
|
+
if (!location)
|
|
48295
|
+
throw new Error(`redirect status ${res.status} without Location`);
|
|
48296
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
48297
|
+
continue;
|
|
48298
|
+
}
|
|
48299
|
+
return res;
|
|
48300
|
+
}
|
|
48301
|
+
throw new Error(`too many redirects (>${maxRedirects})`);
|
|
48302
|
+
}
|
|
48117
48303
|
function validateExtraction(stagingRoot) {
|
|
48118
48304
|
const realStagingRoot = realpathSync3(stagingRoot);
|
|
48119
48305
|
let totalBytes = 0;
|
|
@@ -48125,7 +48311,7 @@ function validateExtraction(stagingRoot) {
|
|
|
48125
48311
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
48126
48312
|
}
|
|
48127
48313
|
for (const entry of entries) {
|
|
48128
|
-
const full =
|
|
48314
|
+
const full = join11(dir, entry);
|
|
48129
48315
|
let lst;
|
|
48130
48316
|
try {
|
|
48131
48317
|
lst = lstatSync2(full);
|
|
@@ -48163,27 +48349,83 @@ function validateExtraction(stagingRoot) {
|
|
|
48163
48349
|
};
|
|
48164
48350
|
walk(realStagingRoot);
|
|
48165
48351
|
}
|
|
48352
|
+
function precheckArchiveSize(archivePath, archiveType) {
|
|
48353
|
+
let totalBytes = 0;
|
|
48354
|
+
if (archiveType === "zip") {
|
|
48355
|
+
const out = execFileSync2("unzip", ["-l", archivePath], { encoding: "utf8" });
|
|
48356
|
+
const match = out.match(/^\s*(\d+)\s+\d+\s+files?\s*$/m);
|
|
48357
|
+
if (match)
|
|
48358
|
+
totalBytes = Number.parseInt(match[1] ?? "0", 10);
|
|
48359
|
+
} else {
|
|
48360
|
+
const out = execFileSync2("tar", ["-tvf", archivePath], { encoding: "utf8" });
|
|
48361
|
+
for (const line of out.split(`
|
|
48362
|
+
`)) {
|
|
48363
|
+
const parts = line.trim().split(/\s+/);
|
|
48364
|
+
if (parts.length >= 6) {
|
|
48365
|
+
const numeric = parts.map((part) => Number.parseInt(part, 10)).filter((value) => Number.isFinite(value) && value >= 0);
|
|
48366
|
+
if (numeric.length > 0)
|
|
48367
|
+
totalBytes += Math.max(...numeric);
|
|
48368
|
+
}
|
|
48369
|
+
}
|
|
48370
|
+
}
|
|
48371
|
+
if (totalBytes > MAX_EXTRACT_BYTES2) {
|
|
48372
|
+
throw new Error(`archive uncompressed size ${totalBytes} exceeds ${MAX_EXTRACT_BYTES2}`);
|
|
48373
|
+
}
|
|
48374
|
+
}
|
|
48166
48375
|
function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
48167
48376
|
const suffix = randomBytes(8).toString("hex");
|
|
48168
48377
|
const stagingDir = `${destDir}.staging-${suffix}`;
|
|
48169
48378
|
try {
|
|
48170
|
-
|
|
48379
|
+
rmSync3(stagingDir, { recursive: true, force: true });
|
|
48171
48380
|
} catch {}
|
|
48172
|
-
|
|
48381
|
+
mkdirSync7(stagingDir, { recursive: true });
|
|
48173
48382
|
try {
|
|
48383
|
+
precheckArchiveSize(archivePath, archiveType);
|
|
48174
48384
|
runPlatformExtractor(archivePath, stagingDir, archiveType);
|
|
48175
48385
|
validateExtraction(stagingDir);
|
|
48176
48386
|
try {
|
|
48177
|
-
|
|
48387
|
+
rmSync3(destDir, { recursive: true, force: true });
|
|
48178
48388
|
} catch {}
|
|
48179
|
-
|
|
48389
|
+
renameSync4(stagingDir, destDir);
|
|
48180
48390
|
} catch (err) {
|
|
48181
48391
|
try {
|
|
48182
|
-
|
|
48392
|
+
rmSync3(stagingDir, { recursive: true, force: true });
|
|
48183
48393
|
} catch {}
|
|
48184
48394
|
throw err;
|
|
48185
48395
|
}
|
|
48186
48396
|
}
|
|
48397
|
+
function quarantineCachedGithubInstall(spec, reason) {
|
|
48398
|
+
const packageDir = ghPackageDir(spec);
|
|
48399
|
+
const dest = join11(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
48400
|
+
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
48401
|
+
try {
|
|
48402
|
+
mkdirSync7(dirname2(dest), { recursive: true });
|
|
48403
|
+
rmSync3(dest, { recursive: true, force: true });
|
|
48404
|
+
renameSync4(packageDir, dest);
|
|
48405
|
+
} catch (err) {
|
|
48406
|
+
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
48407
|
+
}
|
|
48408
|
+
}
|
|
48409
|
+
function validateCachedGithubInstall(spec, platform) {
|
|
48410
|
+
const meta3 = readInstalledMetaIn(ghPackageDir(spec));
|
|
48411
|
+
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join11(ghBinDir(spec), candidate)).find((candidate) => {
|
|
48412
|
+
try {
|
|
48413
|
+
return statSync4(candidate).isFile();
|
|
48414
|
+
} catch {
|
|
48415
|
+
return false;
|
|
48416
|
+
}
|
|
48417
|
+
});
|
|
48418
|
+
if (!meta3?.sha256 || !meta3.version || !isSafeVersion(meta3.version) || !binaryPath) {
|
|
48419
|
+
quarantineCachedGithubInstall(spec, "missing/unsafe metadata or binary");
|
|
48420
|
+
return false;
|
|
48421
|
+
}
|
|
48422
|
+
const currentHash = sha256OfFileSync2(binaryPath);
|
|
48423
|
+
if (currentHash !== meta3.sha256) {
|
|
48424
|
+
quarantineCachedGithubInstall(spec, `recorded ${meta3.sha256}, current ${currentHash}`);
|
|
48425
|
+
return false;
|
|
48426
|
+
}
|
|
48427
|
+
return true;
|
|
48428
|
+
}
|
|
48187
48429
|
function runPlatformExtractor(archivePath, destDir, archiveType) {
|
|
48188
48430
|
if (archiveType === "zip") {
|
|
48189
48431
|
if (process.platform === "win32") {
|
|
@@ -48229,7 +48471,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
|
|
|
48229
48471
|
}
|
|
48230
48472
|
const pkgDir = ghPackageDir(spec);
|
|
48231
48473
|
const extractDir = ghExtractDir(spec);
|
|
48232
|
-
const archivePath =
|
|
48474
|
+
const archivePath = join11(pkgDir, expected.name);
|
|
48233
48475
|
log2(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
|
|
48234
48476
|
try {
|
|
48235
48477
|
await downloadFile(matchingAsset.url, archivePath, fetchImpl2, matchingAsset.size, signal);
|
|
@@ -48268,13 +48510,13 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl2,
|
|
|
48268
48510
|
unlinkSync6(archivePath);
|
|
48269
48511
|
} catch {}
|
|
48270
48512
|
}
|
|
48271
|
-
const innerBinaryPath =
|
|
48513
|
+
const innerBinaryPath = join11(extractDir, spec.binaryPathInArchive(platform, arch, version2));
|
|
48272
48514
|
if (!existsSync7(innerBinaryPath)) {
|
|
48273
48515
|
error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
|
|
48274
48516
|
return null;
|
|
48275
48517
|
}
|
|
48276
48518
|
const targetBinary = ghBinaryPath(spec, platform);
|
|
48277
|
-
|
|
48519
|
+
mkdirSync7(dirname2(targetBinary), { recursive: true });
|
|
48278
48520
|
try {
|
|
48279
48521
|
copyFileSync3(innerBinaryPath, targetBinary);
|
|
48280
48522
|
if (platform !== "win32") {
|
|
@@ -48365,7 +48607,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl2 = fetch) {
|
|
|
48365
48607
|
};
|
|
48366
48608
|
}
|
|
48367
48609
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
48368
|
-
if (isGithubInstalled(spec, host.platform)) {
|
|
48610
|
+
if (isGithubInstalled(spec, host.platform) && validateCachedGithubInstall(spec, host.platform)) {
|
|
48369
48611
|
cachedBinDirs.push(ghBinDir(spec));
|
|
48370
48612
|
}
|
|
48371
48613
|
if (config2.disabled.has(spec.id)) {
|
|
@@ -48449,8 +48691,8 @@ function discoverRelevantGithubServers(projectRoot) {
|
|
|
48449
48691
|
}
|
|
48450
48692
|
|
|
48451
48693
|
// src/notifications.ts
|
|
48452
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
48453
|
-
import { join as
|
|
48694
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync7, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
48695
|
+
import { join as join12 } from "node:path";
|
|
48454
48696
|
var WARNING_MARKER = "\uD83D\uDD27 AFT: ⚠️";
|
|
48455
48697
|
var FEATURE_MARKER = "\uD83D\uDD27 AFT: ✨";
|
|
48456
48698
|
var WARNED_TOOLS_FILE = "warned_tools.json";
|
|
@@ -48468,10 +48710,10 @@ function sendIgnoredMessage(client, sessionId, text) {
|
|
|
48468
48710
|
}
|
|
48469
48711
|
function readWarnedTools(storageDir) {
|
|
48470
48712
|
try {
|
|
48471
|
-
const warnedToolsPath =
|
|
48713
|
+
const warnedToolsPath = join12(storageDir, WARNED_TOOLS_FILE);
|
|
48472
48714
|
if (!existsSync8(warnedToolsPath))
|
|
48473
48715
|
return {};
|
|
48474
|
-
const parsed = JSON.parse(
|
|
48716
|
+
const parsed = JSON.parse(readFileSync7(warnedToolsPath, "utf-8"));
|
|
48475
48717
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
48476
48718
|
return {};
|
|
48477
48719
|
const warned = {};
|
|
@@ -48487,12 +48729,34 @@ function readWarnedTools(storageDir) {
|
|
|
48487
48729
|
}
|
|
48488
48730
|
function writeWarnedTools(storageDir, warned) {
|
|
48489
48731
|
try {
|
|
48490
|
-
|
|
48491
|
-
const warnedToolsPath =
|
|
48492
|
-
|
|
48732
|
+
mkdirSync8(storageDir, { recursive: true });
|
|
48733
|
+
const warnedToolsPath = join12(storageDir, WARNED_TOOLS_FILE);
|
|
48734
|
+
const tmpPath = join12(storageDir, `${WARNED_TOOLS_FILE}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
48735
|
+
writeFileSync5(tmpPath, `${JSON.stringify(warned, null, 2)}
|
|
48493
48736
|
`);
|
|
48737
|
+
renameSync5(tmpPath, warnedToolsPath);
|
|
48494
48738
|
} catch {}
|
|
48495
48739
|
}
|
|
48740
|
+
async function withWarnedToolsLock(storageDir, fn) {
|
|
48741
|
+
const lockDir = join12(storageDir, "warned_tools.lock");
|
|
48742
|
+
for (let attempt = 0;attempt < 5; attempt++) {
|
|
48743
|
+
try {
|
|
48744
|
+
mkdirSync8(storageDir, { recursive: true });
|
|
48745
|
+
mkdirSync8(lockDir, { mode: 448 });
|
|
48746
|
+
try {
|
|
48747
|
+
return await fn();
|
|
48748
|
+
} finally {
|
|
48749
|
+
rmSync4(lockDir, { recursive: true, force: true });
|
|
48750
|
+
}
|
|
48751
|
+
} catch (err) {
|
|
48752
|
+
const code = err.code;
|
|
48753
|
+
if (code !== "EEXIST")
|
|
48754
|
+
return null;
|
|
48755
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10 * (attempt + 1)));
|
|
48756
|
+
}
|
|
48757
|
+
}
|
|
48758
|
+
return null;
|
|
48759
|
+
}
|
|
48496
48760
|
function warningKey(warning, projectRoot) {
|
|
48497
48761
|
const scope = warning.kind === "lsp_binary_missing" ? "_" : projectRoot ?? "_";
|
|
48498
48762
|
return [
|
|
@@ -48531,27 +48795,36 @@ ${warning.hint}`;
|
|
|
48531
48795
|
async function deliverConfigureWarnings(opts, warnings) {
|
|
48532
48796
|
if (warnings.length === 0)
|
|
48533
48797
|
return;
|
|
48534
|
-
const
|
|
48535
|
-
|
|
48536
|
-
|
|
48537
|
-
const
|
|
48538
|
-
|
|
48539
|
-
|
|
48540
|
-
|
|
48541
|
-
|
|
48798
|
+
const deliveredWithLock = await withWarnedToolsLock(opts.storageDir, async () => {
|
|
48799
|
+
const warned = readWarnedTools(opts.storageDir);
|
|
48800
|
+
let changed = false;
|
|
48801
|
+
for (const warning of warnings) {
|
|
48802
|
+
const key = warningKey(warning, opts.projectRoot);
|
|
48803
|
+
if (Object.hasOwn(warned, key))
|
|
48804
|
+
continue;
|
|
48805
|
+
if (!sendIgnoredMessage(opts.client, opts.sessionId, formatConfigureWarning(warning))) {
|
|
48806
|
+
continue;
|
|
48807
|
+
}
|
|
48808
|
+
warned[key] = opts.pluginVersion;
|
|
48809
|
+
changed = true;
|
|
48542
48810
|
}
|
|
48543
|
-
|
|
48544
|
-
|
|
48545
|
-
|
|
48546
|
-
|
|
48547
|
-
|
|
48811
|
+
if (changed) {
|
|
48812
|
+
const merged = { ...readWarnedTools(opts.storageDir), ...warned };
|
|
48813
|
+
writeWarnedTools(opts.storageDir, merged);
|
|
48814
|
+
}
|
|
48815
|
+
return true;
|
|
48816
|
+
});
|
|
48817
|
+
if (deliveredWithLock)
|
|
48818
|
+
return;
|
|
48819
|
+
for (const warning of warnings) {
|
|
48820
|
+
sendIgnoredMessage(opts.client, opts.sessionId, formatConfigureWarning(warning));
|
|
48548
48821
|
}
|
|
48549
48822
|
}
|
|
48550
48823
|
function sendFeatureAnnouncement(version2, features, storageDir) {
|
|
48551
|
-
const versionFile =
|
|
48824
|
+
const versionFile = join12(storageDir, "last_announced_version");
|
|
48552
48825
|
try {
|
|
48553
48826
|
if (existsSync8(versionFile)) {
|
|
48554
|
-
const lastVersion =
|
|
48827
|
+
const lastVersion = readFileSync7(versionFile, "utf-8").trim();
|
|
48555
48828
|
if (lastVersion === version2)
|
|
48556
48829
|
return;
|
|
48557
48830
|
}
|
|
@@ -48559,7 +48832,7 @@ function sendFeatureAnnouncement(version2, features, storageDir) {
|
|
|
48559
48832
|
log2([`${FEATURE_MARKER} v${version2}:`, ...features.map((feature) => ` • ${feature}`)].join(`
|
|
48560
48833
|
`));
|
|
48561
48834
|
try {
|
|
48562
|
-
|
|
48835
|
+
mkdirSync8(storageDir, { recursive: true });
|
|
48563
48836
|
writeFileSync5(versionFile, version2);
|
|
48564
48837
|
} catch {}
|
|
48565
48838
|
}
|
|
@@ -50002,7 +50275,6 @@ var ImportParams = Type6.Object({
|
|
|
50002
50275
|
defaultImport: Type6.Optional(Type6.String({ description: "Default import name (e.g. 'React')" })),
|
|
50003
50276
|
removeName: Type6.Optional(Type6.String({ description: "Named import to remove; omit to remove entire import" })),
|
|
50004
50277
|
typeOnly: Type6.Optional(Type6.Boolean({ description: "Type-only import (TS only)" })),
|
|
50005
|
-
dryRun: Type6.Optional(Type6.Boolean({ description: "Preview without writing" })),
|
|
50006
50278
|
validate: Type6.Optional(StringEnum2(["syntax", "full"], {
|
|
50007
50279
|
description: "Post-edit validation level (default: syntax)"
|
|
50008
50280
|
}))
|
|
@@ -50011,12 +50283,6 @@ function buildImportSections(args, payload, theme) {
|
|
|
50011
50283
|
const response = asRecord2(payload);
|
|
50012
50284
|
if (!response)
|
|
50013
50285
|
return [theme.fg("muted", "No import result.")];
|
|
50014
|
-
if (response.dry_run === true) {
|
|
50015
|
-
return [
|
|
50016
|
-
theme.fg("warning", `[dry run] ${args.op}`),
|
|
50017
|
-
asString(response.diff) || theme.fg("muted", "No diff available.")
|
|
50018
|
-
];
|
|
50019
|
-
}
|
|
50020
50286
|
if (args.op === "organize") {
|
|
50021
50287
|
const groups = asRecords(response.groups);
|
|
50022
50288
|
const groupText = groups.length > 0 ? groups.map((group) => `${asString(group.name) ?? "unknown"}: ${asNumber(group.count) ?? 0}`).join(" · ") : "No imports found";
|
|
@@ -50059,7 +50325,7 @@ function registerImportTools(pi, ctx) {
|
|
|
50059
50325
|
pi.registerTool({
|
|
50060
50326
|
name: "aft_import",
|
|
50061
50327
|
label: "import",
|
|
50062
|
-
description: "Language-aware import management. Supports TS, JS, TSX, Python, Rust, Go. Ops: `add
|
|
50328
|
+
description: "Language-aware import management. Supports TS, JS, TSX, Python, Rust, Go. Ops: `add`, `remove`, `organize`. Use aft_safety checkpoint/undo before broad cleanup.",
|
|
50063
50329
|
parameters: ImportParams,
|
|
50064
50330
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50065
50331
|
if ((params.op === "add" || params.op === "remove") && !params.module) {
|
|
@@ -50082,8 +50348,6 @@ function registerImportTools(pi, ctx) {
|
|
|
50082
50348
|
req.name = params.removeName;
|
|
50083
50349
|
if (params.typeOnly !== undefined)
|
|
50084
50350
|
req.type_only = params.typeOnly;
|
|
50085
|
-
if (params.dryRun !== undefined)
|
|
50086
|
-
req.dry_run = params.dryRun;
|
|
50087
50351
|
if (params.validate !== undefined)
|
|
50088
50352
|
req.validate = params.validate;
|
|
50089
50353
|
const response = await callBridge(bridge, commandMap[params.op], req, extCtx);
|
|
@@ -50564,7 +50828,7 @@ Pass a single \`target\`:
|
|
|
50564
50828
|
if (response.success === false) {
|
|
50565
50829
|
throw new Error(response.message || "zoom failed");
|
|
50566
50830
|
}
|
|
50567
|
-
return textResult(formatZoomText(targetLabel, response));
|
|
50831
|
+
return textResult(formatZoomText(targetLabel, response), response);
|
|
50568
50832
|
},
|
|
50569
50833
|
renderCall(args, theme, context) {
|
|
50570
50834
|
return renderZoomCall(args, theme, context);
|
|
@@ -50627,28 +50891,12 @@ var RefactorParams = Type10.Object({
|
|
|
50627
50891
|
name: Type10.Optional(Type10.String({ description: "New function name (for extract)" })),
|
|
50628
50892
|
startLine: Type10.Optional(Type10.Number({ description: "1-based start line (for extract)" })),
|
|
50629
50893
|
endLine: Type10.Optional(Type10.Number({ description: "1-based end line, inclusive (for extract)" })),
|
|
50630
|
-
callSiteLine: Type10.Optional(Type10.Number({ description: "1-based call site line (for inline)" }))
|
|
50631
|
-
dryRun: Type10.Optional(Type10.Boolean({ description: "Preview as diff" }))
|
|
50894
|
+
callSiteLine: Type10.Optional(Type10.Number({ description: "1-based call site line (for inline)" }))
|
|
50632
50895
|
});
|
|
50633
50896
|
function buildRefactorSections(args, payload, theme) {
|
|
50634
50897
|
const response = asRecord2(payload);
|
|
50635
50898
|
if (!response)
|
|
50636
50899
|
return [theme.fg("muted", "No refactor result.")];
|
|
50637
|
-
if (response.dry_run === true) {
|
|
50638
|
-
const diffs = asRecords(response.diffs);
|
|
50639
|
-
const sections = [theme.fg("warning", `[dry run] ${args.op}`)];
|
|
50640
|
-
if (diffs.length === 0) {
|
|
50641
|
-
sections.push(theme.fg("muted", "No diff available."));
|
|
50642
|
-
return sections;
|
|
50643
|
-
}
|
|
50644
|
-
diffs.forEach((diff) => {
|
|
50645
|
-
const file2 = shortenPath(asString(diff.file) ?? "(unknown file)");
|
|
50646
|
-
const rendered = renderUnifiedDiff(asString(diff.diff) ?? "") || theme.fg("muted", "No diff available.");
|
|
50647
|
-
sections.push(`${theme.fg("accent", file2)}
|
|
50648
|
-
${rendered}`);
|
|
50649
|
-
});
|
|
50650
|
-
return sections;
|
|
50651
|
-
}
|
|
50652
50900
|
if (args.op === "move") {
|
|
50653
50901
|
const results = asRecords(response.results);
|
|
50654
50902
|
return [
|
|
@@ -50691,7 +50939,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
50691
50939
|
pi.registerTool({
|
|
50692
50940
|
name: "aft_refactor",
|
|
50693
50941
|
label: "refactor",
|
|
50694
|
-
description: "Workspace-wide refactoring that updates imports and references across files. `move` relocates a top-level symbol
|
|
50942
|
+
description: "Workspace-wide refactoring that updates imports and references across files. `move` relocates a top-level symbol; `extract` pulls a line range into a new function; `inline` replaces a call site. Use aft_safety checkpoint/undo before risky refactors.",
|
|
50695
50943
|
parameters: RefactorParams,
|
|
50696
50944
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50697
50945
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -50716,8 +50964,6 @@ function registerRefactorTool(pi, ctx) {
|
|
|
50716
50964
|
}
|
|
50717
50965
|
if (params.callSiteLine !== undefined)
|
|
50718
50966
|
req.call_site_line = params.callSiteLine;
|
|
50719
|
-
if (params.dryRun !== undefined)
|
|
50720
|
-
req.dry_run = params.dryRun;
|
|
50721
50967
|
const response = await callBridge(bridge, commandMap[params.op], req, extCtx);
|
|
50722
50968
|
return textResult(JSON.stringify(response, null, 2));
|
|
50723
50969
|
},
|
|
@@ -50976,7 +51222,6 @@ var TransformParams = Type13.Object({
|
|
|
50976
51222
|
position: Type13.Optional(Type13.String({
|
|
50977
51223
|
description: "Position hint: 'first', 'last' (default), 'before:name', 'after:name' for add_member"
|
|
50978
51224
|
})),
|
|
50979
|
-
dryRun: Type13.Optional(Type13.Boolean({ description: "Preview without modifying" })),
|
|
50980
51225
|
validate: Type13.Optional(StringEnum7(["syntax", "full"], {
|
|
50981
51226
|
description: "Validation level (default: syntax)"
|
|
50982
51227
|
}))
|
|
@@ -50985,12 +51230,6 @@ function buildTransformSections(args, payload, theme) {
|
|
|
50985
51230
|
const response = asRecord2(payload);
|
|
50986
51231
|
if (!response)
|
|
50987
51232
|
return [theme.fg("muted", "No transform result.")];
|
|
50988
|
-
if (response.dry_run === true) {
|
|
50989
|
-
return [
|
|
50990
|
-
theme.fg("warning", `[dry run] ${args.op}`),
|
|
50991
|
-
asString(response.diff) ?? theme.fg("muted", "No diff available.")
|
|
50992
|
-
];
|
|
50993
|
-
}
|
|
50994
51233
|
const target = asString(response.target) ?? asString(response.scope) ?? args.target ?? args.container ?? args.field ?? args.filePath;
|
|
50995
51234
|
return [
|
|
50996
51235
|
`${theme.fg("success", "transformed")} ${theme.fg("accent", args.op)}`,
|
|
@@ -51016,7 +51255,7 @@ function registerStructureTool(pi, ctx) {
|
|
|
51016
51255
|
pi.registerTool({
|
|
51017
51256
|
name: "aft_transform",
|
|
51018
51257
|
label: "transform",
|
|
51019
|
-
description: "Scope-aware structural code transformations with correct indentation.
|
|
51258
|
+
description: "Scope-aware structural code transformations with correct indentation. Use aft_safety checkpoint/undo before risky transforms.",
|
|
51020
51259
|
parameters: TransformParams,
|
|
51021
51260
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
51022
51261
|
validateTransformParams(params);
|
|
@@ -51042,8 +51281,6 @@ function registerStructureTool(pi, ctx) {
|
|
|
51042
51281
|
req.value = params.value;
|
|
51043
51282
|
if (params.position !== undefined)
|
|
51044
51283
|
req.position = params.position;
|
|
51045
|
-
if (params.dryRun !== undefined)
|
|
51046
|
-
req.dry_run = params.dryRun;
|
|
51047
51284
|
if (params.validate !== undefined)
|
|
51048
51285
|
req.validate = params.validate;
|
|
51049
51286
|
const response = await callBridge(bridge, params.op, req, extCtx);
|
|
@@ -51195,6 +51432,7 @@ var ALL_ONLY_TOOLS = new Set([
|
|
|
51195
51432
|
"aft_transform",
|
|
51196
51433
|
"aft_refactor"
|
|
51197
51434
|
]);
|
|
51435
|
+
var pendingEagerWarnings = new Map;
|
|
51198
51436
|
function isConfigureWarning(value) {
|
|
51199
51437
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
51200
51438
|
return false;
|
|
@@ -51204,17 +51442,29 @@ function isConfigureWarning(value) {
|
|
|
51204
51442
|
function coerceConfigureWarnings(warnings) {
|
|
51205
51443
|
return warnings.filter(isConfigureWarning);
|
|
51206
51444
|
}
|
|
51445
|
+
function drainPendingEagerWarnings(projectRoot) {
|
|
51446
|
+
const pending = pendingEagerWarnings.get(projectRoot) ?? [];
|
|
51447
|
+
pendingEagerWarnings.delete(projectRoot);
|
|
51448
|
+
return pending;
|
|
51449
|
+
}
|
|
51207
51450
|
async function handleConfigureWarningsForSession(context) {
|
|
51451
|
+
const validWarnings = coerceConfigureWarnings(context.warnings);
|
|
51208
51452
|
if (!context.sessionId) {
|
|
51209
|
-
|
|
51453
|
+
if (validWarnings.length === 0)
|
|
51454
|
+
return;
|
|
51455
|
+
const pending = pendingEagerWarnings.get(context.projectRoot) ?? [];
|
|
51456
|
+
pending.push(...validWarnings);
|
|
51457
|
+
pendingEagerWarnings.set(context.projectRoot, pending);
|
|
51458
|
+
warn2(`[configure] deferred warnings for ${context.projectRoot} arrived without session_id; buffering until first session-bound call`);
|
|
51210
51459
|
return;
|
|
51211
51460
|
}
|
|
51212
51461
|
if (!context.client) {
|
|
51213
51462
|
warn2(`[configure] deferred warnings for session ${context.sessionId} arrived without notification client; skipping notification`);
|
|
51214
51463
|
return;
|
|
51215
51464
|
}
|
|
51216
|
-
const
|
|
51217
|
-
|
|
51465
|
+
const pendingWarnings = drainPendingEagerWarnings(context.projectRoot);
|
|
51466
|
+
const combinedWarnings = [...pendingWarnings, ...validWarnings];
|
|
51467
|
+
if (combinedWarnings.length === 0)
|
|
51218
51468
|
return;
|
|
51219
51469
|
await deliverConfigureWarnings({
|
|
51220
51470
|
client: context.client,
|
|
@@ -51222,10 +51472,10 @@ async function handleConfigureWarningsForSession(context) {
|
|
|
51222
51472
|
storageDir: context.storageDir,
|
|
51223
51473
|
pluginVersion: context.pluginVersion,
|
|
51224
51474
|
projectRoot: context.projectRoot
|
|
51225
|
-
},
|
|
51475
|
+
}, combinedWarnings);
|
|
51226
51476
|
}
|
|
51227
51477
|
function resolveStorageDir() {
|
|
51228
|
-
return
|
|
51478
|
+
return join13(homedir9(), ".pi", "agent", "aft");
|
|
51229
51479
|
}
|
|
51230
51480
|
function resolveToolSurface(config2) {
|
|
51231
51481
|
const surface = config2.tool_surface ?? "recommended";
|
|
@@ -51398,11 +51648,12 @@ ${lines}
|
|
|
51398
51648
|
errorPrefix: "[aft-pi]",
|
|
51399
51649
|
minVersion: PLUGIN_VERSION,
|
|
51400
51650
|
onConfigureWarnings: async ({ projectRoot, sessionId, client, warnings }) => {
|
|
51651
|
+
const pendingWarnings = sessionId ? drainPendingEagerWarnings(projectRoot) : [];
|
|
51401
51652
|
await handleConfigureWarningsForSession({
|
|
51402
51653
|
projectRoot,
|
|
51403
51654
|
sessionId,
|
|
51404
51655
|
client,
|
|
51405
|
-
warnings,
|
|
51656
|
+
warnings: [...pendingWarnings, ...warnings],
|
|
51406
51657
|
storageDir,
|
|
51407
51658
|
pluginVersion: PLUGIN_VERSION
|
|
51408
51659
|
});
|
|
@@ -51511,10 +51762,6 @@ ${lines}
|
|
|
51511
51762
|
runtime: pi
|
|
51512
51763
|
});
|
|
51513
51764
|
});
|
|
51514
|
-
pi.on("input", (_event, extCtx) => {
|
|
51515
|
-
resetBgWake(resolveSessionId(extCtx));
|
|
51516
|
-
return { action: "continue" };
|
|
51517
|
-
});
|
|
51518
51765
|
pi.on("session_shutdown", async () => {
|
|
51519
51766
|
try {
|
|
51520
51767
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|