@cortexkit/aft-pi 0.63.5 → 0.65.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.map +1 -1
- package/dist/config.d.ts +22 -54
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1740 -596
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/omp-approval.d.ts +18 -6
- package/dist/omp-approval.d.ts.map +1 -1
- package/dist/shared/status.d.ts +2 -6
- package/dist/shared/status.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +32 -0
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts +1 -0
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -7871,7 +7871,7 @@ var require_src2 = __commonJS(function(exports, module) {
|
|
|
7871
7871
|
});
|
|
7872
7872
|
|
|
7873
7873
|
// src/index.ts
|
|
7874
|
-
import { createRequire as
|
|
7874
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
7875
7875
|
|
|
7876
7876
|
// ../aft-bridge/dist/active-logger.js
|
|
7877
7877
|
var ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
|
|
@@ -8082,7 +8082,14 @@ var TERMINAL_BASH_STATUSES = new Set(TERMINAL_BG_TASK_STATUSES);
|
|
|
8082
8082
|
function isTerminalBashStatus(status, compatibilityAliases = false) {
|
|
8083
8083
|
return typeof status === "string" && (TERMINAL_BASH_STATUSES.has(status) || compatibilityAliases && (status === "cancelled" || status === "timeout"));
|
|
8084
8084
|
}
|
|
8085
|
+
|
|
8085
8086
|
// ../aft-bridge/dist/bash-format.js
|
|
8087
|
+
function formatUnknownPtyExit(taskId, data) {
|
|
8088
|
+
if (data.status !== "fate_unknown" || data.status_reason !== "AFT could not read how it ended") {
|
|
8089
|
+
return;
|
|
8090
|
+
}
|
|
8091
|
+
return `Task ${taskId}: exit status unknown (AFT could not read how it ended)`;
|
|
8092
|
+
}
|
|
8086
8093
|
function isTerminalStatus(status) {
|
|
8087
8094
|
return isTerminalBashStatus(status);
|
|
8088
8095
|
}
|
|
@@ -8114,6 +8121,79 @@ function relativePathEscapesRoot(rel, platform = process.platform) {
|
|
|
8114
8121
|
return rel === ".." || rel.startsWith("../") || platform === "win32" && rel.startsWith("..\\") || (platform === "win32" ? win32.isAbsolute(rel) : posix.isAbsolute(rel));
|
|
8115
8122
|
}
|
|
8116
8123
|
|
|
8124
|
+
// ../aft-bridge/dist/wait-clock.js
|
|
8125
|
+
var SUSPEND_SKEW_MS = 1000;
|
|
8126
|
+
var RESUME_ALLOWANCE_MS = 1e4;
|
|
8127
|
+
var DEADLINE_POLL_MS = 250;
|
|
8128
|
+
|
|
8129
|
+
class WaitClock {
|
|
8130
|
+
wallNow;
|
|
8131
|
+
awakeNow;
|
|
8132
|
+
wall;
|
|
8133
|
+
awake;
|
|
8134
|
+
skew = 0;
|
|
8135
|
+
elapsed;
|
|
8136
|
+
suspendCount = 0;
|
|
8137
|
+
lastSuspendEnd = Number.NEGATIVE_INFINITY;
|
|
8138
|
+
constructor(wallNow = Date.now, awakeNow = () => performance.now()) {
|
|
8139
|
+
this.wallNow = wallNow;
|
|
8140
|
+
this.awakeNow = awakeNow;
|
|
8141
|
+
this.wall = wallNow();
|
|
8142
|
+
this.awake = awakeNow();
|
|
8143
|
+
this.elapsed = this.awake;
|
|
8144
|
+
}
|
|
8145
|
+
now() {
|
|
8146
|
+
const wall = this.wallNow();
|
|
8147
|
+
const awake = this.awakeNow();
|
|
8148
|
+
const active = Math.max(0, awake - this.awake);
|
|
8149
|
+
this.skew = Math.max(0, this.skew + wall - this.wall - active);
|
|
8150
|
+
this.elapsed += active;
|
|
8151
|
+
if (this.skew >= SUSPEND_SKEW_MS) {
|
|
8152
|
+
this.elapsed += this.skew;
|
|
8153
|
+
this.lastSuspendEnd = this.elapsed;
|
|
8154
|
+
this.skew = 0;
|
|
8155
|
+
this.suspendCount++;
|
|
8156
|
+
}
|
|
8157
|
+
this.wall = wall;
|
|
8158
|
+
this.awake = awake;
|
|
8159
|
+
return this.elapsed;
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
|
|
8163
|
+
class RequestDeadline {
|
|
8164
|
+
clock;
|
|
8165
|
+
allowResume;
|
|
8166
|
+
deadline;
|
|
8167
|
+
suspendCount;
|
|
8168
|
+
resumed = false;
|
|
8169
|
+
constructor(clock, timeoutMs, allowResume = true) {
|
|
8170
|
+
this.clock = clock;
|
|
8171
|
+
this.allowResume = allowResume;
|
|
8172
|
+
this.deadline = clock.now() + timeoutMs;
|
|
8173
|
+
this.suspendCount = clock.suspendCount;
|
|
8174
|
+
}
|
|
8175
|
+
remaining() {
|
|
8176
|
+
const now = this.clock.now();
|
|
8177
|
+
if (now >= this.deadline && this.allowResume && !this.resumed && this.clock.suspendCount > this.suspendCount && this.clock.lastSuspendEnd >= this.deadline) {
|
|
8178
|
+
this.resumed = true;
|
|
8179
|
+
this.deadline = now + RESUME_ALLOWANCE_MS;
|
|
8180
|
+
}
|
|
8181
|
+
return Math.max(0, this.deadline - now);
|
|
8182
|
+
}
|
|
8183
|
+
}
|
|
8184
|
+
function deadlineTimer(scheduler, remaining, expired) {
|
|
8185
|
+
let timer;
|
|
8186
|
+
const check = () => {
|
|
8187
|
+
const left = remaining();
|
|
8188
|
+
if (left <= 0)
|
|
8189
|
+
expired();
|
|
8190
|
+
else
|
|
8191
|
+
timer = scheduler.setTimeout(check, Math.min(left, DEADLINE_POLL_MS));
|
|
8192
|
+
};
|
|
8193
|
+
check();
|
|
8194
|
+
return () => scheduler.clearTimeout(timer);
|
|
8195
|
+
}
|
|
8196
|
+
|
|
8117
8197
|
// ../aft-bridge/dist/bash-hints.js
|
|
8118
8198
|
var DEFAULT_PRIMARY_WATCH_TIMEOUT_MS = 30000;
|
|
8119
8199
|
var MAX_WATCH_TIMEOUT_MS = 1800000;
|
|
@@ -8141,7 +8221,8 @@ function watchPollDelayMs(elapsedMs) {
|
|
|
8141
8221
|
return 1000;
|
|
8142
8222
|
}
|
|
8143
8223
|
var watchClock = {
|
|
8144
|
-
|
|
8224
|
+
source: new WaitClock,
|
|
8225
|
+
now: () => watchClock.source.now(),
|
|
8145
8226
|
sleep: abortableSleep
|
|
8146
8227
|
};
|
|
8147
8228
|
function abortableSleep(ms, signal) {
|
|
@@ -8287,6 +8368,7 @@ var GREP_SEARCH_AFT_SEARCH_HINT = "DO NOT search code by running grep/rg in bash
|
|
|
8287
8368
|
var GREP_SEARCH_GREP_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `grep` tool instead (indexed and ranked).";
|
|
8288
8369
|
var GREP_SEARCH_HINT_PREFIX = "DO NOT search code by running grep/rg in bash —";
|
|
8289
8370
|
var GREP_SEARCH_FRESHNESS_WINDOW_MS = 60000;
|
|
8371
|
+
var MTIME_CLOCK_SLACK_MS = 1;
|
|
8290
8372
|
function maybeAppendConflictsHint(output) {
|
|
8291
8373
|
if (!output.includes("Automatic merge failed; fix conflicts"))
|
|
8292
8374
|
return output;
|
|
@@ -8294,15 +8376,15 @@ function maybeAppendConflictsHint(output) {
|
|
|
8294
8376
|
return output;
|
|
8295
8377
|
return output + CONFLICT_HINT;
|
|
8296
8378
|
}
|
|
8297
|
-
function commandInvokesCodeSearch(command) {
|
|
8298
|
-
const statements = splitTopLevelStatements(command);
|
|
8379
|
+
function commandInvokesCodeSearch(command, platform = process.platform) {
|
|
8380
|
+
const statements = splitTopLevelStatements(command, platform);
|
|
8299
8381
|
if (statements === null)
|
|
8300
8382
|
return false;
|
|
8301
8383
|
for (const statement of statements) {
|
|
8302
|
-
const firstStage = firstPipelineStage(statement);
|
|
8384
|
+
const firstStage = firstPipelineStage(statement, platform);
|
|
8303
8385
|
if (firstStage === null)
|
|
8304
8386
|
continue;
|
|
8305
|
-
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
8387
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0), platform);
|
|
8306
8388
|
if (firstToken === null)
|
|
8307
8389
|
continue;
|
|
8308
8390
|
if (firstToken.token === "grep" || firstToken.token === "rg")
|
|
@@ -8310,42 +8392,43 @@ function commandInvokesCodeSearch(command) {
|
|
|
8310
8392
|
}
|
|
8311
8393
|
return false;
|
|
8312
8394
|
}
|
|
8313
|
-
function maybeAppendGrepSearchHint(output, command, aftSearchRegistered, projectRoot) {
|
|
8395
|
+
function maybeAppendGrepSearchHint(output, command, aftSearchRegistered, projectRoot, platform = process.platform, workdir) {
|
|
8314
8396
|
if (output === "")
|
|
8315
8397
|
return output;
|
|
8316
|
-
if (!commandInvokesCodeSearch(command))
|
|
8398
|
+
if (!commandInvokesCodeSearch(command, platform))
|
|
8317
8399
|
return output;
|
|
8318
8400
|
if (output.includes(GREP_SEARCH_HINT_PREFIX))
|
|
8319
8401
|
return output;
|
|
8320
|
-
if (shouldSuppressGrepSearchHint(command, projectRoot))
|
|
8402
|
+
if (shouldSuppressGrepSearchHint(command, projectRoot, platform, workdir))
|
|
8321
8403
|
return output;
|
|
8322
8404
|
const hint = aftSearchRegistered ? GREP_SEARCH_AFT_SEARCH_HINT : GREP_SEARCH_GREP_HINT;
|
|
8323
8405
|
return `${output}
|
|
8324
8406
|
|
|
8325
8407
|
${hint}`;
|
|
8326
8408
|
}
|
|
8327
|
-
function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
8328
|
-
const statements = splitTopLevelStatements(command);
|
|
8409
|
+
function shouldSuppressGrepSearchHint(command, projectRoot, platform, workdir) {
|
|
8410
|
+
const statements = splitTopLevelStatements(command, platform);
|
|
8329
8411
|
if (statements === null)
|
|
8330
8412
|
return false;
|
|
8331
|
-
if (allSearchPathOperandsAreDynamic(statements))
|
|
8413
|
+
if (allSearchPathOperandsAreDynamic(statements, platform))
|
|
8332
8414
|
return true;
|
|
8333
8415
|
const root = projectRoot?.trim();
|
|
8334
8416
|
if (!root)
|
|
8335
8417
|
return false;
|
|
8336
|
-
const
|
|
8337
|
-
|
|
8418
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8419
|
+
const resolvedRoot = paths.resolve(root);
|
|
8420
|
+
let effectiveCwd = paths.resolve(resolvedRoot, workdir ?? ".");
|
|
8338
8421
|
let sawCodeSearchStatement = false;
|
|
8339
8422
|
for (const statement of statements) {
|
|
8340
|
-
const firstStage = firstPipelineStage(statement);
|
|
8423
|
+
const firstStage = firstPipelineStage(statement, platform);
|
|
8341
8424
|
if (firstStage === null)
|
|
8342
8425
|
continue;
|
|
8343
|
-
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
8426
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0), platform);
|
|
8344
8427
|
if (firstToken === null)
|
|
8345
8428
|
continue;
|
|
8346
8429
|
const head = firstToken.token;
|
|
8347
8430
|
if (head === "cd") {
|
|
8348
|
-
effectiveCwd = nextCwdAfterCd(effectiveCwd, firstStage, firstToken.end);
|
|
8431
|
+
effectiveCwd = nextCwdAfterCd(effectiveCwd, firstStage, firstToken.end, platform);
|
|
8349
8432
|
continue;
|
|
8350
8433
|
}
|
|
8351
8434
|
if (head !== "grep" && head !== "rg")
|
|
@@ -8353,20 +8436,23 @@ function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
|
8353
8436
|
sawCodeSearchStatement = true;
|
|
8354
8437
|
if (effectiveCwd === null)
|
|
8355
8438
|
continue;
|
|
8356
|
-
if (!isDirInsideProject(resolvedRoot, effectiveCwd))
|
|
8439
|
+
if (!isDirInsideProject(resolvedRoot, effectiveCwd, platform))
|
|
8357
8440
|
continue;
|
|
8358
|
-
const operands = collectPathOperands(firstStage, firstToken.end);
|
|
8441
|
+
const operands = collectPathOperands(firstStage, firstToken.end, platform);
|
|
8359
8442
|
if (operands.length === 0)
|
|
8360
8443
|
return false;
|
|
8361
8444
|
let sawInProjectOperand = false;
|
|
8362
8445
|
for (const operand of operands) {
|
|
8363
8446
|
if (isDynamicPathOperand(operand))
|
|
8364
8447
|
continue;
|
|
8365
|
-
const resolvedOperand = resolvePathOperand(effectiveCwd, operand);
|
|
8366
|
-
if (!isPathInsideProject(resolvedRoot, effectiveCwd, operand))
|
|
8448
|
+
const resolvedOperand = resolvePathOperand(effectiveCwd, operand, platform);
|
|
8449
|
+
if (!isPathInsideProject(resolvedRoot, effectiveCwd, operand, platform))
|
|
8450
|
+
continue;
|
|
8451
|
+
const suppress = shouldSuppressResolvedPath(resolvedOperand);
|
|
8452
|
+
if (suppress === undefined)
|
|
8367
8453
|
continue;
|
|
8368
8454
|
sawInProjectOperand = true;
|
|
8369
|
-
if (
|
|
8455
|
+
if (suppress)
|
|
8370
8456
|
return true;
|
|
8371
8457
|
}
|
|
8372
8458
|
if (sawInProjectOperand)
|
|
@@ -8374,19 +8460,19 @@ function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
|
8374
8460
|
}
|
|
8375
8461
|
return sawCodeSearchStatement;
|
|
8376
8462
|
}
|
|
8377
|
-
function allSearchPathOperandsAreDynamic(statements) {
|
|
8463
|
+
function allSearchPathOperandsAreDynamic(statements, platform) {
|
|
8378
8464
|
let sawDynamicOnlySearch = false;
|
|
8379
8465
|
for (const statement of statements) {
|
|
8380
|
-
const firstStage = firstPipelineStage(statement);
|
|
8466
|
+
const firstStage = firstPipelineStage(statement, platform);
|
|
8381
8467
|
if (firstStage === null)
|
|
8382
8468
|
continue;
|
|
8383
|
-
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
8469
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0), platform);
|
|
8384
8470
|
if (firstToken === null)
|
|
8385
8471
|
continue;
|
|
8386
8472
|
const head = firstToken.token;
|
|
8387
8473
|
if (head !== "grep" && head !== "rg")
|
|
8388
8474
|
continue;
|
|
8389
|
-
const operands = collectPathOperands(firstStage, firstToken.end);
|
|
8475
|
+
const operands = collectPathOperands(firstStage, firstToken.end, platform);
|
|
8390
8476
|
if (operands.length === 0)
|
|
8391
8477
|
return false;
|
|
8392
8478
|
if (operands.some((operand) => !isDynamicPathOperand(operand)))
|
|
@@ -8395,11 +8481,12 @@ function allSearchPathOperandsAreDynamic(statements) {
|
|
|
8395
8481
|
}
|
|
8396
8482
|
return sawDynamicOnlySearch;
|
|
8397
8483
|
}
|
|
8398
|
-
function nextCwdAfterCd(currentCwd, firstStage, afterCd) {
|
|
8484
|
+
function nextCwdAfterCd(currentCwd, firstStage, afterCd, platform) {
|
|
8485
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8399
8486
|
let index = skipSpaces(firstStage, afterCd);
|
|
8400
8487
|
let target = null;
|
|
8401
8488
|
while (index < firstStage.length) {
|
|
8402
|
-
const tokenResult = readShellToken(firstStage, index);
|
|
8489
|
+
const tokenResult = readShellToken(firstStage, index, platform);
|
|
8403
8490
|
if (tokenResult === null)
|
|
8404
8491
|
break;
|
|
8405
8492
|
const { token, end } = tokenResult;
|
|
@@ -8414,28 +8501,29 @@ function nextCwdAfterCd(currentCwd, firstStage, afterCd) {
|
|
|
8414
8501
|
break;
|
|
8415
8502
|
}
|
|
8416
8503
|
if (target === null)
|
|
8417
|
-
return
|
|
8504
|
+
return paths.resolve(os.homedir());
|
|
8418
8505
|
if (target.includes("$") || target.includes("`"))
|
|
8419
8506
|
return null;
|
|
8420
|
-
const expanded = expandTilde(target);
|
|
8421
|
-
if (
|
|
8422
|
-
return
|
|
8507
|
+
const expanded = expandTilde(target, platform);
|
|
8508
|
+
if (paths.isAbsolute(expanded))
|
|
8509
|
+
return paths.resolve(expanded);
|
|
8423
8510
|
if (currentCwd === null)
|
|
8424
8511
|
return null;
|
|
8425
|
-
return
|
|
8512
|
+
return paths.resolve(currentCwd, expanded);
|
|
8426
8513
|
}
|
|
8427
|
-
function isDirInsideProject(resolvedRoot, dir) {
|
|
8428
|
-
const
|
|
8429
|
-
|
|
8514
|
+
function isDirInsideProject(resolvedRoot, dir, platform) {
|
|
8515
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8516
|
+
const rel = paths.relative(resolvedRoot, paths.resolve(dir));
|
|
8517
|
+
return !relativePathEscapesRoot(rel, platform);
|
|
8430
8518
|
}
|
|
8431
|
-
function collectPathOperands(firstStage, startAfterCommand) {
|
|
8519
|
+
function collectPathOperands(firstStage, startAfterCommand, platform) {
|
|
8432
8520
|
const operands = [];
|
|
8433
8521
|
let index = skipSpaces(firstStage, startAfterCommand);
|
|
8434
8522
|
let sawPattern = false;
|
|
8435
8523
|
let optionsEnded = false;
|
|
8436
8524
|
let optionValue;
|
|
8437
8525
|
while (index < firstStage.length) {
|
|
8438
|
-
const tokenResult = readShellToken(firstStage, index);
|
|
8526
|
+
const tokenResult = readShellToken(firstStage, index, platform);
|
|
8439
8527
|
if (tokenResult === null)
|
|
8440
8528
|
break;
|
|
8441
8529
|
const { token, end } = tokenResult;
|
|
@@ -8494,22 +8582,25 @@ function collectPathOperands(firstStage, startAfterCommand) {
|
|
|
8494
8582
|
function isDynamicPathOperand(token) {
|
|
8495
8583
|
return token.startsWith("~") || token.includes("$") || token.includes("`");
|
|
8496
8584
|
}
|
|
8497
|
-
function expandTilde(target) {
|
|
8585
|
+
function expandTilde(target, platform) {
|
|
8498
8586
|
if (!target.startsWith("~"))
|
|
8499
8587
|
return target;
|
|
8500
8588
|
if (target === "~" || target.startsWith("~/")) {
|
|
8501
|
-
|
|
8589
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8590
|
+
return paths.join(os.homedir(), target.slice(1));
|
|
8502
8591
|
}
|
|
8503
8592
|
return target;
|
|
8504
8593
|
}
|
|
8505
|
-
function resolvePathOperand(projectRoot, operand) {
|
|
8506
|
-
const
|
|
8507
|
-
|
|
8594
|
+
function resolvePathOperand(projectRoot, operand, platform) {
|
|
8595
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8596
|
+
const expanded = expandTilde(operand, platform);
|
|
8597
|
+
return paths.isAbsolute(expanded) ? paths.resolve(expanded) : paths.resolve(projectRoot, expanded);
|
|
8508
8598
|
}
|
|
8509
|
-
function isPathInsideProject(resolvedRoot, baseCwd, operand) {
|
|
8510
|
-
const
|
|
8511
|
-
const
|
|
8512
|
-
|
|
8599
|
+
function isPathInsideProject(resolvedRoot, baseCwd, operand, platform) {
|
|
8600
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
8601
|
+
const resolved = resolvePathOperand(baseCwd, operand, platform);
|
|
8602
|
+
const rel = paths.relative(resolvedRoot, resolved);
|
|
8603
|
+
return !relativePathEscapesRoot(rel, platform);
|
|
8513
8604
|
}
|
|
8514
8605
|
function shouldSuppressResolvedPath(resolved) {
|
|
8515
8606
|
try {
|
|
@@ -8517,12 +8608,12 @@ function shouldSuppressResolvedPath(resolved) {
|
|
|
8517
8608
|
if (stats.isFile())
|
|
8518
8609
|
return true;
|
|
8519
8610
|
const ageMs = Date.now() - stats.mtimeMs;
|
|
8520
|
-
return ageMs
|
|
8611
|
+
return ageMs > -MTIME_CLOCK_SLACK_MS && ageMs < GREP_SEARCH_FRESHNESS_WINDOW_MS;
|
|
8521
8612
|
} catch {
|
|
8522
|
-
return
|
|
8613
|
+
return;
|
|
8523
8614
|
}
|
|
8524
8615
|
}
|
|
8525
|
-
function splitTopLevelStatements(command) {
|
|
8616
|
+
function splitTopLevelStatements(command, platform = process.platform) {
|
|
8526
8617
|
const statements = [];
|
|
8527
8618
|
let start = 0;
|
|
8528
8619
|
let quote = "none";
|
|
@@ -8541,7 +8632,7 @@ function splitTopLevelStatements(command) {
|
|
|
8541
8632
|
continue;
|
|
8542
8633
|
}
|
|
8543
8634
|
if (quote === "double") {
|
|
8544
|
-
if (ch === "\\")
|
|
8635
|
+
if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform))
|
|
8545
8636
|
escaped = true;
|
|
8546
8637
|
else if (ch === '"')
|
|
8547
8638
|
quote = "none";
|
|
@@ -8552,7 +8643,7 @@ function splitTopLevelStatements(command) {
|
|
|
8552
8643
|
inBacktick = false;
|
|
8553
8644
|
continue;
|
|
8554
8645
|
}
|
|
8555
|
-
if (ch === "\\") {
|
|
8646
|
+
if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform)) {
|
|
8556
8647
|
escaped = true;
|
|
8557
8648
|
continue;
|
|
8558
8649
|
}
|
|
@@ -8595,7 +8686,7 @@ function splitTopLevelStatements(command) {
|
|
|
8595
8686
|
statements.push(command.slice(start));
|
|
8596
8687
|
return statements;
|
|
8597
8688
|
}
|
|
8598
|
-
function firstPipelineStage(command) {
|
|
8689
|
+
function firstPipelineStage(command, platform = process.platform) {
|
|
8599
8690
|
let quote = "none";
|
|
8600
8691
|
let firstPipeIndex;
|
|
8601
8692
|
for (let index = 0;index < command.length; index++) {
|
|
@@ -8608,7 +8699,7 @@ function firstPipelineStage(command) {
|
|
|
8608
8699
|
if (quote === "double") {
|
|
8609
8700
|
if (ch === '"') {
|
|
8610
8701
|
quote = "none";
|
|
8611
|
-
} else if (ch === "\\") {
|
|
8702
|
+
} else if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform)) {
|
|
8612
8703
|
index++;
|
|
8613
8704
|
} else if (ch === "`") {
|
|
8614
8705
|
return null;
|
|
@@ -8619,7 +8710,7 @@ function firstPipelineStage(command) {
|
|
|
8619
8710
|
quote = "single";
|
|
8620
8711
|
} else if (ch === '"') {
|
|
8621
8712
|
quote = "double";
|
|
8622
|
-
} else if (ch === "\\") {
|
|
8713
|
+
} else if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform)) {
|
|
8623
8714
|
index++;
|
|
8624
8715
|
} else if (ch === "`") {
|
|
8625
8716
|
return null;
|
|
@@ -8635,7 +8726,7 @@ function firstPipelineStage(command) {
|
|
|
8635
8726
|
return null;
|
|
8636
8727
|
return command.slice(0, firstPipeIndex ?? command.length).trim();
|
|
8637
8728
|
}
|
|
8638
|
-
function readShellToken(command, start) {
|
|
8729
|
+
function readShellToken(command, start, platform = process.platform) {
|
|
8639
8730
|
let quote = "none";
|
|
8640
8731
|
let token = "";
|
|
8641
8732
|
let index = start;
|
|
@@ -8652,9 +8743,11 @@ function readShellToken(command, start) {
|
|
|
8652
8743
|
if (quote === "double") {
|
|
8653
8744
|
if (ch === '"') {
|
|
8654
8745
|
quote = "none";
|
|
8655
|
-
} else if (ch === "\\") {
|
|
8746
|
+
} else if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform)) {
|
|
8656
8747
|
index++;
|
|
8657
|
-
|
|
8748
|
+
if (command[index] !== `
|
|
8749
|
+
`)
|
|
8750
|
+
token += command[index];
|
|
8658
8751
|
} else if (ch === "`") {
|
|
8659
8752
|
return null;
|
|
8660
8753
|
} else {
|
|
@@ -8670,7 +8763,7 @@ function readShellToken(command, start) {
|
|
|
8670
8763
|
quote = "single";
|
|
8671
8764
|
} else if (ch === '"') {
|
|
8672
8765
|
quote = "double";
|
|
8673
|
-
} else if (ch === "\\") {
|
|
8766
|
+
} else if (ch === "\\" && backslashEscapesNext(command[index + 1], quote, platform)) {
|
|
8674
8767
|
index++;
|
|
8675
8768
|
token += command[index] ?? "\\";
|
|
8676
8769
|
} else if (ch === "`") {
|
|
@@ -8683,6 +8776,11 @@ function readShellToken(command, start) {
|
|
|
8683
8776
|
return null;
|
|
8684
8777
|
return { token, end: index };
|
|
8685
8778
|
}
|
|
8779
|
+
function backslashEscapesNext(next, quote, platform) {
|
|
8780
|
+
if (quote === "double")
|
|
8781
|
+
return next !== undefined && '$`"\\\n'.includes(next);
|
|
8782
|
+
return platform !== "win32";
|
|
8783
|
+
}
|
|
8686
8784
|
function isTokenBoundary(ch) {
|
|
8687
8785
|
return ch === "|" || ch === ";" || ch === "&" || ch === "<" || ch === ">";
|
|
8688
8786
|
}
|
|
@@ -8694,7 +8792,7 @@ function skipSpaces(input, start) {
|
|
|
8694
8792
|
}
|
|
8695
8793
|
// ../aft-bridge/dist/bash-host-fallback.js
|
|
8696
8794
|
import { existsSync } from "node:fs";
|
|
8697
|
-
import { join as
|
|
8795
|
+
import { join as join3 } from "node:path";
|
|
8698
8796
|
|
|
8699
8797
|
// ../aft-bridge/dist/child-process.js
|
|
8700
8798
|
import { execFile as nodeExecFile, execFileSync as nodeExecFileSync, execSync as nodeExecSync, spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
|
@@ -8702,7 +8800,7 @@ import { execFile as nodeExecFile, execFileSync as nodeExecFileSync, execSync as
|
|
|
8702
8800
|
// ../aft-bridge/dist/test-child-environment.js
|
|
8703
8801
|
import { mkdirSync, realpathSync } from "node:fs";
|
|
8704
8802
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
8705
|
-
import { dirname, isAbsolute
|
|
8803
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
8706
8804
|
var DIRECTORIES = [
|
|
8707
8805
|
"HOME",
|
|
8708
8806
|
"USERPROFILE",
|
|
@@ -8722,8 +8820,8 @@ function toolchainHomes(inherited) {
|
|
|
8722
8820
|
if (!global[TOOLCHAINS]) {
|
|
8723
8821
|
const home = process.platform === "win32" ? inherited.USERPROFILE || inherited.HOME || homedir3() : inherited.HOME || homedir3();
|
|
8724
8822
|
global[TOOLCHAINS] = {
|
|
8725
|
-
RUSTUP_HOME: inherited.RUSTUP_HOME ||
|
|
8726
|
-
CARGO_HOME: inherited.CARGO_HOME ||
|
|
8823
|
+
RUSTUP_HOME: inherited.RUSTUP_HOME || join(home, ".rustup"),
|
|
8824
|
+
CARGO_HOME: inherited.CARGO_HOME || join(home, ".cargo")
|
|
8727
8825
|
};
|
|
8728
8826
|
}
|
|
8729
8827
|
return global[TOOLCHAINS];
|
|
@@ -8750,31 +8848,31 @@ function isolatedAftEnvironment(root, inherited = process.env) {
|
|
|
8750
8848
|
"aft-cache"
|
|
8751
8849
|
];
|
|
8752
8850
|
for (const [index, key] of DIRECTORIES.entries()) {
|
|
8753
|
-
env[key] =
|
|
8851
|
+
env[key] = join(root, names[index]);
|
|
8754
8852
|
mkdirSync(env[key], { recursive: true });
|
|
8755
8853
|
}
|
|
8756
8854
|
delete env.AFT_ALLOW_PRODUCTION_MIGRATION;
|
|
8757
|
-
createdRoots().add(
|
|
8855
|
+
createdRoots().add(resolve(root));
|
|
8758
8856
|
return env;
|
|
8759
8857
|
}
|
|
8760
8858
|
function canonical(path) {
|
|
8761
|
-
let ancestor =
|
|
8859
|
+
let ancestor = resolve(path);
|
|
8762
8860
|
const tail = [];
|
|
8763
8861
|
while (true) {
|
|
8764
8862
|
try {
|
|
8765
|
-
return
|
|
8863
|
+
return join(realpathSync(ancestor), ...tail);
|
|
8766
8864
|
} catch {
|
|
8767
8865
|
const parent = dirname(ancestor);
|
|
8768
8866
|
if (parent === ancestor)
|
|
8769
|
-
return
|
|
8770
|
-
tail.unshift(
|
|
8867
|
+
return resolve(path);
|
|
8868
|
+
tail.unshift(relative(parent, ancestor));
|
|
8771
8869
|
ancestor = parent;
|
|
8772
8870
|
}
|
|
8773
8871
|
}
|
|
8774
8872
|
}
|
|
8775
8873
|
function within(path, root) {
|
|
8776
|
-
const tail =
|
|
8777
|
-
return tail === "" || !tail.startsWith("..") && !
|
|
8874
|
+
const tail = relative(canonical(root), canonical(path));
|
|
8875
|
+
return tail === "" || !tail.startsWith("..") && !isAbsolute(tail);
|
|
8778
8876
|
}
|
|
8779
8877
|
function testChildEnvironment(env) {
|
|
8780
8878
|
const root = process.env.AFT_TEST_ISOLATION_ROOT;
|
|
@@ -8850,7 +8948,7 @@ function withPathPrepended(env, dir, platform = process.platform) {
|
|
|
8850
8948
|
|
|
8851
8949
|
// ../aft-bridge/dist/storage-paths.js
|
|
8852
8950
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "node:os";
|
|
8853
|
-
import { join as
|
|
8951
|
+
import { join as join2, resolve as resolve2 } from "node:path";
|
|
8854
8952
|
function environmentValue(context, name) {
|
|
8855
8953
|
const value = (context.lookup ?? ((key) => process.env[key]))(name);
|
|
8856
8954
|
return value !== undefined && value.length > 0 ? value : undefined;
|
|
@@ -8872,21 +8970,21 @@ function resolveDataHome(context = {}) {
|
|
|
8872
8970
|
return localAppData;
|
|
8873
8971
|
const userProfile = environmentValue(context, "USERPROFILE");
|
|
8874
8972
|
if (userProfile)
|
|
8875
|
-
return
|
|
8973
|
+
return join2(userProfile, "AppData", "Local");
|
|
8876
8974
|
}
|
|
8877
8975
|
const home = environmentValue(context, "HOME");
|
|
8878
8976
|
if (home)
|
|
8879
|
-
return
|
|
8880
|
-
return
|
|
8977
|
+
return join2(home, ".local", "share");
|
|
8978
|
+
return join2(".local", "share");
|
|
8881
8979
|
}
|
|
8882
8980
|
function resolveStoragePath(raw, context = {}) {
|
|
8883
8981
|
let expanded = raw;
|
|
8884
8982
|
if (raw === "~") {
|
|
8885
8983
|
expanded = homeDir(context);
|
|
8886
8984
|
} else if (raw.startsWith("~/") || raw.startsWith("~\\")) {
|
|
8887
|
-
expanded =
|
|
8985
|
+
expanded = join2(homeDir(context), raw.slice(2));
|
|
8888
8986
|
}
|
|
8889
|
-
return
|
|
8987
|
+
return resolve2(context.currentDirectory ?? process.cwd(), expanded);
|
|
8890
8988
|
}
|
|
8891
8989
|
function resolveCortexKitStorageRoot(context = {}) {
|
|
8892
8990
|
const override = environmentValue(context, "AFT_STORAGE_DIR");
|
|
@@ -8894,8 +8992,8 @@ function resolveCortexKitStorageRoot(context = {}) {
|
|
|
8894
8992
|
return resolveStoragePath(override, context);
|
|
8895
8993
|
const legacyCache = environmentValue(context, "AFT_CACHE_DIR");
|
|
8896
8994
|
if (legacyCache)
|
|
8897
|
-
return
|
|
8898
|
-
return resolveStoragePath(
|
|
8995
|
+
return join2(resolveStoragePath(legacyCache, context), "aft");
|
|
8996
|
+
return resolveStoragePath(join2(resolveDataHome(context), "cortexkit", "aft"), context);
|
|
8899
8997
|
}
|
|
8900
8998
|
function resolveAftStorageRoot(configuredRoot, context = {}) {
|
|
8901
8999
|
if (environmentValue(context, "AFT_STORAGE_DIR")) {
|
|
@@ -8906,14 +9004,14 @@ function resolveAftStorageRoot(configuredRoot, context = {}) {
|
|
|
8906
9004
|
return resolveCortexKitStorageRoot(context);
|
|
8907
9005
|
}
|
|
8908
9006
|
function resolveAftLogPath(filename, configuredRoot, context = {}) {
|
|
8909
|
-
return
|
|
9007
|
+
return join2(resolveAftStorageRoot(configuredRoot, context), "logs", filename);
|
|
8910
9008
|
}
|
|
8911
9009
|
function isTestEnvironment(context = {}) {
|
|
8912
9010
|
return environmentValue(context, "BUN_TEST") === "1" || environmentValue(context, "NODE_ENV") === "test";
|
|
8913
9011
|
}
|
|
8914
9012
|
function resolvePluginLogPath(context = {}) {
|
|
8915
9013
|
if (isTestEnvironment(context)) {
|
|
8916
|
-
return
|
|
9014
|
+
return join2(environmentValue(context, "TMPDIR") ?? tmpdir2(), `aft-plugin-tests-${process.pid}`, "logs", "aft-plugin-test.log");
|
|
8917
9015
|
}
|
|
8918
9016
|
return resolveAftLogPath("aft-plugin.log", undefined, context);
|
|
8919
9017
|
}
|
|
@@ -8923,9 +9021,10 @@ var BASH_HOST_FALLBACK_BANNER = "[AFT host fallback - module transport down; no
|
|
|
8923
9021
|
var BASH_HOST_FALLBACK_MAX_OUTPUT_BYTES = 100 * 1024;
|
|
8924
9022
|
var BASH_HOST_FALLBACK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
|
|
8925
9023
|
var BASH_HOST_FALLBACK_REFUSAL = "AFT module connection is down";
|
|
8926
|
-
var
|
|
8927
|
-
var
|
|
8928
|
-
var
|
|
9024
|
+
var BASH_RUNON_MEMORY_DESCRIPTION = "On Linux, add `,Ngb` to ask for N GiB of memory, for example `linux,2c,16gb` for a Rust build.";
|
|
9025
|
+
var BASH_RUNON_DESCRIPTION = 'Run on a remote build server: `linux` (2 vCPUs), or `linux,Nc` for N vCPUs such as `linux,8c`; add ",net" for outbound internet (offline by default). `windows` runs the command in Git Bash on a Windows build machine (no vCPU or network options). `macos` sends the command to a macOS machine for builds and tests that need macOS (no vCPU or network options).' + ` ${BASH_RUNON_MEMORY_DESCRIPTION}`;
|
|
9026
|
+
var BASH_RETURN_PATHS_DESCRIPTION = "With runon: up to 64 files to copy back from the build server after the command, as paths relative to workdir inside the project (up to 128 MiB in total). Each verified file replaces the local file at the same path; the result lists every path as returned or not returned, with the reason.";
|
|
9027
|
+
var BASH_RUNON_GUIDANCE = 'When remote runs are available, put `runon: "linux"` on build and test lines (cargo, bun test), including chains and pipes. A runon job\'s file changes are not copied back; only output, the exit code and return_paths files return. Edit locally first; name a file in return_paths to regenerate it remotely. Keep git, gh, interactive and file-editing commands local, and keep a line local if it needs this machine\'s own operating system or hardware (its sandbox, code signing, service manager, permission prompts or GUI frameworks) or runs a binary that already exists on this machine; a build or test that needs only macOS (its sandbox, code signing or file watching) can use runon: "macos" instead; building and testing still go remote, and a remote build leaves no binaries or target/ output in the local worktree. Add `,Nc` to request N vCPUs (`linux,4c`); without it the job gets 2 vCPUs. The job sees exactly the CPUs it gets, so ask for what the command uses: 2c (the default) for a single test binary or script, 4c for `cargo check`/clippy on one crate, 8c for a workspace test suite, and 16c for a large release build.';
|
|
8929
9028
|
function pathKeyForPlatform(env, platform) {
|
|
8930
9029
|
return platform === "win32" ? Object.keys(env).find((key) => key.toLowerCase() === "path") : "PATH";
|
|
8931
9030
|
}
|
|
@@ -8947,8 +9046,8 @@ function hostFallbackEnvWithShims(env, platform) {
|
|
|
8947
9046
|
const pathKey = pathKeyForPlatform(normalized, platform);
|
|
8948
9047
|
const inherited = pathKey === undefined ? undefined : normalized[pathKey];
|
|
8949
9048
|
const storageRoot = normalized.AFT_STORAGE_DIR ? resolveStoragePath(normalized.AFT_STORAGE_DIR) : resolveCortexKitStorageRoot();
|
|
8950
|
-
const shimsDir =
|
|
8951
|
-
if (!existsSync(
|
|
9049
|
+
const shimsDir = join3(storageRoot, "shims");
|
|
9050
|
+
if (!existsSync(join3(shimsDir, platform === "win32" ? "gh.cmd" : "gh")))
|
|
8952
9051
|
return normalized;
|
|
8953
9052
|
const separator = platform === "win32" ? ";" : ":";
|
|
8954
9053
|
const entries = (inherited ?? "").split(separator).filter((entry) => entry.length > 0);
|
|
@@ -9180,6 +9279,7 @@ function bashAcksConfirmed(response, targets) {
|
|
|
9180
9279
|
}
|
|
9181
9280
|
// ../aft-bridge/dist/bash-permissions.js
|
|
9182
9281
|
var BASH_PERMISSION_NOT_SETTLED = "bash permission not settled after {rounds} rounds: still asking for {kind} {path} (operation: {operation}; granted already: {granted})";
|
|
9282
|
+
var BASH_PERMISSION_DENIED = "Permission denied for this shell command: {parts} ({host reason}). The parts are checked together, so one denied part denies the whole command; run the allowed parts in a separate call.";
|
|
9183
9283
|
|
|
9184
9284
|
class BashPermissionLoopError extends Error {
|
|
9185
9285
|
}
|
|
@@ -9197,6 +9297,10 @@ ${ask.command ?? ""}
|
|
|
9197
9297
|
Working directory:
|
|
9198
9298
|
${ask.cwd ?? ""}`;
|
|
9199
9299
|
}
|
|
9300
|
+
function permissionAskPath(ask) {
|
|
9301
|
+
const paths = ask.patterns?.length ? ask.patterns : bashPermissionRetryGrants(ask);
|
|
9302
|
+
return ask.kind === "escalation" && ask.command ? `${ask.command}${ask.cwd ? ` @ ${ask.cwd}` : ""}` : paths.length ? paths.join(", ") : "<no asks returned>";
|
|
9303
|
+
}
|
|
9200
9304
|
async function withBashPermissionLoop(options) {
|
|
9201
9305
|
const granted = new Set;
|
|
9202
9306
|
const alreadyGranted = (ask) => {
|
|
@@ -9218,20 +9322,22 @@ async function withBashPermissionLoop(options) {
|
|
|
9218
9322
|
already_granted: alreadyGranted(ask)
|
|
9219
9323
|
}))
|
|
9220
9324
|
});
|
|
9221
|
-
const unsettled = (
|
|
9325
|
+
const unsettled = () => {
|
|
9222
9326
|
const message = (asks.length ? asks : [{}]).map((ask) => {
|
|
9223
|
-
|
|
9224
|
-
const path = ask.kind === "escalation" && ask.command ? `${ask.command}${ask.cwd ? ` @ ${ask.cwd}` : ""}` : paths.length ? paths.join(", ") : "<no asks returned>";
|
|
9225
|
-
return BASH_PERMISSION_NOT_SETTLED.replace("{rounds}", String(round)).replace("{kind}", ask.kind ?? "unknown").replace("{operation}", ask.operation ?? options.operation ?? "bash").replace("{granted}", alreadyGranted(ask) ? "yes" : "no").replace("{path}", () => path);
|
|
9327
|
+
return BASH_PERMISSION_NOT_SETTLED.replace("{rounds}", String(round)).replace("{kind}", ask.kind ?? "unknown").replace("{operation}", ask.operation ?? options.operation ?? "bash").replace("{granted}", alreadyGranted(ask) ? "yes" : "no").replace("{path}", () => permissionAskPath(ask));
|
|
9226
9328
|
}).join("; ");
|
|
9227
|
-
return new BashPermissionLoopError(
|
|
9329
|
+
return new BashPermissionLoopError(message);
|
|
9228
9330
|
};
|
|
9229
9331
|
if (asks.length === 0 || asks.every(alreadyGranted) || round >= 8)
|
|
9230
9332
|
throw unsettled();
|
|
9333
|
+
const pendingAsks = asks.filter((ask) => !alreadyGranted(ask));
|
|
9231
9334
|
try {
|
|
9232
|
-
await options.approve(
|
|
9335
|
+
await options.approve(pendingAsks);
|
|
9233
9336
|
} catch (error) {
|
|
9234
|
-
|
|
9337
|
+
const parts = pendingAsks.map(permissionAskPath).join(", ");
|
|
9338
|
+
const reason = error instanceof Error && error.message ? error.message.trim().replace(/\.$/, "") : "";
|
|
9339
|
+
const message = reason ? BASH_PERMISSION_DENIED.replace("{host reason}", () => reason) : BASH_PERMISSION_DENIED.replace(" ({host reason})", "");
|
|
9340
|
+
throw new BashPermissionLoopError(message.replace("{parts}", () => parts), { cause: error });
|
|
9235
9341
|
}
|
|
9236
9342
|
for (const ask of asks) {
|
|
9237
9343
|
for (const grant of bashPermissionRetryGrants(ask))
|
|
@@ -9700,7 +9806,7 @@ async function cachedFileSha256(path) {
|
|
|
9700
9806
|
}
|
|
9701
9807
|
// ../aft-bridge/dist/bridge.js
|
|
9702
9808
|
import { homedir as homedir5 } from "node:os";
|
|
9703
|
-
import { join as
|
|
9809
|
+
import { join as join4 } from "node:path";
|
|
9704
9810
|
import { StringDecoder } from "node:string_decoder";
|
|
9705
9811
|
|
|
9706
9812
|
// ../aft-bridge/dist/command-timeouts.js
|
|
@@ -9838,6 +9944,7 @@ function compareSemver(a, b) {
|
|
|
9838
9944
|
}
|
|
9839
9945
|
var SYSTEM_CLOCK = {
|
|
9840
9946
|
now: () => Date.now(),
|
|
9947
|
+
awakeNow: () => performance.now(),
|
|
9841
9948
|
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
|
9842
9949
|
clearTimeout: (timer) => clearTimeout(timer)
|
|
9843
9950
|
};
|
|
@@ -9954,6 +10061,7 @@ class BinaryBridge {
|
|
|
9954
10061
|
hangWindowMinMs;
|
|
9955
10062
|
hangProbeTimeoutMs;
|
|
9956
10063
|
clock;
|
|
10064
|
+
waitClock;
|
|
9957
10065
|
lateShellReplyGraceMs;
|
|
9958
10066
|
maxRestarts;
|
|
9959
10067
|
configured = false;
|
|
@@ -9990,6 +10098,7 @@ class BinaryBridge {
|
|
|
9990
10098
|
this.hangWindowMinMs = options?.hangWindowMinMs ?? DEFAULT_MIN_HANG_WINDOW_MS;
|
|
9991
10099
|
this.hangProbeTimeoutMs = options?.hangProbeTimeoutMs ?? DEFAULT_HANG_PROBE_TIMEOUT_MS;
|
|
9992
10100
|
this.clock = options?.clock ?? SYSTEM_CLOCK;
|
|
10101
|
+
this.waitClock = new WaitClock(() => this.clock.now(), () => this.clock.awakeNow?.() ?? this.clock.now());
|
|
9993
10102
|
this.lateShellReplyGraceMs = options?.lateShellReplyGraceMs ?? DEFAULT_LATE_SHELL_REPLY_GRACE_MS;
|
|
9994
10103
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
9995
10104
|
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
@@ -10356,11 +10465,17 @@ class BinaryBridge {
|
|
|
10356
10465
|
const keepBridgeOnTimeout = passive || command === "bash_drain_completions" || options?.keepBridgeOnTimeout === true;
|
|
10357
10466
|
const child = this.process;
|
|
10358
10467
|
const response = await new Promise((resolve, reject) => {
|
|
10359
|
-
const
|
|
10468
|
+
const deadline = new RequestDeadline(this.waitClock, effectiveTimeoutMs);
|
|
10469
|
+
const expire = () => {
|
|
10360
10470
|
const entry = this.pending.get(id);
|
|
10361
10471
|
if (!entry)
|
|
10362
10472
|
return;
|
|
10363
|
-
|
|
10473
|
+
const remaining = deadline.remaining();
|
|
10474
|
+
if (remaining > 0) {
|
|
10475
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(remaining, DEADLINE_POLL_MS));
|
|
10476
|
+
return;
|
|
10477
|
+
}
|
|
10478
|
+
if (keepBridgeOnTimeout && !deadline.resumed && this.holdForLateShellReply(id, entry, effectiveTimeoutMs, requestSessionId)) {
|
|
10364
10479
|
return;
|
|
10365
10480
|
}
|
|
10366
10481
|
this.pending.delete(id);
|
|
@@ -10373,11 +10488,12 @@ class BinaryBridge {
|
|
|
10373
10488
|
this.warnVia(timeoutMsg);
|
|
10374
10489
|
}
|
|
10375
10490
|
if (keepBridgeOnTimeout) {
|
|
10376
|
-
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms
|
|
10491
|
+
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`, deadline.resumed && SHELL_TASK_COMMANDS.has(command) ? { kind: "unknown" } : undefined));
|
|
10377
10492
|
return;
|
|
10378
10493
|
}
|
|
10379
10494
|
entry.reject(new Error(`${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry. A timed-out mutation may still complete; inspect before repeating it.`));
|
|
10380
|
-
}
|
|
10495
|
+
};
|
|
10496
|
+
const timer = this.clock.setTimeout(expire, Math.min(effectiveTimeoutMs, DEADLINE_POLL_MS));
|
|
10381
10497
|
const entry = {
|
|
10382
10498
|
resolve,
|
|
10383
10499
|
reject,
|
|
@@ -10608,11 +10724,11 @@ class BinaryBridge {
|
|
|
10608
10724
|
})();
|
|
10609
10725
|
const useFastembedBackend = semanticBackend === undefined || semanticBackend === "fastembed" || semanticBackend === "";
|
|
10610
10726
|
const ortDir = typeof this.configOverrides._ort_dylib_dir === "string" && useFastembedBackend ? this.configOverrides._ort_dylib_dir : null;
|
|
10611
|
-
const ortLibraryPath = ortDir == null ? null :
|
|
10727
|
+
const ortLibraryPath = ortDir == null ? null : join4(ortDir, this.platform === "win32" ? "onnxruntime.dll" : this.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so");
|
|
10612
10728
|
const env = { ...process.env };
|
|
10613
10729
|
this.logVia(`bridge.spawnProcess: useFastembedBackend=${useFastembedBackend}, ` + `parentORT=${process.env.ORT_DYLIB_PATH ?? "(unset)"}, ` + `ortLibraryPath=${ortLibraryPath ?? "(none)"}`);
|
|
10614
10730
|
if (useFastembedBackend) {
|
|
10615
|
-
env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ?
|
|
10731
|
+
env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join4(this.configOverrides.storage_dir, "semantic", "models") : join4(homedir5() || "", ".cache", "fastembed"));
|
|
10616
10732
|
if (process.env.ORT_DYLIB_PATH) {
|
|
10617
10733
|
this.logVia(`ORT_DYLIB_PATH inherited from parent env: ${process.env.ORT_DYLIB_PATH}`);
|
|
10618
10734
|
} else if (ortLibraryPath) {
|
|
@@ -10933,14 +11049,21 @@ class BinaryBridge {
|
|
|
10933
11049
|
};
|
|
10934
11050
|
entry.resolve = (response) => settle(shellOutcomeFromLateReply(response));
|
|
10935
11051
|
this.clock.clearTimeout(entry.timer);
|
|
10936
|
-
|
|
11052
|
+
const deadline = new RequestDeadline(this.waitClock, graceMs, false);
|
|
11053
|
+
const expire = () => {
|
|
10937
11054
|
if (this.pending.get(id) !== entry)
|
|
10938
11055
|
return;
|
|
11056
|
+
const remaining = deadline.remaining();
|
|
11057
|
+
if (remaining > 0) {
|
|
11058
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(remaining, DEADLINE_POLL_MS));
|
|
11059
|
+
return;
|
|
11060
|
+
}
|
|
10939
11061
|
this.pending.delete(id);
|
|
10940
11062
|
entry.onSettled?.();
|
|
10941
11063
|
this.warnVia(`Request "${entry.command}" (id=${id}) got no reply within ${graceMs}ms after its deadline`);
|
|
10942
11064
|
settle({ kind: "unknown" });
|
|
10943
|
-
}
|
|
11065
|
+
};
|
|
11066
|
+
entry.timer = this.clock.setTimeout(expire, Math.min(graceMs, DEADLINE_POLL_MS));
|
|
10944
11067
|
return true;
|
|
10945
11068
|
}
|
|
10946
11069
|
accountForBashTaskResponse(command, response) {
|
|
@@ -11188,7 +11311,7 @@ class BinaryBridge {
|
|
|
11188
11311
|
}
|
|
11189
11312
|
// ../aft-bridge/dist/cache-paths.js
|
|
11190
11313
|
import { homedir as homedir6 } from "node:os";
|
|
11191
|
-
import { join as
|
|
11314
|
+
import { join as join5 } from "node:path";
|
|
11192
11315
|
function homeDir2(env) {
|
|
11193
11316
|
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir6();
|
|
11194
11317
|
}
|
|
@@ -11196,20 +11319,20 @@ function getAftCacheRoot(env = process.env) {
|
|
|
11196
11319
|
if (env.AFT_CACHE_DIR)
|
|
11197
11320
|
return env.AFT_CACHE_DIR;
|
|
11198
11321
|
if (process.platform === "win32") {
|
|
11199
|
-
const base = env.LOCALAPPDATA || env.APPDATA ||
|
|
11200
|
-
return
|
|
11322
|
+
const base = env.LOCALAPPDATA || env.APPDATA || join5(homeDir2(env), "AppData", "Local");
|
|
11323
|
+
return join5(base, "aft");
|
|
11201
11324
|
}
|
|
11202
|
-
const base = env.XDG_CACHE_HOME ||
|
|
11203
|
-
return
|
|
11325
|
+
const base = env.XDG_CACHE_HOME || join5(homeDir2(env), ".cache");
|
|
11326
|
+
return join5(base, "aft");
|
|
11204
11327
|
}
|
|
11205
11328
|
function getAftBinaryCacheDir(env = process.env) {
|
|
11206
|
-
return
|
|
11329
|
+
return join5(getAftCacheRoot(env), "bin");
|
|
11207
11330
|
}
|
|
11208
11331
|
function getAftLspPackagesDir(env = process.env) {
|
|
11209
|
-
return
|
|
11332
|
+
return join5(getAftCacheRoot(env), "lsp-packages");
|
|
11210
11333
|
}
|
|
11211
11334
|
function getAftLspBinariesDir(env = process.env) {
|
|
11212
|
-
return
|
|
11335
|
+
return join5(getAftCacheRoot(env), "lsp-binaries");
|
|
11213
11336
|
}
|
|
11214
11337
|
// ../aft-bridge/dist/callgraph-format.js
|
|
11215
11338
|
var UNRESOLVED_SUMMARY_NAME_LIMIT = 10;
|
|
@@ -11309,15 +11432,57 @@ function hubSummaryLine(response, theme) {
|
|
|
11309
11432
|
const message = summary ? asString(summary.message) : undefined;
|
|
11310
11433
|
return message ? theme.fg("warning", message) : undefined;
|
|
11311
11434
|
}
|
|
11435
|
+
var ROOT_KINDS = ["main", "test", "attribute", "public_api", "no_caller_found"];
|
|
11436
|
+
var ROOT_LABELS = {
|
|
11437
|
+
main: "main",
|
|
11438
|
+
test: "test",
|
|
11439
|
+
attribute: "attribute",
|
|
11440
|
+
public_api: "public API",
|
|
11441
|
+
no_caller_found: "no caller found"
|
|
11442
|
+
};
|
|
11443
|
+
function rootCounted(kind, count) {
|
|
11444
|
+
const plural = count === 1 ? "" : "s";
|
|
11445
|
+
switch (kind) {
|
|
11446
|
+
case "main":
|
|
11447
|
+
return count === 1 ? "main" : `${count} main functions`;
|
|
11448
|
+
case "test":
|
|
11449
|
+
return `${count} test${plural}`;
|
|
11450
|
+
case "attribute":
|
|
11451
|
+
return `${count} function${plural} with an entry attribute`;
|
|
11452
|
+
case "public_api":
|
|
11453
|
+
return `${count} public API function${plural}`;
|
|
11454
|
+
default:
|
|
11455
|
+
return `${count} function${plural} with no caller found`;
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
function rootSummary(record) {
|
|
11459
|
+
const counts = asRecord(record.root_counts);
|
|
11460
|
+
if (!counts)
|
|
11461
|
+
return;
|
|
11462
|
+
const parts = ROOT_KINDS.flatMap((kind) => {
|
|
11463
|
+
const count = asNumber(counts[kind]);
|
|
11464
|
+
return count && count > 0 ? [rootCounted(kind, count)] : [];
|
|
11465
|
+
});
|
|
11466
|
+
return parts.length > 0 ? parts.join(", ") : undefined;
|
|
11467
|
+
}
|
|
11468
|
+
function rootTag(record) {
|
|
11469
|
+
const kind = asString(record.root);
|
|
11470
|
+
const label = kind ? ROOT_LABELS[kind] : undefined;
|
|
11471
|
+
if (!kind || !label)
|
|
11472
|
+
return;
|
|
11473
|
+
const attribute = asString(record.attribute);
|
|
11474
|
+
return kind === "attribute" && attribute ? `[root: attribute ${attribute}]` : `[root: ${label}]`;
|
|
11475
|
+
}
|
|
11312
11476
|
function renderTracePath(path, index, lines, theme) {
|
|
11313
11477
|
lines.push(`Path ${index + 1}`);
|
|
11478
|
+
const root = rootTag(path);
|
|
11314
11479
|
asRecords(path.hops).forEach((hop, hopIndex) => {
|
|
11315
11480
|
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
11316
11481
|
const file = shortenHomePath(asString(hop.file) ?? "(unknown file)");
|
|
11317
11482
|
const line = asNumber(hop.line);
|
|
11318
|
-
const
|
|
11483
|
+
const tag = hopIndex === 0 ? root ? ` ${theme.fg("warning", root)}` : "" : hop.exported === true ? " [exported]" : "";
|
|
11319
11484
|
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
11320
|
-
lines.push(treeLine(hopIndex + 1, `${symbol}${
|
|
11485
|
+
lines.push(treeLine(hopIndex + 1, `${symbol}${tag} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
|
|
11321
11486
|
});
|
|
11322
11487
|
}
|
|
11323
11488
|
function renderCallersGroupLines(group, theme, callbackLabel) {
|
|
@@ -11422,18 +11587,18 @@ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME, op
|
|
|
11422
11587
|
const hubSummary = hubSummaryLine(record, theme);
|
|
11423
11588
|
const totalPaths = asNumber(record.total_paths) ?? paths.length;
|
|
11424
11589
|
const totalPathsIsLowerBound = asBoolean(record.total_paths_is_lower_bound) ?? false;
|
|
11425
|
-
const
|
|
11590
|
+
const roots = rootSummary(record);
|
|
11426
11591
|
const sections = [
|
|
11427
11592
|
joinNonEmpty([
|
|
11428
11593
|
theme.fg("success", `${totalPathsIsLowerBound ? "at least " : ""}${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
|
|
11429
|
-
theme.fg("muted",
|
|
11594
|
+
roots ? theme.fg("muted", `roots: ${roots}`) : undefined,
|
|
11430
11595
|
warning
|
|
11431
11596
|
])
|
|
11432
11597
|
];
|
|
11433
11598
|
if (hubSummary)
|
|
11434
11599
|
sections.push(hubSummary);
|
|
11435
11600
|
if (paths.length === 0)
|
|
11436
|
-
sections.push(theme.fg("muted", "No
|
|
11601
|
+
sections.push(theme.fg("muted", "No paths found."));
|
|
11437
11602
|
paths.forEach((path, index) => {
|
|
11438
11603
|
const lines = [];
|
|
11439
11604
|
renderTracePath(path, index, lines, theme);
|
|
@@ -11466,13 +11631,17 @@ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME, op
|
|
|
11466
11631
|
sections.unshift(theme.fg("warning", testFilterNote));
|
|
11467
11632
|
if (hubSummary)
|
|
11468
11633
|
sections.push(hubSummary);
|
|
11634
|
+
const roots = rootSummary(record);
|
|
11635
|
+
if (roots)
|
|
11636
|
+
sections.push(theme.fg("muted", `Reaches ${roots}`));
|
|
11469
11637
|
if (callers.length === 0)
|
|
11470
11638
|
sections.push(theme.fg("muted", "No impacted callers found."));
|
|
11471
11639
|
callers.forEach((caller) => {
|
|
11472
11640
|
const file = shortenHomePath(asString(caller.caller_file) ?? "(unknown file)");
|
|
11473
11641
|
const symbol = asString(caller.caller_symbol) ?? "(unknown)";
|
|
11474
11642
|
const line = asNumber(caller.line) ?? 0;
|
|
11475
|
-
const
|
|
11643
|
+
const root = rootTag(caller);
|
|
11644
|
+
const entry = root ? ` ${theme.fg("warning", root)}` : "";
|
|
11476
11645
|
const nameMatch = nameMatchEdgeMarker(caller, theme);
|
|
11477
11646
|
const reference = caller.relation === "callback_reference" ? asString(record.callback_reference_label) ?? "" : "";
|
|
11478
11647
|
const expression = asString(caller.call_expression);
|
|
@@ -11652,7 +11821,7 @@ function isFindReplaceOnlyEdit(args) {
|
|
|
11652
11821
|
}
|
|
11653
11822
|
// ../aft-bridge/dist/config-error.js
|
|
11654
11823
|
import { homedir as homedir7 } from "node:os";
|
|
11655
|
-
import { isAbsolute as
|
|
11824
|
+
import { isAbsolute as isAbsolute2, join as join6 } from "node:path";
|
|
11656
11825
|
var CONFIG_ERROR_RESTART_NOTE = "AFT reads its config only at startup, so restart the host after fixing it.";
|
|
11657
11826
|
function formatConfigErrorMessage(detail) {
|
|
11658
11827
|
const trimmed = detail.trim();
|
|
@@ -11662,10 +11831,10 @@ function formatConfigErrorMessage(detail) {
|
|
|
11662
11831
|
function resolveSubcConnectionFilePath(raw, home = homedir7()) {
|
|
11663
11832
|
const trimmed = raw.trim();
|
|
11664
11833
|
if (trimmed.startsWith("~"))
|
|
11665
|
-
return
|
|
11666
|
-
if (
|
|
11834
|
+
return join6(home, trimmed.slice(1).replace(/^[/\\]/, ""));
|
|
11835
|
+
if (isAbsolute2(trimmed))
|
|
11667
11836
|
return trimmed;
|
|
11668
|
-
return
|
|
11837
|
+
return join6(home, trimmed);
|
|
11669
11838
|
}
|
|
11670
11839
|
function formatSubcConnectionMissingMessage(raw, resolved) {
|
|
11671
11840
|
return `subc.connection_file is set to "${raw.trim()}" (resolved: ${resolved}) but no subc ` + `connection file exists there. Start the Subconscious daemon, correct the path, ` + `or remove subc.connection_file from your user config to use the standalone bridge.`;
|
|
@@ -11726,7 +11895,7 @@ function stripHarnessSpecificConfigKeys(value, keys) {
|
|
|
11726
11895
|
}
|
|
11727
11896
|
// ../aft-bridge/dist/config-tiers.js
|
|
11728
11897
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
11729
|
-
import { resolve as
|
|
11898
|
+
import { resolve as resolve3 } from "node:path";
|
|
11730
11899
|
function readConfigTiers(opts) {
|
|
11731
11900
|
const tiers = [];
|
|
11732
11901
|
try {
|
|
@@ -11734,7 +11903,7 @@ function readConfigTiers(opts) {
|
|
|
11734
11903
|
const doc = readFileSync2(opts.userConfigPath, "utf-8");
|
|
11735
11904
|
tiers.push({
|
|
11736
11905
|
tier: "user",
|
|
11737
|
-
source:
|
|
11906
|
+
source: resolve3(opts.userConfigPath),
|
|
11738
11907
|
doc
|
|
11739
11908
|
});
|
|
11740
11909
|
}
|
|
@@ -11744,7 +11913,7 @@ function readConfigTiers(opts) {
|
|
|
11744
11913
|
const doc = readFileSync2(opts.projectConfigPath, "utf-8");
|
|
11745
11914
|
tiers.push({
|
|
11746
11915
|
tier: "project",
|
|
11747
|
-
source:
|
|
11916
|
+
source: resolve3(opts.projectConfigPath),
|
|
11748
11917
|
doc
|
|
11749
11918
|
});
|
|
11750
11919
|
}
|
|
@@ -11758,8 +11927,9 @@ function formatDroppedKeyWarnings(dropped) {
|
|
|
11758
11927
|
return dropped.map((item) => `Ignoring ${item.key} from ${item.tier} config (${item.reason})`);
|
|
11759
11928
|
}
|
|
11760
11929
|
// ../aft-bridge/dist/config-watch.js
|
|
11761
|
-
import {
|
|
11762
|
-
import {
|
|
11930
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
11931
|
+
import { closeSync as closeSync2, fstatSync, openSync as openSync2, readFileSync as readFileSync3, readSync as readSync2, realpathSync as realpathSync2, statSync as statSync3, watch } from "node:fs";
|
|
11932
|
+
import { basename, dirname as dirname2, resolve as resolve4 } from "node:path";
|
|
11763
11933
|
var CONFIG_WATCH_DEBOUNCE_MS = 150;
|
|
11764
11934
|
var CONFIG_LIVE_KEEP_NOTE = "AFT keeps using the last valid configuration until the file is fixed.";
|
|
11765
11935
|
function pathKey(name) {
|
|
@@ -11813,8 +11983,8 @@ function aftLiveConfigKeys(resolveBash) {
|
|
|
11813
11983
|
pathKey("restrict_to_project_root"),
|
|
11814
11984
|
pathKey("revision_reads"),
|
|
11815
11985
|
pathKey("inspect.diagnostics_timeout_ms"),
|
|
11816
|
-
pathKey("inspect.tier2_idle_minutes"),
|
|
11817
11986
|
pathKey("lsp.idle_minutes"),
|
|
11987
|
+
pathKey("lsp.max_servers"),
|
|
11818
11988
|
...[
|
|
11819
11989
|
"diagnostics",
|
|
11820
11990
|
"todos",
|
|
@@ -11885,6 +12055,29 @@ function readTextOrNull(path) {
|
|
|
11885
12055
|
var CONFIG_WATCH_REVALIDATE_MS = 2000;
|
|
11886
12056
|
var CONFIG_WATCH_REJECTED_RETRIES = 3;
|
|
11887
12057
|
var CONFIG_WATCH_MAX_DELAY_MS = 1000;
|
|
12058
|
+
var CONFIG_READ_LIMIT = 1024 * 1024;
|
|
12059
|
+
function contentFingerprint(path) {
|
|
12060
|
+
let fd;
|
|
12061
|
+
try {
|
|
12062
|
+
fd = openSync2(path, "r");
|
|
12063
|
+
const metadata = fstatSync(fd, { bigint: true });
|
|
12064
|
+
const bytes = Buffer.alloc(Math.min(Number(metadata.size), CONFIG_READ_LIMIT));
|
|
12065
|
+
let offset = 0;
|
|
12066
|
+
while (offset < bytes.length) {
|
|
12067
|
+
const read = readSync2(fd, bytes, offset, bytes.length - offset, null);
|
|
12068
|
+
if (read === 0)
|
|
12069
|
+
break;
|
|
12070
|
+
offset += read;
|
|
12071
|
+
}
|
|
12072
|
+
const hash = createHash2("sha256").update(bytes.subarray(0, offset)).digest("hex");
|
|
12073
|
+
return `${metadata.size}:${metadata.mtimeNs}:${hash}`;
|
|
12074
|
+
} catch {
|
|
12075
|
+
return null;
|
|
12076
|
+
} finally {
|
|
12077
|
+
if (fd !== undefined)
|
|
12078
|
+
closeSync2(fd);
|
|
12079
|
+
}
|
|
12080
|
+
}
|
|
11888
12081
|
function identityOf(dir) {
|
|
11889
12082
|
try {
|
|
11890
12083
|
const stat = statSync3(dir, { bigint: true });
|
|
@@ -11900,6 +12093,13 @@ function watchDirFor(file) {
|
|
|
11900
12093
|
dir = dirname2(dir);
|
|
11901
12094
|
return dir;
|
|
11902
12095
|
}
|
|
12096
|
+
function canonicalDirectory(dir) {
|
|
12097
|
+
try {
|
|
12098
|
+
return realpathSync2(dir);
|
|
12099
|
+
} catch {
|
|
12100
|
+
return resolve4(dir);
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
11903
12103
|
function watchAftConfigFiles(options) {
|
|
11904
12104
|
const debounceMs = options.debounceMs ?? CONFIG_WATCH_DEBOUNCE_MS;
|
|
11905
12105
|
const lastSeen = new Map;
|
|
@@ -11913,8 +12113,11 @@ function watchAftConfigFiles(options) {
|
|
|
11913
12113
|
let firstPendingAt = null;
|
|
11914
12114
|
let stopped = false;
|
|
11915
12115
|
const watchers = new Map;
|
|
12116
|
+
const targets = new Map;
|
|
12117
|
+
const fallbackSeen = new Map;
|
|
11916
12118
|
const watchImpl = options.watchImpl ?? watch;
|
|
11917
12119
|
let rejectedRetries = 0;
|
|
12120
|
+
let recheckAfterArming = false;
|
|
11918
12121
|
const check = () => {
|
|
11919
12122
|
if (retryTimer)
|
|
11920
12123
|
clearTimeout(retryTimer);
|
|
@@ -11923,7 +12126,8 @@ function watchAftConfigFiles(options) {
|
|
|
11923
12126
|
firstPendingAt = null;
|
|
11924
12127
|
if (stopped)
|
|
11925
12128
|
return;
|
|
11926
|
-
revalidate()
|
|
12129
|
+
if (revalidate())
|
|
12130
|
+
recheckAfterArming = true;
|
|
11927
12131
|
const seen = new Map;
|
|
11928
12132
|
for (const path of options.paths) {
|
|
11929
12133
|
const text = readTextOrNull(path);
|
|
@@ -11966,8 +12170,34 @@ function watchAftConfigFiles(options) {
|
|
|
11966
12170
|
timer.unref?.();
|
|
11967
12171
|
};
|
|
11968
12172
|
function revalidate() {
|
|
11969
|
-
const wanted = new Set
|
|
12173
|
+
const wanted = new Set;
|
|
12174
|
+
const wantedCanonical = new Set;
|
|
12175
|
+
const wantDirectory = (dir) => {
|
|
12176
|
+
const canonical = canonicalDirectory(dir);
|
|
12177
|
+
if (wantedCanonical.has(canonical))
|
|
12178
|
+
return;
|
|
12179
|
+
wantedCanonical.add(canonical);
|
|
12180
|
+
wanted.add(dir);
|
|
12181
|
+
};
|
|
12182
|
+
for (const path of options.paths)
|
|
12183
|
+
wantDirectory(watchDirFor(path));
|
|
11970
12184
|
let moved = false;
|
|
12185
|
+
for (const path of options.paths) {
|
|
12186
|
+
try {
|
|
12187
|
+
const resolved = realpathSync2(path);
|
|
12188
|
+
const target = resolved === resolve4(path) ? undefined : resolved;
|
|
12189
|
+
if (targets.get(path) !== target) {
|
|
12190
|
+
if (target)
|
|
12191
|
+
targets.set(path, target);
|
|
12192
|
+
else
|
|
12193
|
+
targets.delete(path);
|
|
12194
|
+
fallbackSeen.delete(path);
|
|
12195
|
+
moved = true;
|
|
12196
|
+
}
|
|
12197
|
+
} catch {}
|
|
12198
|
+
}
|
|
12199
|
+
for (const target of targets.values())
|
|
12200
|
+
wantDirectory(watchDirFor(target));
|
|
11971
12201
|
for (const [dir, entry] of watchers) {
|
|
11972
12202
|
if (!wanted.has(dir) || entry.stale || identityOf(dir) !== entry.identity) {
|
|
11973
12203
|
entry.watcher.close();
|
|
@@ -12006,14 +12236,29 @@ function watchAftConfigFiles(options) {
|
|
|
12006
12236
|
moved = true;
|
|
12007
12237
|
} catch {}
|
|
12008
12238
|
}
|
|
12239
|
+
const watchedCanonical = new Set(Array.from(watchers.keys(), canonicalDirectory));
|
|
12240
|
+
for (const [path, target] of targets) {
|
|
12241
|
+
if (watchedCanonical.has(canonicalDirectory(dirname2(target)))) {
|
|
12242
|
+
fallbackSeen.delete(path);
|
|
12243
|
+
continue;
|
|
12244
|
+
}
|
|
12245
|
+
const fingerprint = contentFingerprint(path);
|
|
12246
|
+
if (!fallbackSeen.has(path) || fallbackSeen.get(path) !== fingerprint)
|
|
12247
|
+
moved = true;
|
|
12248
|
+
fallbackSeen.set(path, fingerprint);
|
|
12249
|
+
}
|
|
12009
12250
|
return moved;
|
|
12010
12251
|
}
|
|
12011
|
-
revalidate();
|
|
12252
|
+
recheckAfterArming = revalidate();
|
|
12012
12253
|
if (options.checkAtStart)
|
|
12013
12254
|
check();
|
|
12014
12255
|
const revalidateTimer = setInterval(() => {
|
|
12015
|
-
if (
|
|
12256
|
+
if (stopped)
|
|
12257
|
+
return;
|
|
12258
|
+
const moved = revalidate();
|
|
12259
|
+
if (moved || recheckAfterArming)
|
|
12016
12260
|
schedule();
|
|
12261
|
+
recheckAfterArming = moved;
|
|
12017
12262
|
}, CONFIG_WATCH_REVALIDATE_MS);
|
|
12018
12263
|
revalidateTimer.unref?.();
|
|
12019
12264
|
return () => {
|
|
@@ -12056,6 +12301,10 @@ function aftLiveSecurityKeys(keys) {
|
|
|
12056
12301
|
return minutes(next) > minutes(current);
|
|
12057
12302
|
}
|
|
12058
12303
|
},
|
|
12304
|
+
{
|
|
12305
|
+
key: pick("lsp.max_servers"),
|
|
12306
|
+
loosens: (current, next) => (typeof next === "number" ? next : 8) > (typeof current === "number" ? current : 8)
|
|
12307
|
+
},
|
|
12059
12308
|
...[
|
|
12060
12309
|
"diagnostics",
|
|
12061
12310
|
"todos",
|
|
@@ -12162,10 +12411,10 @@ function startLiveConfigReload(options) {
|
|
|
12162
12411
|
return { reload, stop };
|
|
12163
12412
|
}
|
|
12164
12413
|
// ../aft-bridge/dist/downloader.js
|
|
12165
|
-
import { createHash as
|
|
12166
|
-
import { chmodSync, closeSync as
|
|
12414
|
+
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
12415
|
+
import { chmodSync, closeSync as closeSync3, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync as openSync3, readdirSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync4, unlinkSync as unlinkSync2, writeSync } from "node:fs";
|
|
12167
12416
|
import { hostname } from "node:os";
|
|
12168
|
-
import { join as
|
|
12417
|
+
import { join as join7 } from "node:path";
|
|
12169
12418
|
import { Readable } from "node:stream";
|
|
12170
12419
|
import { pipeline } from "node:stream/promises";
|
|
12171
12420
|
|
|
@@ -12281,7 +12530,7 @@ function getBinaryName() {
|
|
|
12281
12530
|
function getCachedBinaryPath(version) {
|
|
12282
12531
|
if (!version)
|
|
12283
12532
|
return null;
|
|
12284
|
-
const binaryPath =
|
|
12533
|
+
const binaryPath = join7(getAftBinaryCacheDir(), version, getBinaryName());
|
|
12285
12534
|
return existsSync3(binaryPath) ? binaryPath : null;
|
|
12286
12535
|
}
|
|
12287
12536
|
async function downloadBinary(version) {
|
|
@@ -12298,16 +12547,16 @@ async function downloadBinary(version) {
|
|
|
12298
12547
|
return null;
|
|
12299
12548
|
}
|
|
12300
12549
|
const tag = rawTag.startsWith("v") ? rawTag : `v${rawTag}`;
|
|
12301
|
-
const versionedCacheDir =
|
|
12550
|
+
const versionedCacheDir = join7(getAftBinaryCacheDir(), tag);
|
|
12302
12551
|
const binaryName = getBinaryName();
|
|
12303
|
-
const binaryPath =
|
|
12552
|
+
const binaryPath = join7(versionedCacheDir, binaryName);
|
|
12304
12553
|
if (existsSync3(binaryPath) && await isExpectedCachedBinary(binaryPath, tag)) {
|
|
12305
12554
|
return binaryPath;
|
|
12306
12555
|
}
|
|
12307
12556
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
|
|
12308
12557
|
const checksumUrl = `https://github.com/${REPO}/releases/download/${tag}/checksums.sha256`;
|
|
12309
12558
|
log(`Downloading AFT binary (${tag}) for ${platformKey}...`);
|
|
12310
|
-
const lockPath =
|
|
12559
|
+
const lockPath = join7(versionedCacheDir, ".download.lock");
|
|
12311
12560
|
let releaseLock = null;
|
|
12312
12561
|
let binaryController = null;
|
|
12313
12562
|
let checksumController = null;
|
|
@@ -12381,7 +12630,7 @@ async function downloadBinary(version) {
|
|
|
12381
12630
|
warn(`Checksum verification failed: checksums.sha256 found but no entry for ${assetName}. ` + "Binary download aborted for security reasons.");
|
|
12382
12631
|
return null;
|
|
12383
12632
|
}
|
|
12384
|
-
const hash =
|
|
12633
|
+
const hash = createHash3("sha256");
|
|
12385
12634
|
let bytesWritten = 0;
|
|
12386
12635
|
const guard = new TransformStream({
|
|
12387
12636
|
transform(chunk, controller) {
|
|
@@ -12533,7 +12782,7 @@ function sweepStaleDownloadTemps(versionedCacheDir, binaryName) {
|
|
|
12533
12782
|
if (!entry.isFile() || !entry.name.startsWith(tempPrefix) || !entry.name.endsWith(".tmp")) {
|
|
12534
12783
|
continue;
|
|
12535
12784
|
}
|
|
12536
|
-
const tempPath =
|
|
12785
|
+
const tempPath = join7(versionedCacheDir, entry.name);
|
|
12537
12786
|
const ageMs = Date.now() - statSync4(tempPath).mtimeMs;
|
|
12538
12787
|
if (Math.abs(ageMs) > DOWNLOAD_LOCK_STALE_MS)
|
|
12539
12788
|
unlinkSync2(tempPath);
|
|
@@ -12548,11 +12797,11 @@ async function acquireDownloadLock(lockPath, timing = {}) {
|
|
|
12548
12797
|
while (true) {
|
|
12549
12798
|
try {
|
|
12550
12799
|
const owner = createDownloadLockOwner();
|
|
12551
|
-
const fd =
|
|
12800
|
+
const fd = openSync3(lockPath, "wx");
|
|
12552
12801
|
try {
|
|
12553
12802
|
writeSync(fd, owner);
|
|
12554
12803
|
} finally {
|
|
12555
|
-
|
|
12804
|
+
closeSync3(fd);
|
|
12556
12805
|
}
|
|
12557
12806
|
return () => {
|
|
12558
12807
|
try {
|
|
@@ -12623,8 +12872,8 @@ import { appendFile, lstat, mkdir, open, rename, rm, stat } from "node:fs/promis
|
|
|
12623
12872
|
import { dirname as dirname3 } from "node:path";
|
|
12624
12873
|
|
|
12625
12874
|
// ../aft-bridge/dist/private-storage.js
|
|
12626
|
-
import { closeSync as
|
|
12627
|
-
import { isAbsolute as
|
|
12875
|
+
import { closeSync as closeSync4, constants, copyFileSync as copyFileSync2, fchmodSync, fstatSync as fstatSync2, lstatSync, mkdirSync as mkdirSync3, openSync as openSync4, readSync as readSync3, writeSync as writeSync2 } from "node:fs";
|
|
12876
|
+
import { isAbsolute as isAbsolute3, join as join8, parse, relative as relative2, resolve as resolve5, sep } from "node:path";
|
|
12628
12877
|
var PRIVATE_FILE_MODE = 384;
|
|
12629
12878
|
var PRIVATE_DIRECTORY_MODE = 448;
|
|
12630
12879
|
function privateMkdirSync(path, recursive = true) {
|
|
@@ -12638,13 +12887,13 @@ function privateCopyFileSync(source, destination) {
|
|
|
12638
12887
|
copyFileSync2(source, destination);
|
|
12639
12888
|
return;
|
|
12640
12889
|
}
|
|
12641
|
-
const input =
|
|
12890
|
+
const input = openSync4(source, "r");
|
|
12642
12891
|
let output;
|
|
12643
12892
|
try {
|
|
12644
|
-
output =
|
|
12893
|
+
output = openSync4(destination, "wx", PRIVATE_FILE_MODE);
|
|
12645
12894
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
12646
12895
|
for (;; ) {
|
|
12647
|
-
const bytes =
|
|
12896
|
+
const bytes = readSync3(input, buffer);
|
|
12648
12897
|
if (bytes === 0)
|
|
12649
12898
|
break;
|
|
12650
12899
|
let offset = 0;
|
|
@@ -12652,9 +12901,9 @@ function privateCopyFileSync(source, destination) {
|
|
|
12652
12901
|
offset += writeSync2(output, buffer, offset, bytes - offset);
|
|
12653
12902
|
}
|
|
12654
12903
|
} finally {
|
|
12655
|
-
|
|
12904
|
+
closeSync4(input);
|
|
12656
12905
|
if (output !== undefined)
|
|
12657
|
-
|
|
12906
|
+
closeSync4(output);
|
|
12658
12907
|
}
|
|
12659
12908
|
}
|
|
12660
12909
|
var warned = new Set;
|
|
@@ -12663,21 +12912,21 @@ function openPrivateStorageDir(root, path = root) {
|
|
|
12663
12912
|
if (!root || base === parse(base).root) {
|
|
12664
12913
|
throw new Error(`refusing filesystem root as private storage: ${base}`);
|
|
12665
12914
|
}
|
|
12666
|
-
const rel =
|
|
12667
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) ||
|
|
12915
|
+
const rel = relative2(base, resolve5(path));
|
|
12916
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute3(rel))
|
|
12668
12917
|
return;
|
|
12669
12918
|
privateMkdirSync(path);
|
|
12670
12919
|
if (process.platform === "win32")
|
|
12671
12920
|
return;
|
|
12672
12921
|
let current = base;
|
|
12673
12922
|
for (const component of ["", ...rel.split(sep).filter(Boolean)]) {
|
|
12674
|
-
current =
|
|
12923
|
+
current = join8(current, component);
|
|
12675
12924
|
let fd;
|
|
12676
12925
|
try {
|
|
12677
12926
|
if (lstatSync(current).isSymbolicLink())
|
|
12678
12927
|
return;
|
|
12679
|
-
fd =
|
|
12680
|
-
if ((
|
|
12928
|
+
fd = openSync4(current, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
12929
|
+
if ((fstatSync2(fd).mode & 63) !== 0)
|
|
12681
12930
|
fchmodSync(fd, PRIVATE_DIRECTORY_MODE);
|
|
12682
12931
|
} catch (error) {
|
|
12683
12932
|
if (!warned.has(current)) {
|
|
@@ -12688,7 +12937,7 @@ function openPrivateStorageDir(root, path = root) {
|
|
|
12688
12937
|
return;
|
|
12689
12938
|
} finally {
|
|
12690
12939
|
if (fd !== undefined)
|
|
12691
|
-
|
|
12940
|
+
closeSync4(fd);
|
|
12692
12941
|
}
|
|
12693
12942
|
}
|
|
12694
12943
|
}
|
|
@@ -12924,21 +13173,23 @@ ${reflowText}`;
|
|
|
12924
13173
|
}
|
|
12925
13174
|
return " Auto-formatted.";
|
|
12926
13175
|
}
|
|
12927
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13176
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/client.js
|
|
12928
13177
|
import { promises as fs3 } from "node:fs";
|
|
13178
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
12929
13179
|
import { debuglog } from "node:util";
|
|
12930
13180
|
|
|
12931
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13181
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
12932
13182
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
12933
13183
|
|
|
12934
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13184
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
12935
13185
|
import net from "node:net";
|
|
12936
13186
|
|
|
12937
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13187
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
12938
13188
|
var PROTOCOL_VERSION = 2;
|
|
12939
13189
|
var HEADER_LEN = 21;
|
|
12940
13190
|
var FROZEN_PREFIX_LEN = 5;
|
|
12941
13191
|
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
13192
|
+
var MAX_DELIVERED_FRAME_LEN = 4 * 1024 * 1024;
|
|
12942
13193
|
var FrameType;
|
|
12943
13194
|
(function(FrameType) {
|
|
12944
13195
|
FrameType[FrameType["Request"] = 0] = "Request";
|
|
@@ -12976,6 +13227,8 @@ var FLAG_PRIORITY_SHIFT = 1;
|
|
|
12976
13227
|
var FLAG_LAST = 8;
|
|
12977
13228
|
var FLAG_ADMISSION_MASK = 48;
|
|
12978
13229
|
var FLAG_ADMISSION_SHIFT = 4;
|
|
13230
|
+
var DAEMON_ORIGIN_FLAG = 64;
|
|
13231
|
+
var SUBSCRIPTION_FLAG = 128;
|
|
12979
13232
|
var FLAG_RESERVED_MASK = 0;
|
|
12980
13233
|
function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
|
|
12981
13234
|
let flags = 0;
|
|
@@ -13110,7 +13363,7 @@ function encodeFrame(frame) {
|
|
|
13110
13363
|
return output;
|
|
13111
13364
|
}
|
|
13112
13365
|
|
|
13113
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13366
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
13114
13367
|
class SocketClosedError extends Error {
|
|
13115
13368
|
}
|
|
13116
13369
|
|
|
@@ -13160,14 +13413,12 @@ class SubcSocket {
|
|
|
13160
13413
|
sock.on("data", (chunk) => {
|
|
13161
13414
|
this.chunks.push(chunk);
|
|
13162
13415
|
this.buffered += chunk.length;
|
|
13163
|
-
if (this.waiter) {
|
|
13416
|
+
if (this.waiter && this.waiter.extendOnProgress && this.buffered < this.waiter.need) {
|
|
13164
13417
|
if (this.waiter.immediate) {
|
|
13165
13418
|
clearImmediate(this.waiter.immediate);
|
|
13166
13419
|
this.waiter.immediate = null;
|
|
13167
13420
|
}
|
|
13168
|
-
|
|
13169
|
-
this.armWaiterTimer(this.waiter);
|
|
13170
|
-
}
|
|
13421
|
+
this.armWaiterTimer(this.waiter);
|
|
13171
13422
|
}
|
|
13172
13423
|
this.tryServe();
|
|
13173
13424
|
});
|
|
@@ -13184,12 +13435,18 @@ class SubcSocket {
|
|
|
13184
13435
|
return this.sock.localPort ?? null;
|
|
13185
13436
|
}
|
|
13186
13437
|
static connect(host, port, deadlineMs) {
|
|
13438
|
+
return SubcSocket.connectTo({ host, port }, `${host}:${port}`, deadlineMs);
|
|
13439
|
+
}
|
|
13440
|
+
static connectLocal(path, deadlineMs) {
|
|
13441
|
+
return SubcSocket.connectTo({ path }, path, deadlineMs);
|
|
13442
|
+
}
|
|
13443
|
+
static connectTo(options, endpoint, deadlineMs) {
|
|
13187
13444
|
return new Promise((resolve, reject) => {
|
|
13188
|
-
const sock = net.connect(
|
|
13445
|
+
const sock = net.connect(options);
|
|
13189
13446
|
sock.setNoDelay(true);
|
|
13190
13447
|
const timer = setTimeout(() => {
|
|
13191
13448
|
sock.destroy();
|
|
13192
|
-
reject(new SocketTimeoutError(`timed out connecting to ${
|
|
13449
|
+
reject(new SocketTimeoutError(`timed out connecting to ${endpoint}`));
|
|
13193
13450
|
}, Math.max(0, deadlineMs - Date.now()));
|
|
13194
13451
|
sock.once("connect", () => {
|
|
13195
13452
|
clearTimeout(timer);
|
|
@@ -13406,7 +13663,7 @@ class SubcSocket {
|
|
|
13406
13663
|
}
|
|
13407
13664
|
}
|
|
13408
13665
|
|
|
13409
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13666
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
13410
13667
|
var NONCE_LEN = 32;
|
|
13411
13668
|
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
13412
13669
|
var SERVER_PROOF_DOMAIN = "subc-server-v1";
|
|
@@ -13480,7 +13737,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
|
|
|
13480
13737
|
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
13481
13738
|
}
|
|
13482
13739
|
|
|
13483
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13740
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
13484
13741
|
import { promises as fs2 } from "node:fs";
|
|
13485
13742
|
var SCHEMA_VERSION = 1;
|
|
13486
13743
|
var MIN_KEY_LEN = 32;
|
|
@@ -13527,7 +13784,10 @@ function verifyOwnerOnly(path, stat, expectedUid) {
|
|
|
13527
13784
|
async function readConnectionFile(path) {
|
|
13528
13785
|
return readConnectionFileForUid(path, process.geteuid?.());
|
|
13529
13786
|
}
|
|
13530
|
-
async function
|
|
13787
|
+
async function readConnectionFileWithLocalEndpoint(path) {
|
|
13788
|
+
return readConnectionFileForUid(path, process.geteuid?.(), true);
|
|
13789
|
+
}
|
|
13790
|
+
async function readConnectionFileForUid(path, expectedUid, includeLocalEndpoint = false) {
|
|
13531
13791
|
const handle = await fs2.open(path, "r");
|
|
13532
13792
|
let raw;
|
|
13533
13793
|
try {
|
|
@@ -13566,13 +13826,26 @@ async function readConnectionFileForUid(path, expectedUid) {
|
|
|
13566
13826
|
daemonVer: parsed.daemon_ver ?? ""
|
|
13567
13827
|
};
|
|
13568
13828
|
validate(info);
|
|
13829
|
+
if (includeLocalEndpoint && parsed.local_endpoint != null) {
|
|
13830
|
+
const endpoint = parsed.local_endpoint;
|
|
13831
|
+
if (endpoint.kind === "unix" && typeof endpoint.path === "string" && endpoint.path.length > 0) {
|
|
13832
|
+
info.localEndpoint = { kind: "unix", path: endpoint.path };
|
|
13833
|
+
} else if (endpoint.kind === "pipe" && typeof endpoint.name === "string" && endpoint.name.length > 0) {
|
|
13834
|
+
info.localEndpoint = { kind: "pipe", name: endpoint.name };
|
|
13835
|
+
} else {
|
|
13836
|
+
throw new ConnectionFileError("connection file local_endpoint must name a unix path or pipe name");
|
|
13837
|
+
}
|
|
13838
|
+
}
|
|
13569
13839
|
return info;
|
|
13570
13840
|
}
|
|
13571
13841
|
|
|
13572
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13573
|
-
import { closeSync as
|
|
13842
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/launch-nonce.js
|
|
13843
|
+
import { closeSync as closeSync5, fstatSync as fstatSync3, readSync as readSync4 } from "node:fs";
|
|
13844
|
+
import { connect } from "node:net";
|
|
13574
13845
|
var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
13575
13846
|
var SUBC_LAUNCH_NONCE_FD_ENV = "SUBC_LAUNCH_NONCE_FD";
|
|
13847
|
+
var SUBC_LAUNCH_NONCE_PIPE_ENV = "SUBC_LAUNCH_NONCE_PIPE";
|
|
13848
|
+
var SUBC_LAUNCH_NONCE_PIPE_FALLBACK_ENV = "SUBC_LAUNCH_NONCE_PIPE_FALLBACK";
|
|
13576
13849
|
class LaunchNonceError extends Error {
|
|
13577
13850
|
name = "LaunchNonceError";
|
|
13578
13851
|
kind;
|
|
@@ -13591,9 +13864,21 @@ class LaunchNonceError extends Error {
|
|
|
13591
13864
|
this.foundInode = details.foundInode;
|
|
13592
13865
|
}
|
|
13593
13866
|
}
|
|
13594
|
-
function
|
|
13595
|
-
|
|
13596
|
-
|
|
13867
|
+
function isLaunchNonceError(error) {
|
|
13868
|
+
return error instanceof Error && error.name === "LaunchNonceError" && typeof error.kind === "string";
|
|
13869
|
+
}
|
|
13870
|
+
async function launchNonceAsync() {
|
|
13871
|
+
const cell = processCell();
|
|
13872
|
+
return unwrap(await (cell.resultAsync?.() ?? cell.result()));
|
|
13873
|
+
}
|
|
13874
|
+
async function launchNonceOrUndefinedAsync() {
|
|
13875
|
+
try {
|
|
13876
|
+
return await launchNonceAsync();
|
|
13877
|
+
} catch (error) {
|
|
13878
|
+
if (isLaunchNonceError(error))
|
|
13879
|
+
return;
|
|
13880
|
+
throw error;
|
|
13881
|
+
}
|
|
13597
13882
|
}
|
|
13598
13883
|
var PROCESS_CELL_KEY = Symbol.for("@cortexkit/subc-client/launch-nonce/v1");
|
|
13599
13884
|
function processCell() {
|
|
@@ -13613,20 +13898,60 @@ function unwrap(result) {
|
|
|
13613
13898
|
|
|
13614
13899
|
class LaunchNonceCell {
|
|
13615
13900
|
cached;
|
|
13901
|
+
pending;
|
|
13616
13902
|
descriptorReads = 0;
|
|
13617
13903
|
lookup;
|
|
13618
13904
|
platform;
|
|
13619
13905
|
stat;
|
|
13620
|
-
|
|
13906
|
+
pipeReader;
|
|
13907
|
+
constructor(lookup, platform = process.platform, stat = (fd) => fstatSync3(fd, { bigint: true }), pipeReader = readLaunchNoncePipe) {
|
|
13621
13908
|
this.lookup = lookup;
|
|
13622
13909
|
this.platform = platform;
|
|
13623
13910
|
this.stat = stat;
|
|
13911
|
+
this.pipeReader = pipeReader;
|
|
13624
13912
|
}
|
|
13625
13913
|
result() {
|
|
13914
|
+
if (this.cached === undefined && this.platform === "win32" && this.lookup(SUBC_LAUNCH_NONCE_PIPE_ENV) !== undefined) {
|
|
13915
|
+
const fallback = this.permittedPipeFallback();
|
|
13916
|
+
if (fallback === undefined) {
|
|
13917
|
+
throw new LaunchNonceError("PipeNeedsAsync", "call launchNonceAsync() before the synchronous reader when SUBC_LAUNCH_NONCE_PIPE is set");
|
|
13918
|
+
}
|
|
13919
|
+
this.cached = { ok: true, nonce: fallback };
|
|
13920
|
+
}
|
|
13626
13921
|
if (this.cached === undefined)
|
|
13627
13922
|
this.cached = this.read();
|
|
13628
13923
|
return this.cached;
|
|
13629
13924
|
}
|
|
13925
|
+
async resultAsync() {
|
|
13926
|
+
if (this.cached !== undefined)
|
|
13927
|
+
return this.cached;
|
|
13928
|
+
if (this.pending !== undefined)
|
|
13929
|
+
return this.pending;
|
|
13930
|
+
const named = this.platform === "win32" ? this.lookup(SUBC_LAUNCH_NONCE_PIPE_ENV) : undefined;
|
|
13931
|
+
if (named === undefined)
|
|
13932
|
+
return this.result();
|
|
13933
|
+
const fallback = this.permittedPipeFallback();
|
|
13934
|
+
this.pending = this.pipeReader(named).then((nonce) => ({ ok: true, nonce: fallback !== undefined && nonce.value !== fallback.value ? fallback : nonce }), (error) => {
|
|
13935
|
+
if (fallback !== undefined)
|
|
13936
|
+
return { ok: true, nonce: fallback };
|
|
13937
|
+
if (!isLaunchNonceError(error))
|
|
13938
|
+
throw error;
|
|
13939
|
+
return { ok: false, error };
|
|
13940
|
+
}).then((result) => {
|
|
13941
|
+
this.cached ??= result;
|
|
13942
|
+
return this.cached;
|
|
13943
|
+
});
|
|
13944
|
+
return this.pending;
|
|
13945
|
+
}
|
|
13946
|
+
permittedPipeFallback() {
|
|
13947
|
+
if (this.lookup(SUBC_LAUNCH_NONCE_PIPE_FALLBACK_ENV) !== "env")
|
|
13948
|
+
return;
|
|
13949
|
+
const value = this.lookup(SUBC_LAUNCH_NONCE_ENV);
|
|
13950
|
+
return value ? makeLaunchNonce(value, "env") : undefined;
|
|
13951
|
+
}
|
|
13952
|
+
async getAsync() {
|
|
13953
|
+
return unwrap(await this.resultAsync());
|
|
13954
|
+
}
|
|
13630
13955
|
get() {
|
|
13631
13956
|
return unwrap(this.result());
|
|
13632
13957
|
}
|
|
@@ -13665,7 +13990,7 @@ class LaunchNonceCell {
|
|
|
13665
13990
|
const chunk = Buffer.alloc(256);
|
|
13666
13991
|
let got;
|
|
13667
13992
|
try {
|
|
13668
|
-
got =
|
|
13993
|
+
got = readSync4(fd, chunk, 0, chunk.length, null);
|
|
13669
13994
|
} catch (error) {
|
|
13670
13995
|
if (isWouldBlock(error))
|
|
13671
13996
|
throw emptyError(fd);
|
|
@@ -13677,7 +14002,7 @@ class LaunchNonceCell {
|
|
|
13677
14002
|
const parts = [Buffer.from(chunk.subarray(0, got))];
|
|
13678
14003
|
try {
|
|
13679
14004
|
for (;; ) {
|
|
13680
|
-
const n =
|
|
14005
|
+
const n = readSync4(fd, chunk, 0, chunk.length, null);
|
|
13681
14006
|
if (n === 0)
|
|
13682
14007
|
break;
|
|
13683
14008
|
parts.push(Buffer.from(chunk.subarray(0, n)));
|
|
@@ -13754,11 +14079,201 @@ function errnoOf(error) {
|
|
|
13754
14079
|
}
|
|
13755
14080
|
function closeQuietly(fd) {
|
|
13756
14081
|
try {
|
|
13757
|
-
|
|
14082
|
+
closeSync5(fd);
|
|
13758
14083
|
} catch {}
|
|
13759
14084
|
}
|
|
14085
|
+
async function readLaunchNoncePipe(name, connector = (path) => connect(path), openWindowMs = 500, readWindowMs = 2000) {
|
|
14086
|
+
const deadline = Date.now() + openWindowMs;
|
|
14087
|
+
for (;; ) {
|
|
14088
|
+
try {
|
|
14089
|
+
const bytes = await pipeAttempt(name, connector, Math.max(1, deadline - Date.now()), readWindowMs);
|
|
14090
|
+
if (bytes.length === 0)
|
|
14091
|
+
throw new LaunchNonceError("PipeEmpty", "the launch nonce named pipe is empty");
|
|
14092
|
+
let value;
|
|
14093
|
+
try {
|
|
14094
|
+
value = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
14095
|
+
} catch {
|
|
14096
|
+
throw new LaunchNonceError("PipeNotUtf8", "the launch nonce named pipe held bytes that are not UTF-8");
|
|
14097
|
+
}
|
|
14098
|
+
return makeLaunchNonce(value, "pipe");
|
|
14099
|
+
} catch (error) {
|
|
14100
|
+
if (isLaunchNonceError(error))
|
|
14101
|
+
throw error;
|
|
14102
|
+
const busy = error?.code === "EBUSY" || errnoOf(error) === 231;
|
|
14103
|
+
if (!busy || Date.now() >= deadline) {
|
|
14104
|
+
throw new LaunchNonceError("PipeNotOpen", "SUBC_LAUNCH_NONCE_PIPE names a pipe that could not be opened", { errno: errnoOf(error) });
|
|
14105
|
+
}
|
|
14106
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(10, Math.max(1, deadline - Date.now()))));
|
|
14107
|
+
}
|
|
14108
|
+
}
|
|
14109
|
+
}
|
|
14110
|
+
function pipeAttempt(name, connector, openWindowMs, readWindowMs) {
|
|
14111
|
+
return new Promise((resolve, reject) => {
|
|
14112
|
+
let socket;
|
|
14113
|
+
try {
|
|
14114
|
+
socket = connector(name);
|
|
14115
|
+
} catch (error) {
|
|
14116
|
+
reject(error);
|
|
14117
|
+
return;
|
|
14118
|
+
}
|
|
14119
|
+
let connected = false;
|
|
14120
|
+
let settled = false;
|
|
14121
|
+
let size = 0;
|
|
14122
|
+
const chunks = [];
|
|
14123
|
+
let timer = setTimeout(() => finish(new LaunchNonceError("PipeNotOpen", "launch nonce pipe open timed out")), openWindowMs);
|
|
14124
|
+
function finish(error) {
|
|
14125
|
+
if (settled)
|
|
14126
|
+
return;
|
|
14127
|
+
settled = true;
|
|
14128
|
+
clearTimeout(timer);
|
|
14129
|
+
socket.destroy();
|
|
14130
|
+
if (error !== undefined)
|
|
14131
|
+
reject(error);
|
|
14132
|
+
else
|
|
14133
|
+
resolve(Buffer.concat(chunks));
|
|
14134
|
+
}
|
|
14135
|
+
socket.once("connect", () => {
|
|
14136
|
+
connected = true;
|
|
14137
|
+
clearTimeout(timer);
|
|
14138
|
+
timer = setTimeout(() => finish(new LaunchNonceError("PipeUnreadable", "launch nonce pipe read timed out")), readWindowMs);
|
|
14139
|
+
});
|
|
14140
|
+
socket.on("data", (chunk) => {
|
|
14141
|
+
size += chunk.length;
|
|
14142
|
+
if (size > 4096) {
|
|
14143
|
+
finish(new LaunchNonceError("PipeUnreadable", "launch nonce pipe exceeded its byte limit"));
|
|
14144
|
+
return;
|
|
14145
|
+
}
|
|
14146
|
+
chunks.push(Buffer.from(chunk));
|
|
14147
|
+
});
|
|
14148
|
+
socket.once("end", () => finish());
|
|
14149
|
+
socket.once("error", (error) => finish(connected ? new LaunchNonceError("PipeUnreadable", "could not read the launch nonce from the named pipe", { errno: errnoOf(error) }) : error));
|
|
14150
|
+
socket.once("close", () => {
|
|
14151
|
+
if (!settled)
|
|
14152
|
+
finish(new LaunchNonceError("PipeUnreadable", "launch nonce pipe closed before EOF"));
|
|
14153
|
+
});
|
|
14154
|
+
});
|
|
14155
|
+
}
|
|
13760
14156
|
|
|
13761
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
14157
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/request-gate.js
|
|
14158
|
+
class Lane {
|
|
14159
|
+
limit;
|
|
14160
|
+
used = 0;
|
|
14161
|
+
waiters = [];
|
|
14162
|
+
failure;
|
|
14163
|
+
constructor(limit) {
|
|
14164
|
+
this.limit = limit;
|
|
14165
|
+
}
|
|
14166
|
+
acquire(timeoutMs, signal) {
|
|
14167
|
+
if (this.failure)
|
|
14168
|
+
return Promise.reject(this.failure);
|
|
14169
|
+
if (signal?.aborted)
|
|
14170
|
+
return Promise.reject(new Error("request cancelled before write"));
|
|
14171
|
+
if (timeoutMs !== undefined && timeoutMs <= 0)
|
|
14172
|
+
return Promise.reject(new Error("request deadline elapsed waiting for a slot"));
|
|
14173
|
+
if (this.used < this.limit && this.waiters.length === 0) {
|
|
14174
|
+
this.used++;
|
|
14175
|
+
return Promise.resolve(this.releaser());
|
|
14176
|
+
}
|
|
14177
|
+
return new Promise((resolve, reject) => {
|
|
14178
|
+
let timer;
|
|
14179
|
+
const clean = () => {
|
|
14180
|
+
if (timer !== undefined)
|
|
14181
|
+
clearTimeout(timer);
|
|
14182
|
+
signal?.removeEventListener("abort", abort);
|
|
14183
|
+
};
|
|
14184
|
+
const waiter = {
|
|
14185
|
+
grant: () => {
|
|
14186
|
+
clean();
|
|
14187
|
+
resolve(this.releaser());
|
|
14188
|
+
},
|
|
14189
|
+
fail: (error) => {
|
|
14190
|
+
clean();
|
|
14191
|
+
reject(error);
|
|
14192
|
+
}
|
|
14193
|
+
};
|
|
14194
|
+
const abort = () => {
|
|
14195
|
+
const index = this.waiters.indexOf(waiter);
|
|
14196
|
+
if (index < 0)
|
|
14197
|
+
return;
|
|
14198
|
+
this.waiters.splice(index, 1);
|
|
14199
|
+
waiter.fail(new Error("request cancelled before write"));
|
|
14200
|
+
};
|
|
14201
|
+
this.waiters.push(waiter);
|
|
14202
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
14203
|
+
if (timeoutMs !== undefined)
|
|
14204
|
+
timer = setTimeout(() => {
|
|
14205
|
+
const index = this.waiters.indexOf(waiter);
|
|
14206
|
+
if (index < 0)
|
|
14207
|
+
return;
|
|
14208
|
+
this.waiters.splice(index, 1);
|
|
14209
|
+
waiter.fail(new Error("request deadline elapsed waiting for a slot"));
|
|
14210
|
+
}, timeoutMs);
|
|
14211
|
+
});
|
|
14212
|
+
}
|
|
14213
|
+
close(error) {
|
|
14214
|
+
this.failure = error;
|
|
14215
|
+
for (const waiter of this.waiters.splice(0))
|
|
14216
|
+
waiter.fail(error);
|
|
14217
|
+
}
|
|
14218
|
+
releaser() {
|
|
14219
|
+
let released = false;
|
|
14220
|
+
return () => {
|
|
14221
|
+
if (released)
|
|
14222
|
+
return;
|
|
14223
|
+
released = true;
|
|
14224
|
+
const waiter = this.waiters.shift();
|
|
14225
|
+
if (waiter)
|
|
14226
|
+
waiter.grant();
|
|
14227
|
+
else
|
|
14228
|
+
this.used--;
|
|
14229
|
+
};
|
|
14230
|
+
}
|
|
14231
|
+
}
|
|
14232
|
+
|
|
14233
|
+
class RequestGate {
|
|
14234
|
+
total;
|
|
14235
|
+
watches;
|
|
14236
|
+
slots = new Set;
|
|
14237
|
+
closed = new AbortController;
|
|
14238
|
+
get signal() {
|
|
14239
|
+
return this.closed.signal;
|
|
14240
|
+
}
|
|
14241
|
+
constructor(requestWindow, watchWindow) {
|
|
14242
|
+
this.total = new Lane(requestWindow);
|
|
14243
|
+
if (watchWindow !== undefined)
|
|
14244
|
+
this.watches = new Lane(watchWindow);
|
|
14245
|
+
}
|
|
14246
|
+
async acquire(watch, timeoutMs, signal) {
|
|
14247
|
+
const deadline = timeoutMs === undefined ? undefined : Date.now() + timeoutMs;
|
|
14248
|
+
const watchRelease = watch && this.watches ? await this.watches.acquire(timeoutMs, signal) : undefined;
|
|
14249
|
+
try {
|
|
14250
|
+
const totalRelease = await this.total.acquire(deadline === undefined ? undefined : deadline - Date.now(), signal);
|
|
14251
|
+
const slot = {
|
|
14252
|
+
inFlight: false,
|
|
14253
|
+
release: () => {
|
|
14254
|
+
if (!this.slots.delete(slot))
|
|
14255
|
+
return;
|
|
14256
|
+
totalRelease();
|
|
14257
|
+
watchRelease?.();
|
|
14258
|
+
}
|
|
14259
|
+
};
|
|
14260
|
+
this.slots.add(slot);
|
|
14261
|
+
return slot;
|
|
14262
|
+
} catch (error) {
|
|
14263
|
+
watchRelease?.();
|
|
14264
|
+
throw error;
|
|
14265
|
+
}
|
|
14266
|
+
}
|
|
14267
|
+
close(error) {
|
|
14268
|
+
this.total.close(error);
|
|
14269
|
+
this.watches?.close(error);
|
|
14270
|
+
this.closed.abort(error);
|
|
14271
|
+
for (const slot of this.slots)
|
|
14272
|
+
slot.release();
|
|
14273
|
+
}
|
|
14274
|
+
}
|
|
14275
|
+
|
|
14276
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
13762
14277
|
var connectionToken = new WeakMap;
|
|
13763
14278
|
var reverseRequests = new WeakMap;
|
|
13764
14279
|
|
|
@@ -13851,10 +14366,13 @@ function sameRouteHandle(left, right) {
|
|
|
13851
14366
|
return left === right;
|
|
13852
14367
|
}
|
|
13853
14368
|
|
|
13854
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
14369
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.24.5/node_modules/@cortexkit/subc-client/dist/client.js
|
|
13855
14370
|
var debug = debuglog("subc-client");
|
|
13856
14371
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
13857
14372
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
14373
|
+
var DEFAULT_SLEEP_GRACE_MS = 15000;
|
|
14374
|
+
var SLEEP_DETECTION_THRESHOLD_MS = 2000;
|
|
14375
|
+
var MIN_REQUEST_BUDGET_MS = 1;
|
|
13858
14376
|
var TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
13859
14377
|
var LIVENESS_PROBE_WINDOW_MS = 2000;
|
|
13860
14378
|
var REQUEST_DEADLINE_MARKER = "request_deadline";
|
|
@@ -13890,6 +14408,14 @@ class SubcCallError extends Error {
|
|
|
13890
14408
|
}
|
|
13891
14409
|
}
|
|
13892
14410
|
|
|
14411
|
+
class DaemonUnavailableError extends SubcCallError {
|
|
14412
|
+
recovery = "user_action_required";
|
|
14413
|
+
constructor(cause) {
|
|
14414
|
+
super("not_sent", "CortexKit's daemon is not running or can't be reached on this computer, so this tool call was not sent. Retrying won't start it. Stop calling CortexKit tools and ask the user to start the daemon (run `ck setup`, or restart the CortexKit service), then try again after they confirm it is running.", errorCode(cause), cause);
|
|
14415
|
+
this.name = "DaemonUnavailableError";
|
|
14416
|
+
}
|
|
14417
|
+
}
|
|
14418
|
+
|
|
13893
14419
|
class SubcError extends Error {
|
|
13894
14420
|
code;
|
|
13895
14421
|
detail;
|
|
@@ -14014,6 +14540,14 @@ class SubcClient {
|
|
|
14014
14540
|
reconnecting = null;
|
|
14015
14541
|
generation = 1;
|
|
14016
14542
|
routeOpenGateInstance;
|
|
14543
|
+
requestGates;
|
|
14544
|
+
wireSlots;
|
|
14545
|
+
get gates() {
|
|
14546
|
+
return this.requestGates ??= new Map;
|
|
14547
|
+
}
|
|
14548
|
+
get slots() {
|
|
14549
|
+
return this.wireSlots ??= new Map;
|
|
14550
|
+
}
|
|
14017
14551
|
readerActive = false;
|
|
14018
14552
|
constructor(sock, currentConn, opts) {
|
|
14019
14553
|
this.sock = sock;
|
|
@@ -14029,8 +14563,15 @@ class SubcClient {
|
|
|
14029
14563
|
}
|
|
14030
14564
|
static async connect(opts) {
|
|
14031
14565
|
const normalized = normalizeConnectOptions(opts);
|
|
14032
|
-
|
|
14033
|
-
|
|
14566
|
+
try {
|
|
14567
|
+
const opened = await SubcClient.openConnection(normalized);
|
|
14568
|
+
return new SubcClient(opened.sock, opened.conn, normalized);
|
|
14569
|
+
} catch (err) {
|
|
14570
|
+
if (errorCode(err) === "ENOENT" || errorCode(err) === "ECONNREFUSED") {
|
|
14571
|
+
throw new DaemonUnavailableError(err);
|
|
14572
|
+
}
|
|
14573
|
+
throw err;
|
|
14574
|
+
}
|
|
14034
14575
|
}
|
|
14035
14576
|
async catalogList(moduleId) {
|
|
14036
14577
|
const body = this.encode(moduleId === undefined ? { op: "catalog.list" } : { op: "catalog.list", module_id: moduleId });
|
|
@@ -14066,17 +14607,23 @@ class SubcClient {
|
|
|
14066
14607
|
}
|
|
14067
14608
|
async sendRouteOpen(target, identity, opts = {}) {
|
|
14068
14609
|
const scope = validatedRouteScope(opts.scope);
|
|
14069
|
-
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
14610
|
+
const consumerIdentity = await routeOpenConsumerIdentity(opts);
|
|
14070
14611
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry;
|
|
14071
14612
|
reverseRequests.seal();
|
|
14072
14613
|
const consumerCapabilities = reverseRequests.capabilities();
|
|
14073
14614
|
const body = this.encode({
|
|
14074
14615
|
op: "route.open",
|
|
14616
|
+
busy_refusals: true,
|
|
14075
14617
|
target,
|
|
14076
14618
|
identity,
|
|
14077
14619
|
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
14078
14620
|
...consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {},
|
|
14079
|
-
...scope ? { scope: {
|
|
14621
|
+
...scope ? { scope: {
|
|
14622
|
+
owner: scope.owner,
|
|
14623
|
+
ref: scope.ref,
|
|
14624
|
+
scope_epoch: scope.scopeEpoch,
|
|
14625
|
+
...scope.bindProof !== undefined ? { bind_proof: scope.bindProof } : {}
|
|
14626
|
+
} } : {}
|
|
14080
14627
|
});
|
|
14081
14628
|
let installed = null;
|
|
14082
14629
|
const install = (frame) => {
|
|
@@ -14087,6 +14634,8 @@ class SubcClient {
|
|
|
14087
14634
|
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
14088
14635
|
}
|
|
14089
14636
|
installed = this.installRoute(parsed.route_channel, parsed.route_epoch, reverseRequests);
|
|
14637
|
+
if (parsed.request_window !== undefined)
|
|
14638
|
+
this.gates.set(installed, new RequestGate(parsed.request_window, parsed.watch_window));
|
|
14090
14639
|
this.routeModules.set(parsed.route_channel, target.module_id);
|
|
14091
14640
|
return true;
|
|
14092
14641
|
};
|
|
@@ -14116,7 +14665,7 @@ class SubcClient {
|
|
|
14116
14665
|
const bytes = binary ? requireBinaryBody(body) : body instanceof Uint8Array ? body : this.encode(body);
|
|
14117
14666
|
const priority = opts.priority ?? Priority.Interactive;
|
|
14118
14667
|
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
14119
|
-
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, undefined, undefined, binary, opts.signal);
|
|
14668
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, undefined, undefined, binary, opts.signal, validatedSleepGraceMs(opts.sleepGraceMs ?? this.opts.sleepGraceMs ?? DEFAULT_SLEEP_GRACE_MS));
|
|
14120
14669
|
return this.decodeReply(reply);
|
|
14121
14670
|
}
|
|
14122
14671
|
async call(moduleId, method, params, opts = {}) {
|
|
@@ -14132,10 +14681,12 @@ class SubcClient {
|
|
|
14132
14681
|
}
|
|
14133
14682
|
async managedCall(moduleId, body, opts) {
|
|
14134
14683
|
let retriedUnknownChannel = false;
|
|
14684
|
+
const startedAt = opts.timeoutMs === undefined ? undefined : this.opts.now();
|
|
14685
|
+
let attemptOpts = opts;
|
|
14135
14686
|
for (;; ) {
|
|
14136
|
-
const routeHandle = await this.cachedRouteHandle(moduleId,
|
|
14687
|
+
const routeHandle = await this.cachedRouteHandle(moduleId, attemptOpts);
|
|
14137
14688
|
try {
|
|
14138
|
-
return await this.managedRequest(routeHandle, body,
|
|
14689
|
+
return await this.managedRequest(routeHandle, body, attemptOpts);
|
|
14139
14690
|
} catch (err) {
|
|
14140
14691
|
if (!(err instanceof SubcCallError))
|
|
14141
14692
|
throw this.terminalCallError("managed call failed", err);
|
|
@@ -14145,6 +14696,15 @@ class SubcClient {
|
|
|
14145
14696
|
this.evictRouteHandle(routeHandle);
|
|
14146
14697
|
continue;
|
|
14147
14698
|
}
|
|
14699
|
+
if (isDataPlaneDrainRefusal(err.code) && !retriedUnknownChannel && !this.closeStarted && !opts.signal?.aborted) {
|
|
14700
|
+
const remainingMs = opts.timeoutMs === undefined || startedAt === undefined ? undefined : opts.timeoutMs - (this.opts.now() - startedAt);
|
|
14701
|
+
if (remainingMs === undefined || remainingMs > 0) {
|
|
14702
|
+
retriedUnknownChannel = true;
|
|
14703
|
+
this.evictRouteHandle(routeHandle);
|
|
14704
|
+
attemptOpts = remainingMs === undefined ? opts : { ...opts, timeoutMs: remainingMs };
|
|
14705
|
+
continue;
|
|
14706
|
+
}
|
|
14707
|
+
}
|
|
14148
14708
|
if (deadBindCode && retriedUnknownChannel) {
|
|
14149
14709
|
this.evictRouteHandle(routeHandle);
|
|
14150
14710
|
}
|
|
@@ -14174,6 +14734,8 @@ class SubcClient {
|
|
|
14174
14734
|
}
|
|
14175
14735
|
subscribe(handle, body, onEvent, opts = {}) {
|
|
14176
14736
|
this.assertLiveHandle(handle);
|
|
14737
|
+
if (this.gates.has(handle))
|
|
14738
|
+
return this.subscribeGated(handle, body, onEvent, opts);
|
|
14177
14739
|
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
14178
14740
|
const priority = opts.priority ?? Priority.Interactive;
|
|
14179
14741
|
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
@@ -14181,22 +14743,43 @@ class SubcClient {
|
|
|
14181
14743
|
const key = pendingKey(handle, corr);
|
|
14182
14744
|
let subscriptionPending = null;
|
|
14183
14745
|
let resolveClosed = null;
|
|
14746
|
+
let resolveEnded;
|
|
14747
|
+
const ended = new Promise((resolve) => {
|
|
14748
|
+
resolveEnded = resolve;
|
|
14749
|
+
});
|
|
14750
|
+
const endFailure = (err, frame) => {
|
|
14751
|
+
if (frame?.header.ty === FrameType.Error) {
|
|
14752
|
+
const error = err;
|
|
14753
|
+
resolveEnded({ kind: "error", code: error.code, message: error.message, detail: error.detail });
|
|
14754
|
+
} else if (err instanceof SubcError && err.closeReason && err.closeReason !== "connection_lost") {
|
|
14755
|
+
resolveEnded({ kind: "route_closed", reason: err.closeReason });
|
|
14756
|
+
} else {
|
|
14757
|
+
resolveEnded({ kind: "connection_lost" });
|
|
14758
|
+
}
|
|
14759
|
+
};
|
|
14184
14760
|
const closed = new Promise((resolve, reject) => {
|
|
14185
14761
|
if (this.closedErr) {
|
|
14762
|
+
endFailure(this.closedErr);
|
|
14186
14763
|
reject(this.closedErr);
|
|
14187
14764
|
return;
|
|
14188
14765
|
}
|
|
14189
14766
|
resolveClosed = resolve;
|
|
14190
14767
|
subscriptionPending = {
|
|
14191
14768
|
handle,
|
|
14192
|
-
resolve: () =>
|
|
14193
|
-
|
|
14769
|
+
resolve: (frame) => {
|
|
14770
|
+
resolveEnded(frame.header.ty === FrameType.Response ? { kind: "response", body: frame.body } : { kind: "stream_end" });
|
|
14771
|
+
resolve();
|
|
14772
|
+
},
|
|
14773
|
+
reject: (err, frame) => {
|
|
14774
|
+
endFailure(err, frame);
|
|
14775
|
+
reject(err);
|
|
14776
|
+
},
|
|
14194
14777
|
onProgress: onEvent,
|
|
14195
14778
|
timer: null,
|
|
14196
14779
|
subscription: true
|
|
14197
14780
|
};
|
|
14198
14781
|
this.pending.set(key, subscriptionPending);
|
|
14199
|
-
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
14782
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission) | SUBSCRIPTION_FLAG, handle.channel, handle.epoch, corr, bytes);
|
|
14200
14783
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
14201
14784
|
const pending = this.pending.get(key);
|
|
14202
14785
|
if (pending)
|
|
@@ -14209,11 +14792,14 @@ class SubcClient {
|
|
|
14209
14792
|
return;
|
|
14210
14793
|
cancelled = true;
|
|
14211
14794
|
if (subscriptionPending && resolveClosed)
|
|
14212
|
-
this.settle(key, subscriptionPending,
|
|
14795
|
+
this.settle(key, subscriptionPending, () => {
|
|
14796
|
+
resolveEnded({ kind: "cancelled" });
|
|
14797
|
+
resolveClosed?.();
|
|
14798
|
+
});
|
|
14213
14799
|
if (this.isLiveHandle(handle))
|
|
14214
14800
|
this.cancel(handle, corr, priority);
|
|
14215
14801
|
};
|
|
14216
|
-
return { unsubscribe, closed };
|
|
14802
|
+
return { unsubscribe, closed, ended };
|
|
14217
14803
|
}
|
|
14218
14804
|
attachCancelSignal(signal, key, handle, corr, priority) {
|
|
14219
14805
|
if (!handle)
|
|
@@ -14279,7 +14865,7 @@ class SubcClient {
|
|
|
14279
14865
|
this.sendRouteGoodbye(handle);
|
|
14280
14866
|
}
|
|
14281
14867
|
async closeManagedRoute(target, identity, opts = {}) {
|
|
14282
|
-
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry).capabilities(), validatedRouteScope(opts.scope));
|
|
14868
|
+
const key = routeCacheKey(target, identity, await routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry).capabilities(), validatedRouteScope(opts.scope));
|
|
14283
14869
|
const cached = this.routes.get(key);
|
|
14284
14870
|
if (!cached)
|
|
14285
14871
|
return;
|
|
@@ -14332,10 +14918,21 @@ class SubcClient {
|
|
|
14332
14918
|
});
|
|
14333
14919
|
}
|
|
14334
14920
|
static async openConnection(opts) {
|
|
14335
|
-
const conn = await readConnectionFile(opts.connectionFile);
|
|
14921
|
+
const conn = opts.transport === "local" ? await readConnectionFileWithLocalEndpoint(opts.connectionFile) : await readConnectionFile(opts.connectionFile);
|
|
14336
14922
|
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
14337
14923
|
const endpoint = conn.endpoints[0];
|
|
14338
|
-
|
|
14924
|
+
let sock;
|
|
14925
|
+
if (opts.transport === "local") {
|
|
14926
|
+
const local = conn.localEndpoint;
|
|
14927
|
+
if (!local)
|
|
14928
|
+
throw new SubcError("connection file has no local_endpoint", "local_endpoint_absent");
|
|
14929
|
+
if (local.kind === "unix" !== (process.platform !== "win32")) {
|
|
14930
|
+
throw new SubcError("unsupported local endpoint kind on this platform", "local_endpoint_unsupported");
|
|
14931
|
+
}
|
|
14932
|
+
sock = await SubcSocket.connectLocal(local.kind === "unix" ? local.path : local.name, deadline);
|
|
14933
|
+
} else {
|
|
14934
|
+
sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
14935
|
+
}
|
|
14339
14936
|
try {
|
|
14340
14937
|
await authenticateClient(sock, conn, deadline);
|
|
14341
14938
|
} catch (err) {
|
|
@@ -14347,7 +14944,125 @@ class SubcClient {
|
|
|
14347
14944
|
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
14348
14945
|
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
14349
14946
|
}
|
|
14350
|
-
send(
|
|
14947
|
+
send(...args) {
|
|
14948
|
+
const [handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse, binary, signal, sleepGraceMs] = args;
|
|
14949
|
+
if (!handle || !this.gates.has(handle))
|
|
14950
|
+
return this.sendAttempt(...args);
|
|
14951
|
+
return this.gatedRequest(handle, false, timeoutMs, signal, (slot, remaining) => this.sendAttempt(handle, body, priority, admission, remaining, onProgress, acceptFrame, onLateResponse, binary, signal, sleepGraceMs, slot));
|
|
14952
|
+
}
|
|
14953
|
+
async gatedRequest(handle, watch, timeoutMs, signal, attempt) {
|
|
14954
|
+
const gate = this.gates.get(handle);
|
|
14955
|
+
const deadline = this.opts.now() + (timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
14956
|
+
let slot;
|
|
14957
|
+
try {
|
|
14958
|
+
slot = await gate.acquire(watch, deadline - this.opts.now(), signal);
|
|
14959
|
+
} catch (error) {
|
|
14960
|
+
throw this.notSentCallError("request was not sent while waiting for a route slot", error);
|
|
14961
|
+
}
|
|
14962
|
+
let delay = 25;
|
|
14963
|
+
let busyDeadlineError;
|
|
14964
|
+
try {
|
|
14965
|
+
for (;; ) {
|
|
14966
|
+
if (gate.signal.aborted)
|
|
14967
|
+
throw gate.signal.reason;
|
|
14968
|
+
if (signal?.aborted)
|
|
14969
|
+
throw this.notSentCallError("request cancelled before write", signal.reason);
|
|
14970
|
+
this.assertLiveHandle(handle);
|
|
14971
|
+
const budget = deadline - this.opts.now();
|
|
14972
|
+
if (budget < MIN_REQUEST_BUDGET_MS)
|
|
14973
|
+
throw busyDeadlineError ?? this.notSentCallError("request deadline elapsed before write", new Error("less than 1ms remains before the request deadline"));
|
|
14974
|
+
try {
|
|
14975
|
+
return await attempt(slot, budget);
|
|
14976
|
+
} catch (error) {
|
|
14977
|
+
const refusal = error instanceof SubcError ? error : error instanceof SubcCallError && error.cause instanceof SubcError ? error.cause : undefined;
|
|
14978
|
+
if (!refusal || !isRouteBusyRetryable(errorFrameFlags.get(refusal) ?? 0, refusal.code, this.gates.get(handle) === gate))
|
|
14979
|
+
throw error;
|
|
14980
|
+
const exhausted = () => new SubcCallError("not_sent", `daemon refused before forwarding: ${refusal.message}`, "route_busy", refusal);
|
|
14981
|
+
busyDeadlineError = exhausted();
|
|
14982
|
+
const remaining = deadline - this.opts.now();
|
|
14983
|
+
if (remaining <= 0 || signal?.aborted)
|
|
14984
|
+
throw exhausted();
|
|
14985
|
+
const ms = Math.min(remaining, delay * (0.5 + this.opts.random() * 0.5));
|
|
14986
|
+
delay = Math.min(1000, delay * 2);
|
|
14987
|
+
const proceed = await this.busySleep(ms, gate.signal, signal);
|
|
14988
|
+
if (gate.signal.aborted)
|
|
14989
|
+
throw gate.signal.reason;
|
|
14990
|
+
if (!proceed || this.opts.now() >= deadline)
|
|
14991
|
+
throw exhausted();
|
|
14992
|
+
}
|
|
14993
|
+
}
|
|
14994
|
+
} finally {
|
|
14995
|
+
if (!slot.inFlight)
|
|
14996
|
+
slot.release();
|
|
14997
|
+
}
|
|
14998
|
+
}
|
|
14999
|
+
async busySleep(ms, closed, signal) {
|
|
15000
|
+
if (closed.aborted || signal?.aborted)
|
|
15001
|
+
return false;
|
|
15002
|
+
let abort;
|
|
15003
|
+
const cancelled = new Promise((resolve) => {
|
|
15004
|
+
abort = () => resolve(false);
|
|
15005
|
+
});
|
|
15006
|
+
closed.addEventListener("abort", abort, { once: true });
|
|
15007
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
15008
|
+
try {
|
|
15009
|
+
return await Promise.race([this.opts.sleep(ms).then(() => true), cancelled]);
|
|
15010
|
+
} finally {
|
|
15011
|
+
closed.removeEventListener("abort", abort);
|
|
15012
|
+
signal?.removeEventListener("abort", abort);
|
|
15013
|
+
}
|
|
15014
|
+
}
|
|
15015
|
+
subscribeGated(handle, body, onEvent, opts) {
|
|
15016
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
15017
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
15018
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
15019
|
+
const abort = new AbortController;
|
|
15020
|
+
let current;
|
|
15021
|
+
let cancelled = false;
|
|
15022
|
+
let resolveClosed;
|
|
15023
|
+
let rejectClosed;
|
|
15024
|
+
let resolveEnded;
|
|
15025
|
+
const closed = new Promise((resolve, reject) => {
|
|
15026
|
+
resolveClosed = resolve;
|
|
15027
|
+
rejectClosed = reject;
|
|
15028
|
+
});
|
|
15029
|
+
const ended = new Promise((resolve) => {
|
|
15030
|
+
resolveEnded = resolve;
|
|
15031
|
+
});
|
|
15032
|
+
this.gatedRequest(handle, true, undefined, abort.signal, (slot, remaining) => this.sendAttempt(handle, bytes, priority, admission, remaining, onEvent, undefined, undefined, false, undefined, undefined, slot, true, (key, pending, corr) => {
|
|
15033
|
+
current = { key, pending, corr };
|
|
15034
|
+
})).then((frame) => {
|
|
15035
|
+
if (cancelled)
|
|
15036
|
+
return;
|
|
15037
|
+
resolveEnded(frame.header.ty === FrameType.Response ? { kind: "response", body: frame.body } : { kind: "stream_end" });
|
|
15038
|
+
resolveClosed();
|
|
15039
|
+
}, (error) => {
|
|
15040
|
+
if (cancelled)
|
|
15041
|
+
return;
|
|
15042
|
+
if (error instanceof SubcError && error.closeReason)
|
|
15043
|
+
resolveEnded(error.closeReason === "connection_lost" ? { kind: "connection_lost" } : { kind: "route_closed", reason: error.closeReason });
|
|
15044
|
+
else if (error instanceof SubcError || error instanceof SubcCallError)
|
|
15045
|
+
resolveEnded({ kind: "error", code: error.code, message: error.message, detail: error.detail });
|
|
15046
|
+
else
|
|
15047
|
+
resolveEnded({ kind: "connection_lost" });
|
|
15048
|
+
rejectClosed(error);
|
|
15049
|
+
});
|
|
15050
|
+
return { closed, ended, unsubscribe: () => {
|
|
15051
|
+
if (cancelled)
|
|
15052
|
+
return;
|
|
15053
|
+
cancelled = true;
|
|
15054
|
+
abort.abort();
|
|
15055
|
+
if (current && this.pending.get(current.key) === current.pending) {
|
|
15056
|
+
const { key, pending, corr } = current;
|
|
15057
|
+
this.settle(key, pending, () => pending.reject(new SubcError("subscription cancelled")));
|
|
15058
|
+
if (this.isLiveHandle(handle))
|
|
15059
|
+
this.cancel(handle, corr, priority);
|
|
15060
|
+
}
|
|
15061
|
+
resolveEnded({ kind: "cancelled" });
|
|
15062
|
+
resolveClosed();
|
|
15063
|
+
} };
|
|
15064
|
+
}
|
|
15065
|
+
sendAttempt(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse, binary = false, signal, sleepGraceMs = this.opts.sleepGraceMs ?? DEFAULT_SLEEP_GRACE_MS, slot, subscription = false, registered) {
|
|
14351
15066
|
if (handle)
|
|
14352
15067
|
this.assertLiveHandle(handle);
|
|
14353
15068
|
if (this.closedErr)
|
|
@@ -14361,7 +15076,7 @@ class SubcClient {
|
|
|
14361
15076
|
const key = pendingKey(handle, corr);
|
|
14362
15077
|
const channel = handle?.channel ?? 0;
|
|
14363
15078
|
const epoch = handle?.epoch ?? 0;
|
|
14364
|
-
const frame = buildFrame(FrameType.Request, buildFlags(binary, priority, false, admission), channel, epoch, corr, body);
|
|
15079
|
+
const frame = buildFrame(FrameType.Request, buildFlags(binary, priority, false, admission) | (subscription ? SUBSCRIPTION_FLAG : 0), channel, epoch, corr, body);
|
|
14365
15080
|
return new Promise((resolve, reject) => {
|
|
14366
15081
|
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14367
15082
|
const pending = {
|
|
@@ -14371,11 +15086,27 @@ class SubcClient {
|
|
|
14371
15086
|
onProgress,
|
|
14372
15087
|
timer: null,
|
|
14373
15088
|
acceptFrame,
|
|
14374
|
-
onLateResponse
|
|
15089
|
+
onLateResponse,
|
|
15090
|
+
slot,
|
|
15091
|
+
subscription
|
|
14375
15092
|
};
|
|
14376
|
-
|
|
15093
|
+
if (!subscription)
|
|
15094
|
+
this.armRequestTimeout(key, pending, channel, corr, ms, sleepGraceMs);
|
|
14377
15095
|
this.pending.set(key, pending);
|
|
14378
|
-
|
|
15096
|
+
registered?.(key, pending, corr);
|
|
15097
|
+
if (slot && handle) {
|
|
15098
|
+
slot.inFlight = true;
|
|
15099
|
+
this.slots.set(key, { handle, slot });
|
|
15100
|
+
}
|
|
15101
|
+
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
15102
|
+
if (slot && !write.queued) {
|
|
15103
|
+
this.slots.delete(key);
|
|
15104
|
+
slot.inFlight = false;
|
|
15105
|
+
slot.release();
|
|
15106
|
+
}
|
|
15107
|
+
write.completed.catch((error) => {
|
|
15108
|
+
if (error instanceof SocketWriteNotQueuedError || error instanceof SocketWriteQueuedError)
|
|
15109
|
+
error = error.cause ?? error;
|
|
14379
15110
|
const current = this.pending.get(key);
|
|
14380
15111
|
if (current)
|
|
14381
15112
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
@@ -14384,10 +15115,16 @@ class SubcClient {
|
|
|
14384
15115
|
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
14385
15116
|
});
|
|
14386
15117
|
}
|
|
15118
|
+
armRequestTimeout(key, pending, channel, corr, ms, sleepGraceMs) {
|
|
15119
|
+
pending.sleepGrace = { wallStartMs: Date.now(), awakeStartMs: performance.now(), ms: sleepGraceMs, used: false };
|
|
15120
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
15121
|
+
}
|
|
14387
15122
|
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
14388
15123
|
const settleAsTimeout = () => {
|
|
14389
15124
|
if (pending.onLateResponse)
|
|
14390
15125
|
this.lateResponses.set(key, pending.onLateResponse);
|
|
15126
|
+
if (pending.slot?.inFlight && pending.handle && this.isLiveHandle(pending.handle))
|
|
15127
|
+
this.cancel(pending.handle, corr);
|
|
14391
15128
|
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
14392
15129
|
};
|
|
14393
15130
|
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
@@ -14399,6 +15136,17 @@ class SubcClient {
|
|
|
14399
15136
|
setImmediate(arbitrate);
|
|
14400
15137
|
return;
|
|
14401
15138
|
}
|
|
15139
|
+
const sleep = pending.sleepGrace;
|
|
15140
|
+
if (sleep && !sleep.used && sleep.ms > 0) {
|
|
15141
|
+
const wallElapsed = Date.now() - sleep.wallStartMs;
|
|
15142
|
+
const awakeElapsed = performance.now() - sleep.awakeStartMs;
|
|
15143
|
+
const sleptMs = wallElapsed - awakeElapsed;
|
|
15144
|
+
if (sleptMs > SLEEP_DETECTION_THRESHOLD_MS) {
|
|
15145
|
+
sleep.used = true;
|
|
15146
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), sleep.ms);
|
|
15147
|
+
return;
|
|
15148
|
+
}
|
|
15149
|
+
}
|
|
14402
15150
|
settleAsTimeout();
|
|
14403
15151
|
};
|
|
14404
15152
|
setImmediate(arbitrate);
|
|
@@ -14409,7 +15157,7 @@ class SubcClient {
|
|
|
14409
15157
|
const priority = opts.priority ?? Priority.Interactive;
|
|
14410
15158
|
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
14411
15159
|
try {
|
|
14412
|
-
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, binary, opts.signal);
|
|
15160
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, binary, opts.signal, validatedSleepGraceMs(opts.sleepGraceMs ?? this.opts.sleepGraceMs ?? DEFAULT_SLEEP_GRACE_MS));
|
|
14413
15161
|
return this.decodeReply(reply);
|
|
14414
15162
|
} catch (error) {
|
|
14415
15163
|
if (error instanceof SubcCallError)
|
|
@@ -14417,7 +15165,13 @@ class SubcClient {
|
|
|
14417
15165
|
throw this.terminalCallError("managed call failed", error);
|
|
14418
15166
|
}
|
|
14419
15167
|
}
|
|
14420
|
-
sendManaged(
|
|
15168
|
+
sendManaged(...args) {
|
|
15169
|
+
const [handle, body, priority, admission, timeoutMs, onProgress, binary, signal, sleepGraceMs] = args;
|
|
15170
|
+
if (!this.gates.has(handle))
|
|
15171
|
+
return this.sendManagedAttempt(...args);
|
|
15172
|
+
return this.gatedRequest(handle, false, timeoutMs, signal, (slot, remaining) => this.sendManagedAttempt(handle, body, priority, admission, remaining, onProgress, binary, signal, sleepGraceMs, slot));
|
|
15173
|
+
}
|
|
15174
|
+
sendManagedAttempt(handle, body, priority, admission, timeoutMs, onProgress, binary = false, signal, sleepGraceMs = this.opts.sleepGraceMs ?? DEFAULT_SLEEP_GRACE_MS, slot) {
|
|
14421
15175
|
try {
|
|
14422
15176
|
this.assertLiveHandle(handle);
|
|
14423
15177
|
} catch (error) {
|
|
@@ -14451,12 +15205,22 @@ class SubcClient {
|
|
|
14451
15205
|
reject,
|
|
14452
15206
|
onProgress,
|
|
14453
15207
|
timer: null,
|
|
14454
|
-
classifyFailure
|
|
15208
|
+
classifyFailure,
|
|
15209
|
+
slot
|
|
14455
15210
|
};
|
|
14456
|
-
|
|
15211
|
+
this.armRequestTimeout(key, pending, handle.channel, corr, ms, sleepGraceMs);
|
|
14457
15212
|
this.pending.set(key, pending);
|
|
15213
|
+
if (slot) {
|
|
15214
|
+
slot.inFlight = true;
|
|
15215
|
+
this.slots.set(key, { handle, slot });
|
|
15216
|
+
}
|
|
14458
15217
|
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
14459
15218
|
handedToSocket = write.queued;
|
|
15219
|
+
if (slot && !write.queued) {
|
|
15220
|
+
this.slots.delete(key);
|
|
15221
|
+
slot.inFlight = false;
|
|
15222
|
+
slot.release();
|
|
15223
|
+
}
|
|
14460
15224
|
write.completed.catch((error) => {
|
|
14461
15225
|
const current = this.pending.get(key);
|
|
14462
15226
|
if (current)
|
|
@@ -14473,7 +15237,7 @@ class SubcClient {
|
|
|
14473
15237
|
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
14474
15238
|
}
|
|
14475
15239
|
const target = { kind: opts.targetKind ?? this.opts.targetKind, module_id: moduleId };
|
|
14476
|
-
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
15240
|
+
const consumerIdentity = await routeOpenConsumerIdentity(opts);
|
|
14477
15241
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry;
|
|
14478
15242
|
reverseRequests.seal();
|
|
14479
15243
|
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities(), scope);
|
|
@@ -14558,6 +15322,7 @@ class SubcClient {
|
|
|
14558
15322
|
if ("handle" in outcome) {
|
|
14559
15323
|
const { handle } = outcome;
|
|
14560
15324
|
if (cached.closed) {
|
|
15325
|
+
this.failHandle(handle, new SubcError("route closed during route.open", "route_closed", undefined, "closed_by_caller"));
|
|
14561
15326
|
this.liveRoutes.delete(handle.channel);
|
|
14562
15327
|
this.sendRouteGoodbye(handle);
|
|
14563
15328
|
throw this.routeClosedDuringOpen();
|
|
@@ -14681,6 +15446,9 @@ class SubcClient {
|
|
|
14681
15446
|
return;
|
|
14682
15447
|
} catch (err) {
|
|
14683
15448
|
if (!isConsumerReconnectTransient(err) || attempt >= this.opts.reconnectBackoff.maxAttempts) {
|
|
15449
|
+
if (isConsumerReconnectTransient(err) && !(err instanceof AuthError)) {
|
|
15450
|
+
throw new DaemonUnavailableError(err);
|
|
15451
|
+
}
|
|
14684
15452
|
if (err instanceof AuthError && attempt > 1) {
|
|
14685
15453
|
throw new AuthError(`reconnect gave up after ${attempt} attempts: ${err.message} — the connection file and the daemon's key disagree persistently ` + `(daemon restarting in a loop, split connection-file paths, or a genuinely foreign daemon on this port); ` + `check the daemon, then restart this host app`);
|
|
14686
15454
|
}
|
|
@@ -14803,6 +15571,16 @@ class SubcClient {
|
|
|
14803
15571
|
}
|
|
14804
15572
|
const key = pendingKey(handle, frame.header.corr);
|
|
14805
15573
|
const pending = this.pending.get(key);
|
|
15574
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
|
|
15575
|
+
const record = this.slots.get(key);
|
|
15576
|
+
if (record) {
|
|
15577
|
+
this.slots.delete(key);
|
|
15578
|
+
record.slot.inFlight = false;
|
|
15579
|
+
const busy = frame.header.ty === FrameType.Error && pending?.slot === record.slot && isRouteBusyRetryable(frame.header.flags, this.errorFromFrame(frame).code, !!handle && this.gates.has(handle));
|
|
15580
|
+
if (!busy)
|
|
15581
|
+
record.slot.release();
|
|
15582
|
+
}
|
|
15583
|
+
}
|
|
14806
15584
|
if (pending) {
|
|
14807
15585
|
if (pending.acceptFrame && !pending.acceptFrame(frame))
|
|
14808
15586
|
return;
|
|
@@ -14818,7 +15596,7 @@ class SubcClient {
|
|
|
14818
15596
|
this.settle(key, pending, () => pending.resolve(frame));
|
|
14819
15597
|
return;
|
|
14820
15598
|
case FrameType.Error:
|
|
14821
|
-
this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame)));
|
|
15599
|
+
this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame), frame));
|
|
14822
15600
|
return;
|
|
14823
15601
|
default:
|
|
14824
15602
|
return;
|
|
@@ -14955,6 +15733,14 @@ class SubcClient {
|
|
|
14955
15733
|
return this.legacyChannelReasons.get(channel) ?? "unknown";
|
|
14956
15734
|
}
|
|
14957
15735
|
failHandle(handle, error) {
|
|
15736
|
+
this.gates.get(handle)?.close(error);
|
|
15737
|
+
this.gates.delete(handle);
|
|
15738
|
+
for (const [key, record] of this.slots) {
|
|
15739
|
+
if (sameRouteHandle(record.handle, handle)) {
|
|
15740
|
+
record.slot.release();
|
|
15741
|
+
this.slots.delete(key);
|
|
15742
|
+
}
|
|
15743
|
+
}
|
|
14958
15744
|
for (const [key, pending] of this.pending) {
|
|
14959
15745
|
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
14960
15746
|
this.rejectPending(key, pending, error);
|
|
@@ -14964,6 +15750,10 @@ class SubcClient {
|
|
|
14964
15750
|
const routeError = err instanceof SubcError && err.closeReason ? err : new SubcError(err.message, errorCode(err), undefined, this.closeStarted ? "closed_by_caller" : "connection_lost");
|
|
14965
15751
|
if (!this.closedErr)
|
|
14966
15752
|
this.closedErr = err;
|
|
15753
|
+
for (const gate of this.gates.values())
|
|
15754
|
+
gate.close(routeError);
|
|
15755
|
+
this.gates.clear();
|
|
15756
|
+
this.slots.clear();
|
|
14967
15757
|
this.routeEndReasons.clear();
|
|
14968
15758
|
this.legacyChannelReasons.clear();
|
|
14969
15759
|
this.routeModules.clear();
|
|
@@ -14997,6 +15787,9 @@ class SubcClient {
|
|
|
14997
15787
|
return this.ingressEpochDropCount;
|
|
14998
15788
|
}
|
|
14999
15789
|
installRoute(channel, epoch, reverseRequests = new ReverseRequestRegistry) {
|
|
15790
|
+
const previous = this.liveRoutes.get(channel);
|
|
15791
|
+
if (previous && this.gates.has(previous))
|
|
15792
|
+
this.failHandle(previous, new SubcError("route replaced", "route_closed", undefined, "closed_by_caller"));
|
|
15000
15793
|
const handle = createRouteHandle(channel, epoch, this.connectionToken, reverseRequests);
|
|
15001
15794
|
this.routeEndReasons.delete(channel);
|
|
15002
15795
|
this.legacyChannelReasons.delete(channel);
|
|
@@ -15067,9 +15860,15 @@ function isRetryableRouteOpenCode(code) {
|
|
|
15067
15860
|
return false;
|
|
15068
15861
|
if (code === "target_flow_unsupported")
|
|
15069
15862
|
return false;
|
|
15863
|
+
if (code === "target_federated_unsupported")
|
|
15864
|
+
return false;
|
|
15070
15865
|
if (code === "target_agent_run_unsupported")
|
|
15071
15866
|
return false;
|
|
15072
|
-
|
|
15867
|
+
if (code === "scope_bind_proof_required" || code === "scope_bind_proof_mismatch")
|
|
15868
|
+
return false;
|
|
15869
|
+
if (code === "scope_epoch_in_use")
|
|
15870
|
+
return false;
|
|
15871
|
+
return code === "module_reloading" || code === "module_warming" || code === "target_unavailable" || code === "module_timeout" || code === "scope_not_synced" || code === "scope_changed" || code === "session_address_unavailable" || code === "bind_busy";
|
|
15073
15872
|
}
|
|
15074
15873
|
function equalJitter(delayMs, random) {
|
|
15075
15874
|
const half = delayMs / 2;
|
|
@@ -15106,6 +15905,12 @@ var STALE_ROUTE_EPOCH = "stale_route_epoch";
|
|
|
15106
15905
|
function isEstablishedRouteDead(_flags, code) {
|
|
15107
15906
|
return code === UNKNOWN_CHANNEL || code === STALE_ROUTE_EPOCH;
|
|
15108
15907
|
}
|
|
15908
|
+
function isRouteBusyRetryable(flags, code, routeOptedIn) {
|
|
15909
|
+
return routeOptedIn && (flags & DAEMON_ORIGIN_FLAG) !== 0 && code === "route_busy";
|
|
15910
|
+
}
|
|
15911
|
+
function isDataPlaneDrainRefusal(code) {
|
|
15912
|
+
return code === "module_reloading";
|
|
15913
|
+
}
|
|
15109
15914
|
async function connectionFileExists(path) {
|
|
15110
15915
|
try {
|
|
15111
15916
|
await fs3.access(path);
|
|
@@ -15117,6 +15922,7 @@ async function connectionFileExists(path) {
|
|
|
15117
15922
|
function normalizeConnectOptions(opts) {
|
|
15118
15923
|
return {
|
|
15119
15924
|
connectionFile: opts.connectionFile,
|
|
15925
|
+
transport: opts.transport ?? "tcp",
|
|
15120
15926
|
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
15121
15927
|
identity: opts.identity,
|
|
15122
15928
|
targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
|
|
@@ -15126,11 +15932,18 @@ function normalizeConnectOptions(opts) {
|
|
|
15126
15932
|
random: opts.random ?? Math.random,
|
|
15127
15933
|
routeOpenRetryDeadlineMs: opts.routeOpenRetryDeadlineMs ?? ROUTE_OPEN_RETRY_DEADLINE_MS,
|
|
15128
15934
|
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS,
|
|
15935
|
+
sleepGraceMs: validatedSleepGraceMs(opts.sleepGraceMs ?? DEFAULT_SLEEP_GRACE_MS),
|
|
15129
15936
|
bodyReadTimeoutMs: opts.bodyReadTimeoutMs,
|
|
15130
15937
|
livenessProbeWindowMs: opts.livenessProbeWindowMs ?? LIVENESS_PROBE_WINDOW_MS,
|
|
15131
15938
|
onControlPush: opts.onControlPush
|
|
15132
15939
|
};
|
|
15133
15940
|
}
|
|
15941
|
+
function validatedSleepGraceMs(ms) {
|
|
15942
|
+
if (!Number.isFinite(ms) || ms < 0 || ms > 2147483647) {
|
|
15943
|
+
throw new RangeError("sleepGraceMs must be finite and in 0..2147483647 milliseconds");
|
|
15944
|
+
}
|
|
15945
|
+
return ms;
|
|
15946
|
+
}
|
|
15134
15947
|
function isValidCapabilityIdentifier(identifier) {
|
|
15135
15948
|
if (/\s/u.test(identifier))
|
|
15136
15949
|
return false;
|
|
@@ -15153,7 +15966,13 @@ function isValidCapabilityIdentifier(identifier) {
|
|
|
15153
15966
|
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities, scope) {
|
|
15154
15967
|
const consumerPart = consumerIdentity ? `${consumerIdentity.module_id}\x00${consumerIdentity.launch_nonce}` : "";
|
|
15155
15968
|
const capabilitiesPart = [...consumerCapabilities].sort().join("\x00");
|
|
15156
|
-
const scopePart = scope ? JSON.stringify([
|
|
15969
|
+
const scopePart = scope ? JSON.stringify([
|
|
15970
|
+
scope.owner.kind,
|
|
15971
|
+
scope.owner.kind === "reserved" ? scope.owner.module_id : null,
|
|
15972
|
+
scope.ref,
|
|
15973
|
+
scope.scopeEpoch,
|
|
15974
|
+
scope.bindProof === undefined ? null : createHash4("sha256").update(scope.bindProof).digest("hex")
|
|
15975
|
+
]) : "";
|
|
15157
15976
|
return `${target.kind}\x00${target.module_id}\x00${identity.project_root}\x00${identity.harness}\x00${identity.session}\x00${consumerPart}\x00${capabilitiesPart}\x00${scopePart}`;
|
|
15158
15977
|
}
|
|
15159
15978
|
function validatedRouteScope(scope) {
|
|
@@ -15171,13 +15990,18 @@ function validatedRouteScope(scope) {
|
|
|
15171
15990
|
if (!Number.isSafeInteger(scope.scopeEpoch) || scope.scopeEpoch < 0) {
|
|
15172
15991
|
throw new SubcError("scope.scopeEpoch must be a safe non-negative integer", "invalid_scope");
|
|
15173
15992
|
}
|
|
15174
|
-
return {
|
|
15993
|
+
return {
|
|
15994
|
+
owner: { ...scope.owner },
|
|
15995
|
+
ref: scope.ref,
|
|
15996
|
+
scopeEpoch: scope.scopeEpoch,
|
|
15997
|
+
...scope.bindProof !== undefined ? { bindProof: scope.bindProof } : {}
|
|
15998
|
+
};
|
|
15175
15999
|
}
|
|
15176
|
-
function routeOpenConsumerIdentity(opts = {}) {
|
|
16000
|
+
async function routeOpenConsumerIdentity(opts = {}) {
|
|
15177
16001
|
if (opts.consumerIdentity !== undefined)
|
|
15178
16002
|
return opts.consumerIdentity ?? undefined;
|
|
15179
16003
|
const moduleId = process.env[SUBC_MODULE_ID_ENV];
|
|
15180
|
-
const launchNonce =
|
|
16004
|
+
const launchNonce = (await launchNonceOrUndefinedAsync())?.value;
|
|
15181
16005
|
if (!moduleId || !launchNonce)
|
|
15182
16006
|
return;
|
|
15183
16007
|
return { module_id: moduleId, launch_nonce: launchNonce };
|
|
@@ -15629,20 +16453,20 @@ function getLifecycleRegistry() {
|
|
|
15629
16453
|
}
|
|
15630
16454
|
|
|
15631
16455
|
// ../aft-bridge/dist/project-identity.js
|
|
15632
|
-
import { realpathSync as
|
|
15633
|
-
import { posix as
|
|
16456
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
16457
|
+
import { posix as posix3, resolve as resolve6, win32 as win323 } from "node:path";
|
|
15634
16458
|
function canonicalizeProjectRoot(dir) {
|
|
15635
16459
|
const trimmed = trimProjectRootSeparators(dir);
|
|
15636
16460
|
let canonical;
|
|
15637
16461
|
try {
|
|
15638
|
-
canonical =
|
|
16462
|
+
canonical = realpathSync3(trimmed);
|
|
15639
16463
|
} catch {
|
|
15640
16464
|
canonical = resolve6(trimmed);
|
|
15641
16465
|
}
|
|
15642
16466
|
return normalizeWindowsRoot(canonical);
|
|
15643
16467
|
}
|
|
15644
16468
|
function trimProjectRootSeparators(dir, platform = process.platform) {
|
|
15645
|
-
const root = (platform === "win32" ?
|
|
16469
|
+
const root = (platform === "win32" ? win323 : posix3).parse(dir).root;
|
|
15646
16470
|
const trimmed = dir.replace(/[/\\]+$/, "");
|
|
15647
16471
|
return trimmed.length < root.length ? root : trimmed;
|
|
15648
16472
|
}
|
|
@@ -16441,6 +17265,8 @@ class SubcTransportPool {
|
|
|
16441
17265
|
harness;
|
|
16442
17266
|
connectionFile;
|
|
16443
17267
|
handshakeTimeoutMs;
|
|
17268
|
+
clock;
|
|
17269
|
+
waitClock;
|
|
16444
17270
|
consumerIdentity;
|
|
16445
17271
|
hostScopes;
|
|
16446
17272
|
hostScopeCache = new Map;
|
|
@@ -16483,6 +17309,13 @@ class SubcTransportPool {
|
|
|
16483
17309
|
editSlotSurvives;
|
|
16484
17310
|
editSlotSurvivesCaptured = false;
|
|
16485
17311
|
constructor(options) {
|
|
17312
|
+
this.clock = options.clock ?? {
|
|
17313
|
+
now: () => Date.now(),
|
|
17314
|
+
awakeNow: () => performance.now(),
|
|
17315
|
+
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
|
17316
|
+
clearTimeout: (timer) => clearTimeout(timer)
|
|
17317
|
+
};
|
|
17318
|
+
this.waitClock = new WaitClock(() => this.clock.now(), () => this.clock.awakeNow?.() ?? this.clock.now());
|
|
16486
17319
|
this.connectionFile = options.connectionFile;
|
|
16487
17320
|
this.harness = options.harness;
|
|
16488
17321
|
this.handshakeTimeoutMs = options.handshakeTimeoutMs;
|
|
@@ -17109,11 +17942,12 @@ class SubcTransportPool {
|
|
|
17109
17942
|
this.stopUnusedHostScopeRetry(record.identity.session);
|
|
17110
17943
|
}
|
|
17111
17944
|
async routeRequest(identity, body, timeoutMs, onProgress, expectedGeneration, abortSignal) {
|
|
17112
|
-
const callStartedAt =
|
|
17945
|
+
const callStartedAt = this.waitClock.now();
|
|
17113
17946
|
const callDeadlineMs = timeoutMs ?? SUBC_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
17947
|
+
const deadline = new RequestDeadline(this.waitClock, callDeadlineMs);
|
|
17114
17948
|
let scheduledDelayMs = 0;
|
|
17115
|
-
const spentMs = () => Math.max(
|
|
17116
|
-
const remainingMs = () =>
|
|
17949
|
+
const spentMs = () => Math.max(this.waitClock.now() - callStartedAt, scheduledDelayMs);
|
|
17950
|
+
const remainingMs = () => deadline.remaining() - Math.max(0, scheduledDelayMs - (this.waitClock.now() - callStartedAt));
|
|
17117
17951
|
const root = asCanonicalRootPath(identity.project_root);
|
|
17118
17952
|
let generation = expectedGeneration;
|
|
17119
17953
|
if (this.lifecycleEnabled()) {
|
|
@@ -17134,12 +17968,12 @@ class SubcTransportPool {
|
|
|
17134
17968
|
const remaining = remainingMs();
|
|
17135
17969
|
if (remaining <= 0)
|
|
17136
17970
|
throw keyChanged ?? callDeadlinePassedError(undefined, callDeadlineMs);
|
|
17137
|
-
let
|
|
17971
|
+
let cancelTimer;
|
|
17138
17972
|
try {
|
|
17139
17973
|
const acquired = await Promise.race([
|
|
17140
17974
|
this.ensureClient(),
|
|
17141
17975
|
new Promise((resolve) => {
|
|
17142
|
-
|
|
17976
|
+
cancelTimer = deadlineTimer(this.clock, remainingMs, () => resolve(CALL_DEADLINE_EXPIRED));
|
|
17143
17977
|
})
|
|
17144
17978
|
]);
|
|
17145
17979
|
if (acquired === CALL_DEADLINE_EXPIRED) {
|
|
@@ -17154,22 +17988,22 @@ class SubcTransportPool {
|
|
|
17154
17988
|
if (spentMs() + this.nextRouteReopenDelayMs() >= callDeadlineMs)
|
|
17155
17989
|
throw error;
|
|
17156
17990
|
} finally {
|
|
17157
|
-
|
|
17991
|
+
cancelTimer?.();
|
|
17158
17992
|
}
|
|
17159
17993
|
const { delayMs, wait } = this.waitForRouteReopenBackoff();
|
|
17160
17994
|
scheduledDelayMs = spentMs() + delayMs;
|
|
17161
|
-
let
|
|
17995
|
+
let cancelRetryTimer;
|
|
17162
17996
|
try {
|
|
17163
17997
|
const waited = await Promise.race([
|
|
17164
17998
|
wait,
|
|
17165
17999
|
new Promise((resolve) => {
|
|
17166
|
-
|
|
18000
|
+
cancelRetryTimer = deadlineTimer(this.clock, remainingMs, () => resolve(CALL_DEADLINE_EXPIRED));
|
|
17167
18001
|
})
|
|
17168
18002
|
]);
|
|
17169
18003
|
if (waited === CALL_DEADLINE_EXPIRED)
|
|
17170
18004
|
throw keyChanged;
|
|
17171
18005
|
} finally {
|
|
17172
|
-
|
|
18006
|
+
cancelRetryTimer?.();
|
|
17173
18007
|
}
|
|
17174
18008
|
}
|
|
17175
18009
|
this.assertRecordLive(record);
|
|
@@ -17212,9 +18046,9 @@ class SubcTransportPool {
|
|
|
17212
18046
|
const remaining = remainingMs();
|
|
17213
18047
|
if (remaining <= 0)
|
|
17214
18048
|
throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
|
|
17215
|
-
let
|
|
18049
|
+
let cancelTimer;
|
|
17216
18050
|
const expired = new Promise((resolve) => {
|
|
17217
|
-
|
|
18051
|
+
cancelTimer = deadlineTimer(this.clock, remainingMs, () => resolve(CALL_DEADLINE_EXPIRED));
|
|
17218
18052
|
});
|
|
17219
18053
|
try {
|
|
17220
18054
|
const opened = await Promise.race([openRoute(), expired]);
|
|
@@ -17223,7 +18057,7 @@ class SubcTransportPool {
|
|
|
17223
18057
|
}
|
|
17224
18058
|
return opened;
|
|
17225
18059
|
} finally {
|
|
17226
|
-
|
|
18060
|
+
cancelTimer?.();
|
|
17227
18061
|
}
|
|
17228
18062
|
};
|
|
17229
18063
|
const openRouteAfterReloadWindow = async () => {
|
|
@@ -17302,6 +18136,7 @@ class SubcTransportPool {
|
|
|
17302
18136
|
try {
|
|
17303
18137
|
request = client.request(route, body, {
|
|
17304
18138
|
timeoutMs: Math.ceil(requestTimeoutMs),
|
|
18139
|
+
...deadline.resumed ? { sleepGraceMs: 0 } : {},
|
|
17305
18140
|
onProgress
|
|
17306
18141
|
});
|
|
17307
18142
|
} catch (error) {
|
|
@@ -17429,6 +18264,7 @@ class SubcTransportPool {
|
|
|
17429
18264
|
this.connecting = this.connectFn({
|
|
17430
18265
|
connectionFile: this.connectionFile,
|
|
17431
18266
|
handshakeTimeoutMs: this.handshakeTimeoutMs,
|
|
18267
|
+
sleepGraceMs: RESUME_ALLOWANCE_MS,
|
|
17432
18268
|
onControlPush: (push) => {
|
|
17433
18269
|
if (owner && this.client === owner)
|
|
17434
18270
|
this.onScopeRouteClose(push);
|
|
@@ -17617,7 +18453,9 @@ class SubcTransportPool {
|
|
|
17617
18453
|
});
|
|
17618
18454
|
retry = state.bindPending;
|
|
17619
18455
|
} else {
|
|
17620
|
-
retry = this.hostScope(client, record.identity)
|
|
18456
|
+
retry = this.hostScope(client, record.identity).finally(() => {
|
|
18457
|
+
this.scheduleHostScopeRetry(session, state);
|
|
18458
|
+
});
|
|
17621
18459
|
}
|
|
17622
18460
|
retry.catch((error) => {
|
|
17623
18461
|
warn(`subc head scope: session=${session} background retry failed code=${error instanceof Error ? error.name : "Error"}`);
|
|
@@ -17785,12 +18623,18 @@ class SubcTransportPool {
|
|
|
17785
18623
|
}
|
|
17786
18624
|
return this.openScopedHostToolRoute(client, identity, record, entry, channel);
|
|
17787
18625
|
}
|
|
17788
|
-
bindHostToolRoute(client, identity, channel, reason, scope) {
|
|
18626
|
+
async bindHostToolRoute(client, identity, channel, reason, scope) {
|
|
17789
18627
|
log(`subc head scope: session=${identity.session} bind=${scope ? "scoped" : "unscoped"} channel=${channel} root=${identity.project_root} reason=${reason}` + (scope ? ` ref=${scope.ref.slice(0, 8)} epoch=${scope.scopeEpoch}` : ""));
|
|
17790
|
-
|
|
18628
|
+
const route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
|
|
17791
18629
|
consumerIdentity: this.consumerIdentity,
|
|
17792
18630
|
...scope ? { scope } : {}
|
|
17793
18631
|
});
|
|
18632
|
+
const state = this.hostScopeCache.get(identity.session);
|
|
18633
|
+
if (scope && state?.unscopedReason && state.answer?.result === "scope" && state.answer.scope === scope) {
|
|
18634
|
+
log(`subc head scope: session=${identity.session} route=upgraded from=unscoped ref=${scope.ref.slice(0, 8)} epoch=${scope.scopeEpoch}`);
|
|
18635
|
+
state.unscopedReason = undefined;
|
|
18636
|
+
}
|
|
18637
|
+
return route;
|
|
17794
18638
|
}
|
|
17795
18639
|
async openScopedHostToolRoute(client, identity, record, entry, channel = "tool") {
|
|
17796
18640
|
const decision = await this.hostScope(client, identity);
|
|
@@ -18422,7 +19266,8 @@ function taskIdFromError(error) {
|
|
|
18422
19266
|
return;
|
|
18423
19267
|
}
|
|
18424
19268
|
// ../aft-bridge/dist/feature-config.js
|
|
18425
|
-
import { createHash as
|
|
19269
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
19270
|
+
import { createRequire } from "node:module";
|
|
18426
19271
|
var CANONICAL_TOOLS = [
|
|
18427
19272
|
"aft_callgraph",
|
|
18428
19273
|
"aft_conflicts",
|
|
@@ -18472,23 +19317,115 @@ var REMOVED_OPENCODE_PATHS = [
|
|
|
18472
19317
|
"opencode.server_url",
|
|
18473
19318
|
"opencode.server_password_env"
|
|
18474
19319
|
];
|
|
18475
|
-
|
|
19320
|
+
var REMOVED_DATA_LAYER_PATHS = [
|
|
19321
|
+
"views.callgraph_copies",
|
|
19322
|
+
"views.enabled",
|
|
19323
|
+
"worktree.ram_overlay"
|
|
19324
|
+
];
|
|
19325
|
+
var RETIRED_IDLE_CHECKOUT_HOURS = [
|
|
19326
|
+
"views.idle_checkout_hours",
|
|
19327
|
+
"idle.checkout_hours"
|
|
19328
|
+
];
|
|
19329
|
+
var DATA_LAYER_KEYS_REFUSED_FROM = [0, 65];
|
|
19330
|
+
var DATA_LAYER_RETIRED_PATHS = ["views.idle_checkout_hours"];
|
|
19331
|
+
var IDLE_CHECKOUT_HOURS_DEFAULT = 168;
|
|
19332
|
+
var IDLE_CHECKOUT_HOURS_MIN = 1;
|
|
19333
|
+
var IDLE_CHECKOUT_HOURS_MAX = 24 * 365;
|
|
19334
|
+
function parseRelease(version) {
|
|
19335
|
+
const match = /^(\d+)\.(\d+)(?:\.|$)/.exec(version);
|
|
19336
|
+
return match ? [Number(match[1]), Number(match[2])] : undefined;
|
|
19337
|
+
}
|
|
19338
|
+
var AFT_PACKAGE_NAMES = new Set([
|
|
19339
|
+
"@cortexkit/aft-bridge",
|
|
19340
|
+
"@cortexkit/aft-opencode",
|
|
19341
|
+
"@cortexkit/aft-pi",
|
|
19342
|
+
"@cortexkit/aft"
|
|
19343
|
+
]);
|
|
19344
|
+
function readRunningRelease() {
|
|
19345
|
+
const req = createRequire(import.meta.url);
|
|
19346
|
+
for (const candidate of ["../package.json", "../../package.json", "../../../package.json"]) {
|
|
19347
|
+
try {
|
|
19348
|
+
const manifest = req(candidate);
|
|
19349
|
+
if (manifest.name && AFT_PACKAGE_NAMES.has(manifest.name) && manifest.version) {
|
|
19350
|
+
return parseRelease(manifest.version) ?? [0, 0];
|
|
19351
|
+
}
|
|
19352
|
+
} catch {}
|
|
19353
|
+
}
|
|
19354
|
+
return [0, 0];
|
|
19355
|
+
}
|
|
19356
|
+
var runningRelease;
|
|
19357
|
+
var runningReleaseOverrideForTest;
|
|
19358
|
+
function getRunningRelease() {
|
|
19359
|
+
if (runningReleaseOverrideForTest)
|
|
19360
|
+
return runningReleaseOverrideForTest;
|
|
19361
|
+
runningRelease ??= readRunningRelease();
|
|
19362
|
+
return runningRelease;
|
|
19363
|
+
}
|
|
19364
|
+
function releaseAtLeast(release, floor) {
|
|
19365
|
+
return release[0] !== floor[0] ? release[0] > floor[0] : release[1] >= floor[1];
|
|
19366
|
+
}
|
|
19367
|
+
function refusedFromVersion() {
|
|
19368
|
+
return `${DATA_LAYER_KEYS_REFUSED_FROM[0]}.${DATA_LAYER_KEYS_REFUSED_FROM[1]}.0`;
|
|
19369
|
+
}
|
|
19370
|
+
function removedConfigKeyErrors(value, release = getRunningRelease(), refuseRetired = true) {
|
|
18476
19371
|
if (!isRecord2(value))
|
|
18477
19372
|
return [];
|
|
18478
|
-
const errors =
|
|
19373
|
+
const errors = removedBlockKeyErrors(value, release, refuseRetired);
|
|
18479
19374
|
if (isRecord2(value.harnesses)) {
|
|
18480
19375
|
for (const [name, block] of Object.entries(value.harnesses)) {
|
|
18481
19376
|
if (!isRecord2(block))
|
|
18482
19377
|
continue;
|
|
18483
|
-
for (const error of
|
|
19378
|
+
for (const error of removedBlockKeyErrors(block, release, refuseRetired)) {
|
|
18484
19379
|
errors.push(error.replace("removed_config_key:", `removed_config_key:harnesses.${name}.`));
|
|
18485
19380
|
}
|
|
18486
19381
|
}
|
|
18487
19382
|
}
|
|
18488
19383
|
return errors;
|
|
18489
19384
|
}
|
|
18490
|
-
function
|
|
18491
|
-
|
|
19385
|
+
function removedBlockKeyErrors(value, release, refuseRetired) {
|
|
19386
|
+
const paths = REMOVED_OPENCODE_PATHS.filter((path) => hasPath(value, path));
|
|
19387
|
+
if (refuseRetired)
|
|
19388
|
+
paths.push(...refusedRetiredNames(value));
|
|
19389
|
+
if (refuseRetired && releaseAtLeast(release, DATA_LAYER_KEYS_REFUSED_FROM))
|
|
19390
|
+
paths.push(...DATA_LAYER_RETIRED_PATHS.filter((path) => hasPath(value, path)));
|
|
19391
|
+
return paths.map((path) => `removed_config_key:${path}; run npx @cortexkit/aft doctor --fix`);
|
|
19392
|
+
}
|
|
19393
|
+
var REFUSED_RETIRED_PATHS = [
|
|
19394
|
+
"sandbox.enabled",
|
|
19395
|
+
"tool_surface",
|
|
19396
|
+
"hoist_builtin_tools",
|
|
19397
|
+
"enabled",
|
|
19398
|
+
"search_index",
|
|
19399
|
+
"experimental_search_index",
|
|
19400
|
+
"semantic_search",
|
|
19401
|
+
"experimental_semantic_search",
|
|
19402
|
+
"callgraph_store",
|
|
19403
|
+
"github.enabled",
|
|
19404
|
+
"gh_read",
|
|
19405
|
+
"gh_shim.enabled",
|
|
19406
|
+
"experimental_lsp_ty",
|
|
19407
|
+
"experimental_bash_rewrite",
|
|
19408
|
+
"experimental_bash_compress",
|
|
19409
|
+
"experimental_bash_background",
|
|
19410
|
+
"experimental.bash",
|
|
19411
|
+
"idle.lsp_ttl_minutes",
|
|
19412
|
+
"inspect.tier2_soft_deadline_ms",
|
|
19413
|
+
"inspect.max_drill_down_items",
|
|
19414
|
+
"views.callgraph_copies",
|
|
19415
|
+
"views.enabled",
|
|
19416
|
+
"worktree.ram_overlay"
|
|
19417
|
+
];
|
|
19418
|
+
function refusedRetiredNames(block) {
|
|
19419
|
+
const names = REFUSED_RETIRED_PATHS.filter((path) => hasPath(block, path));
|
|
19420
|
+
if (Array.isArray(block.disabled_tools)) {
|
|
19421
|
+
for (const name of block.disabled_tools) {
|
|
19422
|
+
if (typeof name !== "string" || !Object.hasOwn(LEGACY_TOOL_ALIASES, name))
|
|
19423
|
+
continue;
|
|
19424
|
+
if (!names.includes(name))
|
|
19425
|
+
names.push(name);
|
|
19426
|
+
}
|
|
19427
|
+
}
|
|
19428
|
+
return names;
|
|
18492
19429
|
}
|
|
18493
19430
|
var RETIRED_PATHS = [
|
|
18494
19431
|
["sandbox.enabled", "sandbox.profile"],
|
|
@@ -18643,7 +19580,10 @@ function translateInspectLspPaths(map, prefix, out) {
|
|
|
18643
19580
|
recordRetired(out, prefix, `inspect.${leaf}`);
|
|
18644
19581
|
}
|
|
18645
19582
|
}
|
|
18646
|
-
var REMOVED_NO_EFFECT_PATHS = [
|
|
19583
|
+
var REMOVED_NO_EFFECT_PATHS = [
|
|
19584
|
+
"bridge.hang_threshold",
|
|
19585
|
+
"inspect.tier2_idle_minutes"
|
|
19586
|
+
];
|
|
18647
19587
|
function dropNoEffectPaths(map, prefix, out) {
|
|
18648
19588
|
for (const path of REMOVED_NO_EFFECT_PATHS) {
|
|
18649
19589
|
const [container, leaf] = path.split(".");
|
|
@@ -18652,6 +19592,30 @@ function dropNoEffectPaths(map, prefix, out) {
|
|
|
18652
19592
|
}
|
|
18653
19593
|
}
|
|
18654
19594
|
}
|
|
19595
|
+
function retiredIdleCheckoutHours(value) {
|
|
19596
|
+
const raw = typeof value === "number" && Number.isSafeInteger(value) ? value : IDLE_CHECKOUT_HOURS_DEFAULT;
|
|
19597
|
+
return Math.min(IDLE_CHECKOUT_HOURS_MAX, Math.max(IDLE_CHECKOUT_HOURS_MIN, raw));
|
|
19598
|
+
}
|
|
19599
|
+
function translateDataLayerPaths(map, prefix, out) {
|
|
19600
|
+
const [retired, replacement] = RETIRED_IDLE_CHECKOUT_HOURS;
|
|
19601
|
+
const hours = takeNested(map, "views", "idle_checkout_hours");
|
|
19602
|
+
if (hours.found) {
|
|
19603
|
+
recordRetired(out, prefix, retired);
|
|
19604
|
+
if (isRecord2(map.idle) && map.idle.checkout_hours !== undefined) {
|
|
19605
|
+
superseded(out, `${prefix}${retired}`, `${retired} is ignored because ${replacement} is set`);
|
|
19606
|
+
} else {
|
|
19607
|
+
if (!Object.hasOwn(map, "idle"))
|
|
19608
|
+
map.idle = {};
|
|
19609
|
+
if (isRecord2(map.idle))
|
|
19610
|
+
map.idle.checkout_hours = retiredIdleCheckoutHours(hours.value);
|
|
19611
|
+
}
|
|
19612
|
+
}
|
|
19613
|
+
for (const path of REMOVED_DATA_LAYER_PATHS) {
|
|
19614
|
+
const [container, leaf] = path.split(".");
|
|
19615
|
+
if (container && leaf && takeNested(map, container, leaf).found)
|
|
19616
|
+
recordRetired(out, prefix, path);
|
|
19617
|
+
}
|
|
19618
|
+
}
|
|
18655
19619
|
function translateExperimentalPaths(map, prefix, out) {
|
|
18656
19620
|
for (const [oldKey, leaf] of [
|
|
18657
19621
|
["experimental_lsp_ty", "lsp_ty"],
|
|
@@ -18684,23 +19648,25 @@ function translateExperimentalPaths(map, prefix, out) {
|
|
|
18684
19648
|
const experimental = map.experimental;
|
|
18685
19649
|
const legacy = experimental.bash;
|
|
18686
19650
|
if (isRecord2(legacy)) {
|
|
18687
|
-
if (
|
|
18688
|
-
|
|
19651
|
+
if (Object.keys(legacy).length === 0)
|
|
19652
|
+
recordRetired(out, prefix, "experimental.bash");
|
|
18689
19653
|
for (const leaf of Object.keys(legacy))
|
|
18690
19654
|
recordRetired(out, prefix, `experimental.bash.${leaf}`);
|
|
18691
19655
|
if (Object.hasOwn(map, "bash")) {
|
|
18692
19656
|
superseded(out, "experimental.bash", 'experimental.bash is ignored because top-level "bash" is already set');
|
|
18693
19657
|
} else {
|
|
18694
|
-
const bash = {
|
|
18695
|
-
|
|
18696
|
-
|
|
18697
|
-
|
|
18698
|
-
|
|
19658
|
+
const bash = {};
|
|
19659
|
+
if (["rewrite", "compress", "background"].some((leaf) => Object.hasOwn(legacy, leaf))) {
|
|
19660
|
+
bash.rewrite = legacy.rewrite === true;
|
|
19661
|
+
bash.compress = legacy.compress === true;
|
|
19662
|
+
bash.background = legacy.background === true;
|
|
19663
|
+
}
|
|
18699
19664
|
for (const leaf of ["long_running_reminder_enabled", "long_running_reminder_interval_ms"]) {
|
|
18700
19665
|
if (Object.hasOwn(legacy, leaf))
|
|
18701
19666
|
bash[leaf] = legacy[leaf];
|
|
18702
19667
|
}
|
|
18703
|
-
|
|
19668
|
+
if (Object.keys(bash).length > 0)
|
|
19669
|
+
map.bash = bash;
|
|
18704
19670
|
}
|
|
18705
19671
|
} else {
|
|
18706
19672
|
recordRetired(out, prefix, "experimental.bash");
|
|
@@ -18713,6 +19679,7 @@ function translateBlock(map, userBase, blockLabel, out) {
|
|
|
18713
19679
|
const prefix = blockLabel === "base" ? "" : `${blockLabel}.`;
|
|
18714
19680
|
translateGithubAliases(map, prefix, out);
|
|
18715
19681
|
translateInspectLspPaths(map, prefix, out);
|
|
19682
|
+
translateDataLayerPaths(map, prefix, out);
|
|
18716
19683
|
dropNoEffectPaths(map, prefix, out);
|
|
18717
19684
|
translateExperimentalPaths(map, prefix, out);
|
|
18718
19685
|
if (isRecord2(map.sandbox) && Object.hasOwn(map.sandbox, "enabled")) {
|
|
@@ -18751,8 +19718,6 @@ function translateBlock(map, userBase, blockLabel, out) {
|
|
|
18751
19718
|
});
|
|
18752
19719
|
}
|
|
18753
19720
|
for (const [path] of RETIRED_PATHS) {
|
|
18754
|
-
if (path === "experimental.bash")
|
|
18755
|
-
continue;
|
|
18756
19721
|
if (hasPath(map, path))
|
|
18757
19722
|
recordRetired(out, prefix, path);
|
|
18758
19723
|
}
|
|
@@ -18888,13 +19853,38 @@ var NO_EFFECT_RETIRED_NOTES = [
|
|
|
18888
19853
|
"inspect.max_drill_down_items",
|
|
18889
19854
|
"inspect.max_drill_down_items never had an effect, so the setting can be removed."
|
|
18890
19855
|
],
|
|
19856
|
+
[
|
|
19857
|
+
"inspect.tier2_idle_minutes",
|
|
19858
|
+
"inspect.tier2_idle_minutes no longer does anything: AFT runs whole-repository analyses only when aft_inspect is called, so the setting can be removed."
|
|
19859
|
+
],
|
|
18891
19860
|
[
|
|
18892
19861
|
"inspect.tier2_soft_deadline_ms",
|
|
18893
19862
|
"inspect.tier2_soft_deadline_ms never had an effect, so the setting can be removed."
|
|
18894
19863
|
]
|
|
18895
19864
|
];
|
|
18896
19865
|
function noEffectNote(key) {
|
|
18897
|
-
return NO_EFFECT_RETIRED_NOTES.find(([path]) => key
|
|
19866
|
+
return [...NO_EFFECT_RETIRED_NOTES, ...REMOVED_DATA_LAYER_NOTES].find(([path]) => namesPath(key, path))?.[1];
|
|
19867
|
+
}
|
|
19868
|
+
function namesPath(key, path) {
|
|
19869
|
+
return key === path || key.endsWith(`.${path}`);
|
|
19870
|
+
}
|
|
19871
|
+
var REMOVED_DATA_LAYER_NOTES = [
|
|
19872
|
+
[
|
|
19873
|
+
"views.callgraph_copies",
|
|
19874
|
+
"views.callgraph_copies is gone because no checkout borrows another checkout's call graph any more."
|
|
19875
|
+
],
|
|
19876
|
+
[
|
|
19877
|
+
"views.enabled",
|
|
19878
|
+
"views.enabled is gone because AFT now keeps one index store per repository and always uses it."
|
|
19879
|
+
],
|
|
19880
|
+
[
|
|
19881
|
+
"worktree.ram_overlay",
|
|
19882
|
+
"worktree.ram_overlay is gone because every worktree now has its own index state, so there is no borrowed index to patch in memory."
|
|
19883
|
+
]
|
|
19884
|
+
];
|
|
19885
|
+
function retiredIdleCheckoutHoursNote() {
|
|
19886
|
+
const [retired, replacement] = RETIRED_IDLE_CHECKOUT_HOURS;
|
|
19887
|
+
return `${retired} is now ${replacement}; from ${refusedFromVersion()} AFT refuses a user config file that still sets ${retired}.`;
|
|
18898
19888
|
}
|
|
18899
19889
|
function legacyConfigNoticeMessage(configPath, translation) {
|
|
18900
19890
|
const equivalents = translation.retiredKeys.filter((key) => noEffectNote(key) === undefined);
|
|
@@ -18904,8 +19894,11 @@ function legacyConfigNoticeMessage(configPath, translation) {
|
|
|
18904
19894
|
parts.push(`${configPath} uses retired keys (${equivalents.join(", ")}); AFT applied their current equivalents, with the same limits a project config has for those keys. Run \`npx @cortexkit/aft doctor --fix\` to update the file.`);
|
|
18905
19895
|
if (translation.retiredEnabledFalse)
|
|
18906
19896
|
parts.push(RETIRED_ENABLED_FALSE_INDEXES_NOTE);
|
|
19897
|
+
if (equivalents.some((key) => namesPath(key, RETIRED_IDLE_CHECKOUT_HOURS[0]))) {
|
|
19898
|
+
parts.push(retiredIdleCheckoutHoursNote());
|
|
19899
|
+
}
|
|
18907
19900
|
}
|
|
18908
|
-
for (const [, note] of NO_EFFECT_RETIRED_NOTES) {
|
|
19901
|
+
for (const [, note] of [...NO_EFFECT_RETIRED_NOTES, ...REMOVED_DATA_LAYER_NOTES]) {
|
|
18909
19902
|
if (noEffect.has(note))
|
|
18910
19903
|
parts.push(note);
|
|
18911
19904
|
}
|
|
@@ -18961,7 +19954,12 @@ function validateResolvedConfig(value) {
|
|
|
18961
19954
|
var ABSENT = { absent: true };
|
|
18962
19955
|
function projectBlock(block) {
|
|
18963
19956
|
const legacyPaths = {};
|
|
18964
|
-
for (const path of [
|
|
19957
|
+
for (const path of [
|
|
19958
|
+
...RETIRED_PATHS.map(([retired]) => retired),
|
|
19959
|
+
...REMOVED_NO_EFFECT_PATHS,
|
|
19960
|
+
...DATA_LAYER_RETIRED_PATHS,
|
|
19961
|
+
...REMOVED_DATA_LAYER_PATHS
|
|
19962
|
+
]) {
|
|
18965
19963
|
const dot = path.indexOf(".");
|
|
18966
19964
|
if (dot < 0) {
|
|
18967
19965
|
if (Object.hasOwn(block, path))
|
|
@@ -19040,7 +20038,7 @@ function canonicalJson(value) {
|
|
|
19040
20038
|
return JSON.stringify(value);
|
|
19041
20039
|
}
|
|
19042
20040
|
function noticeDigest(projection) {
|
|
19043
|
-
return
|
|
20041
|
+
return createHash5("sha256").update(canonicalJson(projection), "utf8").digest("hex");
|
|
19044
20042
|
}
|
|
19045
20043
|
var SEMANTIC_COST_NOTICE = "AFT indexes now default on; the local semantic backend may download an ONNX runtime and model and use CPU. Run npx @cortexkit/aft setup to change indexes.semantic.";
|
|
19046
20044
|
var SEMANTIC_COST_NOTICE_DIGEST = noticeDigest({ notice: "semantic_default_cost_v1" });
|
|
@@ -19913,9 +20911,9 @@ async function createConcreteAftTransportPool(opts) {
|
|
|
19913
20911
|
var LIFECYCLE_STATE = Symbol.for("@cortexkit/aft-bridge/location-lifecycle/v1");
|
|
19914
20912
|
var RELEASE_LOCATION = Symbol.for("@cortexkit/aft-bridge/location-release/v1");
|
|
19915
20913
|
// ../aft-bridge/dist/lsp-github-install.js
|
|
19916
|
-
import { createHash as
|
|
19917
|
-
import { closeSync as
|
|
19918
|
-
import { dirname as dirname4, join as
|
|
20914
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
20915
|
+
import { closeSync as closeSync6, copyFileSync as copyFileSync3, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync4, openSync as openSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, readlinkSync, readSync as readSync5, realpathSync as realpathSync4, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20916
|
+
import { dirname as dirname4, join as join10, relative as relative3, resolve as resolve7 } from "node:path";
|
|
19919
20917
|
import { Readable as Readable2 } from "node:stream";
|
|
19920
20918
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
19921
20919
|
|
|
@@ -20134,13 +21132,13 @@ function detectHostPlatform() {
|
|
|
20134
21132
|
|
|
20135
21133
|
// ../aft-bridge/dist/tar-executable.js
|
|
20136
21134
|
import { existsSync as existsSync5 } from "node:fs";
|
|
20137
|
-
import { join as
|
|
21135
|
+
import { join as join9 } from "node:path";
|
|
20138
21136
|
function windowsTarExecutable(options = {}) {
|
|
20139
21137
|
const platform = options.platform ?? process.platform;
|
|
20140
21138
|
if (platform !== "win32")
|
|
20141
21139
|
return "tar";
|
|
20142
21140
|
const env = options.env ?? process.env;
|
|
20143
|
-
const systemTar =
|
|
21141
|
+
const systemTar = join9(env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe");
|
|
20144
21142
|
return (options.exists ?? existsSync5)(systemTar) ? systemTar : "tar";
|
|
20145
21143
|
}
|
|
20146
21144
|
function extractionCommand(args, privateStorage) {
|
|
@@ -20184,14 +21182,14 @@ function createGithubInstaller(services) {
|
|
|
20184
21182
|
return getAftLspBinariesDir();
|
|
20185
21183
|
}
|
|
20186
21184
|
function ghPackageDir(spec) {
|
|
20187
|
-
return
|
|
21185
|
+
return join10(ghCacheRoot(), spec.id);
|
|
20188
21186
|
}
|
|
20189
21187
|
function ghBinDir(spec) {
|
|
20190
21188
|
const platform = detectHostPlatform()?.platform ?? "linux";
|
|
20191
21189
|
return dirname4(ghBinaryPath(spec, platform));
|
|
20192
21190
|
}
|
|
20193
21191
|
function ghExtractDir(spec) {
|
|
20194
|
-
return
|
|
21192
|
+
return join10(ghPackageDir(spec), "extracted");
|
|
20195
21193
|
}
|
|
20196
21194
|
const INSTALLED_META_FILE = ".aft-installed";
|
|
20197
21195
|
const INSTALL_FAILED_FILE = ".aft-install-failed.json";
|
|
@@ -20199,8 +21197,9 @@ function createGithubInstaller(services) {
|
|
|
20199
21197
|
const INSTALL_LOCK_WAIT_MS = 10 * 60 * 1000;
|
|
20200
21198
|
const INSTALL_LOCK_POLL_INTERVAL_MS = 1000;
|
|
20201
21199
|
const INSTALL_LOCK_WAIT_TIMEOUT_REASON = "install lock held by another process for 10 minutes";
|
|
21200
|
+
const SMOKE_TIMEOUT_MS = 2000;
|
|
20202
21201
|
function installFailureMarkerPath(spec) {
|
|
20203
|
-
return
|
|
21202
|
+
return join10(ghPackageDir(spec), INSTALL_FAILED_FILE);
|
|
20204
21203
|
}
|
|
20205
21204
|
function readInstallFailureMarker(spec) {
|
|
20206
21205
|
try {
|
|
@@ -20244,15 +21243,15 @@ function createGithubInstaller(services) {
|
|
|
20244
21243
|
const meta = readGithubInstalledMetaIn(ghPackageDir(spec));
|
|
20245
21244
|
if (spec.layout === "distribution" && meta?.layout === "distribution" && isSafeVersion(meta.version)) {
|
|
20246
21245
|
const arch = detectHostPlatform()?.arch ?? "x64";
|
|
20247
|
-
return
|
|
21246
|
+
return join10(ghExtractDir(spec), spec.binaryPathInArchive(platform, arch, stripTagV(meta.version)));
|
|
20248
21247
|
}
|
|
20249
21248
|
const ext = platform === "win32" ? ".exe" : "";
|
|
20250
|
-
return
|
|
21249
|
+
return join10(ghPackageDir(spec), "bin", `${spec.binary}${ext}`);
|
|
20251
21250
|
}
|
|
20252
21251
|
function isGithubInstalled(spec, platform) {
|
|
20253
21252
|
for (const candidate of ghBinaryCandidates(spec, platform)) {
|
|
20254
21253
|
try {
|
|
20255
|
-
if (statSync6(
|
|
21254
|
+
if (statSync6(join10(ghBinDir(spec), candidate)).isFile())
|
|
20256
21255
|
return true;
|
|
20257
21256
|
} catch {}
|
|
20258
21257
|
}
|
|
@@ -20265,7 +21264,7 @@ function createGithubInstaller(services) {
|
|
|
20265
21264
|
}
|
|
20266
21265
|
function readGithubInstalledMetaIn(installDir) {
|
|
20267
21266
|
try {
|
|
20268
|
-
const path =
|
|
21267
|
+
const path = join10(installDir, INSTALLED_META_FILE);
|
|
20269
21268
|
if (!statSync6(path).isFile())
|
|
20270
21269
|
return null;
|
|
20271
21270
|
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
@@ -20296,7 +21295,7 @@ function createGithubInstaller(services) {
|
|
|
20296
21295
|
...layout ? { layout } : {},
|
|
20297
21296
|
...smoke ? { smoke } : {}
|
|
20298
21297
|
};
|
|
20299
|
-
const path =
|
|
21298
|
+
const path = join10(installDir, INSTALLED_META_FILE);
|
|
20300
21299
|
const tempPath = `${path}.tmp-${process.pid}-${randomBytes2(6).toString("hex")}`;
|
|
20301
21300
|
try {
|
|
20302
21301
|
writeFileSync2(tempPath, JSON.stringify(meta), { encoding: "utf8", flag: "wx" });
|
|
@@ -20312,7 +21311,7 @@ function createGithubInstaller(services) {
|
|
|
20312
21311
|
const MAX_EXTRACT_BYTES = 1024 * 1024 * 1024;
|
|
20313
21312
|
function sha256OfFile(path) {
|
|
20314
21313
|
return new Promise((resolve, reject) => {
|
|
20315
|
-
const hash =
|
|
21314
|
+
const hash = createHash6("sha256");
|
|
20316
21315
|
const stream = createReadStream2(path);
|
|
20317
21316
|
stream.on("error", reject);
|
|
20318
21317
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
@@ -20320,17 +21319,17 @@ function createGithubInstaller(services) {
|
|
|
20320
21319
|
});
|
|
20321
21320
|
}
|
|
20322
21321
|
function sha256OfFileSync(path) {
|
|
20323
|
-
const hash =
|
|
20324
|
-
const fd =
|
|
21322
|
+
const hash = createHash6("sha256");
|
|
21323
|
+
const fd = openSync5(path, "r");
|
|
20325
21324
|
try {
|
|
20326
21325
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
20327
|
-
let read =
|
|
21326
|
+
let read = readSync5(fd, buffer, 0, buffer.length, null);
|
|
20328
21327
|
while (read > 0) {
|
|
20329
21328
|
hash.update(buffer.subarray(0, read));
|
|
20330
|
-
read =
|
|
21329
|
+
read = readSync5(fd, buffer, 0, buffer.length, null);
|
|
20331
21330
|
}
|
|
20332
21331
|
} finally {
|
|
20333
|
-
|
|
21332
|
+
closeSync6(fd);
|
|
20334
21333
|
}
|
|
20335
21334
|
return hash.digest("hex");
|
|
20336
21335
|
}
|
|
@@ -20538,7 +21537,7 @@ function createGithubInstaller(services) {
|
|
|
20538
21537
|
throw new Error(`too many redirects (>${maxRedirects})`);
|
|
20539
21538
|
}
|
|
20540
21539
|
function validateExtraction(stagingRoot) {
|
|
20541
|
-
const realStagingRoot =
|
|
21540
|
+
const realStagingRoot = realpathSync4(stagingRoot);
|
|
20542
21541
|
let totalBytes = 0;
|
|
20543
21542
|
const walk = (dir) => {
|
|
20544
21543
|
let entries;
|
|
@@ -20548,7 +21547,7 @@ function createGithubInstaller(services) {
|
|
|
20548
21547
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
20549
21548
|
}
|
|
20550
21549
|
for (const entry of entries) {
|
|
20551
|
-
const full =
|
|
21550
|
+
const full = join10(dir, entry);
|
|
20552
21551
|
let lst;
|
|
20553
21552
|
try {
|
|
20554
21553
|
lst = lstatSync2(full);
|
|
@@ -20560,15 +21559,15 @@ function createGithubInstaller(services) {
|
|
|
20560
21559
|
try {
|
|
20561
21560
|
target = readlinkSync(full);
|
|
20562
21561
|
} catch {}
|
|
20563
|
-
throw new Error(`archive contains symlink ${
|
|
21562
|
+
throw new Error(`archive contains symlink ${relative3(realStagingRoot, full)} → ${target}; rejecting (zip-slip defense)`);
|
|
20564
21563
|
}
|
|
20565
21564
|
let realFull;
|
|
20566
21565
|
try {
|
|
20567
|
-
realFull =
|
|
21566
|
+
realFull = realpathSync4(full);
|
|
20568
21567
|
} catch (err) {
|
|
20569
21568
|
throw new Error(`failed to realpath ${full}: ${err}`);
|
|
20570
21569
|
}
|
|
20571
|
-
const rel =
|
|
21570
|
+
const rel = relative3(realStagingRoot, realFull);
|
|
20572
21571
|
if (relativePathEscapesRoot(rel) || resolve7(realStagingRoot, rel) !== realFull) {
|
|
20573
21572
|
throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
|
|
20574
21573
|
}
|
|
@@ -20709,7 +21708,7 @@ function createGithubInstaller(services) {
|
|
|
20709
21708
|
}
|
|
20710
21709
|
function quarantineCachedGithubInstall(spec, reason) {
|
|
20711
21710
|
const packageDir = ghPackageDir(spec);
|
|
20712
|
-
const dest =
|
|
21711
|
+
const dest = join10(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
20713
21712
|
warn(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
20714
21713
|
try {
|
|
20715
21714
|
mkdirSync4(dirname4(dest), { recursive: true });
|
|
@@ -20719,7 +21718,7 @@ function createGithubInstaller(services) {
|
|
|
20719
21718
|
warn(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
20720
21719
|
}
|
|
20721
21720
|
}
|
|
20722
|
-
async function smokeGithubBinary(spec, binaryPath) {
|
|
21721
|
+
async function smokeGithubBinary(spec, binaryPath, timeoutMs = SMOKE_TIMEOUT_MS) {
|
|
20723
21722
|
return new Promise((resolveSmoke) => {
|
|
20724
21723
|
let child;
|
|
20725
21724
|
let settled = false;
|
|
@@ -20739,8 +21738,8 @@ function createGithubInstaller(services) {
|
|
|
20739
21738
|
const timer = setTimeout(() => {
|
|
20740
21739
|
child?.kill("SIGKILL");
|
|
20741
21740
|
child?.unref();
|
|
20742
|
-
finish("timeout",
|
|
20743
|
-
},
|
|
21741
|
+
finish("timeout", `exceeded ${timeoutMs} ms; keeping install`);
|
|
21742
|
+
}, timeoutMs);
|
|
20744
21743
|
const capture = (chunk) => {
|
|
20745
21744
|
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
20746
21745
|
const available = 64 * 1024 - output.length;
|
|
@@ -20764,19 +21763,19 @@ function createGithubInstaller(services) {
|
|
|
20764
21763
|
function hasRequiredRuntime(spec, extractDir, version) {
|
|
20765
21764
|
return (spec.requiredRuntimePaths?.(stripTagV(version)) ?? []).every(({ path, kind }) => {
|
|
20766
21765
|
try {
|
|
20767
|
-
const info = statSync6(
|
|
21766
|
+
const info = statSync6(join10(extractDir, path));
|
|
20768
21767
|
return kind === "file" ? info.isFile() : info.isDirectory();
|
|
20769
21768
|
} catch {
|
|
20770
21769
|
return false;
|
|
20771
21770
|
}
|
|
20772
21771
|
});
|
|
20773
21772
|
}
|
|
20774
|
-
function validateCachedGithubInstall(spec, platform) {
|
|
21773
|
+
function validateCachedGithubInstall(spec, platform, smokeTimeoutMs = SMOKE_TIMEOUT_MS) {
|
|
20775
21774
|
const lease = acquireInstallLock(spec.githubRepo);
|
|
20776
21775
|
if (!lease)
|
|
20777
21776
|
return null;
|
|
20778
21777
|
try {
|
|
20779
|
-
const validation = validateCachedGithubInstallUnderLock(spec, platform);
|
|
21778
|
+
const validation = validateCachedGithubInstallUnderLock(spec, platform, smokeTimeoutMs);
|
|
20780
21779
|
if (typeof validation !== "boolean")
|
|
20781
21780
|
return validation.finally(() => lease.release());
|
|
20782
21781
|
lease.release();
|
|
@@ -20786,10 +21785,10 @@ function createGithubInstaller(services) {
|
|
|
20786
21785
|
throw err;
|
|
20787
21786
|
}
|
|
20788
21787
|
}
|
|
20789
|
-
function validateCachedGithubInstallUnderLock(spec, platform) {
|
|
21788
|
+
function validateCachedGithubInstallUnderLock(spec, platform, smokeTimeoutMs = SMOKE_TIMEOUT_MS) {
|
|
20790
21789
|
const packageDir = ghPackageDir(spec);
|
|
20791
21790
|
const meta = readGithubInstalledMetaIn(packageDir);
|
|
20792
|
-
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) =>
|
|
21791
|
+
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join10(ghBinDir(spec), candidate)).find((candidate) => {
|
|
20793
21792
|
try {
|
|
20794
21793
|
return statSync6(candidate).isFile();
|
|
20795
21794
|
} catch {
|
|
@@ -20819,7 +21818,7 @@ function createGithubInstaller(services) {
|
|
|
20819
21818
|
quarantineCachedGithubInstall(spec, "missing required runtime files");
|
|
20820
21819
|
return false;
|
|
20821
21820
|
}
|
|
20822
|
-
const verdict = await smokeGithubBinary(spec, binaryPath);
|
|
21821
|
+
const verdict = await smokeGithubBinary(spec, binaryPath, smokeTimeoutMs);
|
|
20823
21822
|
if (verdict === "failed") {
|
|
20824
21823
|
quarantineCachedGithubInstall(spec, "cached binary failed runtime smoke check");
|
|
20825
21824
|
return false;
|
|
@@ -20865,7 +21864,7 @@ function createGithubInstaller(services) {
|
|
|
20865
21864
|
}
|
|
20866
21865
|
const pkgDir = ghPackageDir(spec);
|
|
20867
21866
|
const extractDir = ghExtractDir(spec);
|
|
20868
|
-
const archivePath =
|
|
21867
|
+
const archivePath = join10(pkgDir, expected.name);
|
|
20869
21868
|
log(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
|
|
20870
21869
|
try {
|
|
20871
21870
|
await downloadFile(matchingAsset.url, archivePath, fetchImpl, matchingAsset.size, signal);
|
|
@@ -20901,7 +21900,7 @@ function createGithubInstaller(services) {
|
|
|
20901
21900
|
const binaryRelativePath = spec.binaryPathInArchive(platform, arch, version);
|
|
20902
21901
|
try {
|
|
20903
21902
|
await extractArchiveSafely(archivePath, extractDir, expected.archive, async (stagingDir) => {
|
|
20904
|
-
const stagedBinary =
|
|
21903
|
+
const stagedBinary = join10(stagingDir, binaryRelativePath);
|
|
20905
21904
|
if (!existsSync6(stagedBinary) || !statSync6(stagedBinary).isFile()) {
|
|
20906
21905
|
throw new Error(`extracted binary not found at ${stagedBinary}`);
|
|
20907
21906
|
}
|
|
@@ -20931,7 +21930,7 @@ function createGithubInstaller(services) {
|
|
|
20931
21930
|
unlinkSync3(archivePath);
|
|
20932
21931
|
} catch {}
|
|
20933
21932
|
}
|
|
20934
|
-
const innerBinaryPath =
|
|
21933
|
+
const innerBinaryPath = join10(extractDir, binaryRelativePath);
|
|
20935
21934
|
const targetBinary = spec.layout === "distribution" ? innerBinaryPath : ghBinaryPath(spec, platform);
|
|
20936
21935
|
mkdirSync4(dirname4(targetBinary), { recursive: true });
|
|
20937
21936
|
try {
|
|
@@ -21058,6 +22057,7 @@ function createGithubInstaller(services) {
|
|
|
21058
22057
|
let installsStarted = 0;
|
|
21059
22058
|
const lockWaitTimeoutMs = Math.max(0, options.__lockWaitTimeoutMsForTests ?? INSTALL_LOCK_WAIT_MS);
|
|
21060
22059
|
const lockPollIntervalMs = Math.max(1, options.__lockPollIntervalMsForTests ?? INSTALL_LOCK_POLL_INTERVAL_MS);
|
|
22060
|
+
const smokeTimeoutMs = Math.max(1, options.__smokeTimeoutMsForTests ?? SMOKE_TIMEOUT_MS);
|
|
21061
22061
|
function notifyCachedBinDirsChanged(directory) {
|
|
21062
22062
|
try {
|
|
21063
22063
|
options.onCachedBinDirsChanged?.(directory);
|
|
@@ -21070,7 +22070,7 @@ function createGithubInstaller(services) {
|
|
|
21070
22070
|
while (true) {
|
|
21071
22071
|
if (signal.aborted)
|
|
21072
22072
|
return null;
|
|
21073
|
-
const validation = await withInstallLock(spec.githubRepo, async () => validateCachedGithubInstallUnderLock(spec, platform));
|
|
22073
|
+
const validation = await withInstallLock(spec.githubRepo, async () => validateCachedGithubInstallUnderLock(spec, platform, smokeTimeoutMs));
|
|
21074
22074
|
if (validation !== null)
|
|
21075
22075
|
return validation;
|
|
21076
22076
|
if (signal.aborted)
|
|
@@ -21099,7 +22099,7 @@ function createGithubInstaller(services) {
|
|
|
21099
22099
|
}
|
|
21100
22100
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
21101
22101
|
const controller = new AbortController;
|
|
21102
|
-
const validation = isGithubInstalled(spec, host.platform) ? validateCachedGithubInstall(spec, host.platform) : false;
|
|
22102
|
+
const validation = isGithubInstalled(spec, host.platform) ? validateCachedGithubInstall(spec, host.platform, smokeTimeoutMs) : false;
|
|
21103
22103
|
if (validation === true)
|
|
21104
22104
|
cachedBinDirs.push(ghBinDir(spec));
|
|
21105
22105
|
const cacheReady = (validation === null ? retryBusyCacheValidation(spec, host.platform, controller.signal) : Promise.resolve(validation)).then((healthy) => {
|
|
@@ -21154,7 +22154,7 @@ function createGithubInstaller(services) {
|
|
|
21154
22154
|
const currentHost = detectHostPlatform();
|
|
21155
22155
|
if (!currentHost)
|
|
21156
22156
|
return [];
|
|
21157
|
-
const directories = await Promise.all(GITHUB_LSP_TABLE.map(async (spec) => isGithubInstalled(spec, currentHost.platform) && await validateCachedGithubInstall(spec, currentHost.platform) ? ghBinDir(spec) : null));
|
|
22157
|
+
const directories = await Promise.all(GITHUB_LSP_TABLE.map(async (spec) => isGithubInstalled(spec, currentHost.platform) && await validateCachedGithubInstall(spec, currentHost.platform, smokeTimeoutMs) ? ghBinDir(spec) : null));
|
|
21158
22158
|
return directories.filter((directory) => directory !== null);
|
|
21159
22159
|
}
|
|
21160
22160
|
};
|
|
@@ -21225,7 +22225,7 @@ import { stat as stat2 } from "node:fs/promises";
|
|
|
21225
22225
|
// ../aft-bridge/dist/npm-resolver.js
|
|
21226
22226
|
import { readdirSync as readdirSync3, statSync as statSync7 } from "node:fs";
|
|
21227
22227
|
import { homedir as homedir9 } from "node:os";
|
|
21228
|
-
import { dirname as dirname5, isAbsolute as
|
|
22228
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join11 } from "node:path";
|
|
21229
22229
|
function defaultDeps() {
|
|
21230
22230
|
return {
|
|
21231
22231
|
platform: process.platform,
|
|
@@ -21252,16 +22252,16 @@ function npmFromPath(deps) {
|
|
|
21252
22252
|
const separator = deps.platform === "win32" ? ";" : ":";
|
|
21253
22253
|
for (const entry of raw.split(separator)) {
|
|
21254
22254
|
const dir = entry.trim().replace(/^"|"$/g, "");
|
|
21255
|
-
if (!dir || !
|
|
22255
|
+
if (!dir || !isAbsolute4(dir))
|
|
21256
22256
|
continue;
|
|
21257
|
-
if (isFile(
|
|
22257
|
+
if (isFile(join11(dir, name)))
|
|
21258
22258
|
return dir;
|
|
21259
22259
|
}
|
|
21260
22260
|
return null;
|
|
21261
22261
|
}
|
|
21262
22262
|
function npmAdjacentToNode(deps) {
|
|
21263
22263
|
const dir = dirname5(deps.execPath);
|
|
21264
|
-
return isFile(
|
|
22264
|
+
return isFile(join11(dir, npmBinaryName(deps.platform))) ? dir : null;
|
|
21265
22265
|
}
|
|
21266
22266
|
function highestVersionedNodeBin(installsDir, name) {
|
|
21267
22267
|
let entries;
|
|
@@ -21270,8 +22270,8 @@ function highestVersionedNodeBin(installsDir, name) {
|
|
|
21270
22270
|
} catch {
|
|
21271
22271
|
return null;
|
|
21272
22272
|
}
|
|
21273
|
-
const candidates = entries.filter((v) => isFile(
|
|
21274
|
-
return candidates.length > 0 ?
|
|
22273
|
+
const candidates = entries.filter((v) => isFile(join11(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
|
|
22274
|
+
return candidates.length > 0 ? join11(installsDir, candidates[0], "bin") : null;
|
|
21275
22275
|
}
|
|
21276
22276
|
function compareVersionsDesc(a, b) {
|
|
21277
22277
|
const pa = a.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
|
|
@@ -21296,22 +22296,22 @@ function wellKnownNpmDirs(deps) {
|
|
|
21296
22296
|
const programFiles = env.ProgramFiles || "C:\\Program Files";
|
|
21297
22297
|
const appData = env.APPDATA;
|
|
21298
22298
|
const localAppData = env.LOCALAPPDATA;
|
|
21299
|
-
push(
|
|
22299
|
+
push(join11(programFiles, "nodejs"));
|
|
21300
22300
|
if (appData)
|
|
21301
|
-
push(
|
|
22301
|
+
push(join11(appData, "npm"));
|
|
21302
22302
|
if (localAppData)
|
|
21303
|
-
push(
|
|
22303
|
+
push(join11(localAppData, "Volta", "bin"));
|
|
21304
22304
|
if (env.NVM_SYMLINK)
|
|
21305
22305
|
push(env.NVM_SYMLINK);
|
|
21306
22306
|
} else {
|
|
21307
22307
|
if (env.NVM_BIN)
|
|
21308
22308
|
push(env.NVM_BIN);
|
|
21309
|
-
push(highestVersionedNodeBin(
|
|
21310
|
-
push(highestVersionedNodeBin(
|
|
21311
|
-
push(highestVersionedNodeBin(
|
|
21312
|
-
push(
|
|
21313
|
-
push(
|
|
21314
|
-
const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin",
|
|
22309
|
+
push(highestVersionedNodeBin(join11(home, ".nvm", "versions", "node"), name));
|
|
22310
|
+
push(highestVersionedNodeBin(join11(home, ".local", "share", "mise", "installs", "node"), name));
|
|
22311
|
+
push(highestVersionedNodeBin(join11(home, ".asdf", "installs", "nodejs"), name));
|
|
22312
|
+
push(join11(home, ".volta", "bin"));
|
|
22313
|
+
push(join11(home, ".asdf", "shims"));
|
|
22314
|
+
const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join11(home, ".local", "bin")]);
|
|
21315
22315
|
for (const dir of systemDirs)
|
|
21316
22316
|
push(dir);
|
|
21317
22317
|
}
|
|
@@ -21321,17 +22321,32 @@ function resolveNpm(deps = defaultDeps()) {
|
|
|
21321
22321
|
const name = npmBinaryName(deps.platform);
|
|
21322
22322
|
const onPath = npmFromPath(deps);
|
|
21323
22323
|
if (onPath)
|
|
21324
|
-
return { command:
|
|
22324
|
+
return { command: join11(onPath, name), binDir: onPath };
|
|
21325
22325
|
const adjacent = npmAdjacentToNode(deps);
|
|
21326
22326
|
if (adjacent)
|
|
21327
|
-
return { command:
|
|
22327
|
+
return { command: join11(adjacent, name), binDir: adjacent };
|
|
21328
22328
|
for (const dir of wellKnownNpmDirs(deps)) {
|
|
21329
|
-
const candidate =
|
|
22329
|
+
const candidate = join11(dir, name);
|
|
21330
22330
|
if (isFile(candidate))
|
|
21331
22331
|
return { command: candidate, binDir: dir };
|
|
21332
22332
|
}
|
|
21333
22333
|
return null;
|
|
21334
22334
|
}
|
|
22335
|
+
function npmCandidates(deps = defaultDeps()) {
|
|
22336
|
+
const name = npmBinaryName(deps.platform);
|
|
22337
|
+
const dirs = [
|
|
22338
|
+
...(deps.env.PATH ?? "").split(deps.platform === "win32" ? ";" : ":").filter(isAbsolute4),
|
|
22339
|
+
dirname5(deps.execPath),
|
|
22340
|
+
...wellKnownNpmDirs(deps)
|
|
22341
|
+
];
|
|
22342
|
+
return [...new Set(dirs)].flatMap((binDir) => {
|
|
22343
|
+
return [
|
|
22344
|
+
join11(binDir, name),
|
|
22345
|
+
join11(binDir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
22346
|
+
join11(binDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
22347
|
+
].filter(isFile).map((command) => ({ command, binDir }));
|
|
22348
|
+
});
|
|
22349
|
+
}
|
|
21335
22350
|
function quoteCmdArgument(value) {
|
|
21336
22351
|
if (/[\0\r\n"%]/.test(value)) {
|
|
21337
22352
|
throw new Error(`npm argument cannot be represented safely for cmd.exe: ${JSON.stringify(value)}`);
|
|
@@ -21339,6 +22354,8 @@ function quoteCmdArgument(value) {
|
|
|
21339
22354
|
return `"${value}"`;
|
|
21340
22355
|
}
|
|
21341
22356
|
function npmInvocation(resolved, npmArgs, platform = process.platform, env = process.env) {
|
|
22357
|
+
if (resolved.cliPath)
|
|
22358
|
+
return { command: resolved.command, args: [resolved.cliPath, ...npmArgs] };
|
|
21342
22359
|
if (platform !== "win32" || !/\.(?:cmd|bat)$/i.test(resolved.command)) {
|
|
21343
22360
|
return { command: resolved.command, args: [...npmArgs] };
|
|
21344
22361
|
}
|
|
@@ -21495,7 +22512,7 @@ function terminateNpmProcessTree(child, invocation, env = process.env, gracePeri
|
|
|
21495
22512
|
};
|
|
21496
22513
|
child.once("exit", onChildExit);
|
|
21497
22514
|
const systemRoot = env.SystemRoot ?? env.SYSTEMROOT;
|
|
21498
|
-
const taskkill = systemRoot ?
|
|
22515
|
+
const taskkill = systemRoot ? join11(systemRoot, "System32", "taskkill.exe") : "taskkill.exe";
|
|
21499
22516
|
try {
|
|
21500
22517
|
killer = spawn(taskkill, ["/pid", String(child.pid), "/t", "/f"], {
|
|
21501
22518
|
stdio: "ignore",
|
|
@@ -21591,10 +22608,11 @@ function probeNpmCutoffSupport(npm, clock) {
|
|
|
21591
22608
|
});
|
|
21592
22609
|
}
|
|
21593
22610
|
async function npmSupportsCutoff(npm, clock) {
|
|
21594
|
-
const
|
|
22611
|
+
const key = npm.cliPath ? `${npm.command}\x00${npm.cliPath}` : npm.command;
|
|
22612
|
+
const identity = await stat2(npm.cliPath ?? npm.command).then(({ size, mtimeMs }) => ({ size, mtimeMs })).catch(() => {
|
|
21595
22613
|
return;
|
|
21596
22614
|
});
|
|
21597
|
-
const existing = npmCutoffSupport.get(
|
|
22615
|
+
const existing = npmCutoffSupport.get(key);
|
|
21598
22616
|
const sameIdentity = existing && existing.identity?.size === identity?.size && existing.identity?.mtimeMs === identity?.mtimeMs;
|
|
21599
22617
|
if (existing && sameIdentity) {
|
|
21600
22618
|
if (!existing.settled || identity !== undefined && existing.settled.outcome !== "transient" || clock.now() < existing.settled.retryAt)
|
|
@@ -21605,7 +22623,7 @@ async function npmSupportsCutoff(npm, clock) {
|
|
|
21605
22623
|
return outcome;
|
|
21606
22624
|
});
|
|
21607
22625
|
const entry = { identity, pending };
|
|
21608
|
-
npmCutoffSupport.set(
|
|
22626
|
+
npmCutoffSupport.set(key, entry);
|
|
21609
22627
|
return pending;
|
|
21610
22628
|
}
|
|
21611
22629
|
function lspPublicationCutoff(graceDays, pinned, now = Date.now()) {
|
|
@@ -21623,6 +22641,79 @@ async function lspNpmInstallInvocation(npm, target, before, clock = SYSTEM_CLOCK
|
|
|
21623
22641
|
target
|
|
21624
22642
|
]);
|
|
21625
22643
|
}
|
|
22644
|
+
// ../aft-bridge/dist/lsp-npm-selection.js
|
|
22645
|
+
import { readFile, realpath, stat as stat3 } from "node:fs/promises";
|
|
22646
|
+
import { dirname as dirname6, join as join12, resolve as resolve8 } from "node:path";
|
|
22647
|
+
var LSP_NPM_REFUSAL = "AFT can't install language servers with the npm at <path> (<reason>). Put a real npm 7 or newer first on PATH; pinning a server version won't help.";
|
|
22648
|
+
function lspNpmRefusal(path, reason) {
|
|
22649
|
+
return LSP_NPM_REFUSAL.replace("<path>", path).replace("<reason>", reason);
|
|
22650
|
+
}
|
|
22651
|
+
async function genuineNpm(candidate) {
|
|
22652
|
+
let executable;
|
|
22653
|
+
try {
|
|
22654
|
+
executable = await realpath(candidate.command);
|
|
22655
|
+
} catch (error) {
|
|
22656
|
+
return { reason: `probe failed: ${String(error)}` };
|
|
22657
|
+
}
|
|
22658
|
+
const windowsShim = /\.(?:cmd|bat)$/i.test(executable);
|
|
22659
|
+
let current = dirname6(executable);
|
|
22660
|
+
if (windowsShim)
|
|
22661
|
+
current = join12(current, "node_modules", "npm");
|
|
22662
|
+
for (;; ) {
|
|
22663
|
+
try {
|
|
22664
|
+
const pkg = JSON.parse(await readFile(join12(current, "package.json"), "utf8"));
|
|
22665
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.npm;
|
|
22666
|
+
if (pkg.name === "npm" && typeof bin === "string") {
|
|
22667
|
+
const cli = await realpath(resolve8(current, bin));
|
|
22668
|
+
if (!windowsShim && cli !== executable)
|
|
22669
|
+
return { reason: "not npm: command is not npm's declared CLI" };
|
|
22670
|
+
const major = /^(\d+)\.\d+\.\d+$/.exec(pkg.version)?.[1];
|
|
22671
|
+
if (!major)
|
|
22672
|
+
return { reason: "probe failed: npm manifest has an unknown version" };
|
|
22673
|
+
if (Number(major) < 7)
|
|
22674
|
+
return { reason: `old npm: ${pkg.version} (requires npm 7 or newer)` };
|
|
22675
|
+
if (windowsShim || process.platform === "win32" && /\.[cm]?js$/i.test(cli)) {
|
|
22676
|
+
const node = join12(candidate.binDir ?? dirname6(executable), "node.exe");
|
|
22677
|
+
if (!(await stat3(node)).isFile())
|
|
22678
|
+
return { reason: "probe failed: adjacent Node is missing" };
|
|
22679
|
+
return { npm: { command: node, binDir: dirname6(node), cliPath: cli }, reason: "" };
|
|
22680
|
+
}
|
|
22681
|
+
return { npm: { command: cli, binDir: candidate.binDir }, reason: "" };
|
|
22682
|
+
}
|
|
22683
|
+
} catch {}
|
|
22684
|
+
const parent = dirname6(current);
|
|
22685
|
+
if (parent === current || windowsShim)
|
|
22686
|
+
break;
|
|
22687
|
+
current = parent;
|
|
22688
|
+
}
|
|
22689
|
+
return { reason: "not npm: no matching npm package manifest and CLI" };
|
|
22690
|
+
}
|
|
22691
|
+
async function selectLspNpm(preferred, candidates, signal) {
|
|
22692
|
+
const aborted = () => ({
|
|
22693
|
+
refusal: lspNpmRefusal(preferred.command, "probe failed: installation aborted")
|
|
22694
|
+
});
|
|
22695
|
+
if (signal?.aborted)
|
|
22696
|
+
return aborted();
|
|
22697
|
+
const first = await genuineNpm(preferred);
|
|
22698
|
+
if (signal?.aborted)
|
|
22699
|
+
return aborted();
|
|
22700
|
+
if (first.npm)
|
|
22701
|
+
return { npm: first.npm };
|
|
22702
|
+
const seen = new Set([preferred.command]);
|
|
22703
|
+
for (const candidate of candidates ?? npmCandidates()) {
|
|
22704
|
+
if (signal?.aborted)
|
|
22705
|
+
return aborted();
|
|
22706
|
+
if (seen.has(candidate.command))
|
|
22707
|
+
continue;
|
|
22708
|
+
seen.add(candidate.command);
|
|
22709
|
+
const result = await genuineNpm(candidate);
|
|
22710
|
+
if (signal?.aborted)
|
|
22711
|
+
return aborted();
|
|
22712
|
+
if (result.npm)
|
|
22713
|
+
return { npm: result.npm };
|
|
22714
|
+
}
|
|
22715
|
+
return { refusal: lspNpmRefusal(preferred.command, first.reason) };
|
|
22716
|
+
}
|
|
21626
22717
|
// ../aft-bridge/dist/message-detach.js
|
|
21627
22718
|
var BASH_WAIT_DETACH_MAGIC_KEYWORD = "&detach";
|
|
21628
22719
|
var STANDALONE_DETACH_KEYWORD_SOURCE = `(^|[^\\p{L}\\p{N}_])${BASH_WAIT_DETACH_MAGIC_KEYWORD}(?![\\p{L}\\p{N}_])`;
|
|
@@ -21672,14 +22763,14 @@ function shouldInterruptWaitsForMessage(detachOnUserMessage, operatorText) {
|
|
|
21672
22763
|
return detachOnUserMessage || containsStandaloneDetachKeyword(operatorText);
|
|
21673
22764
|
}
|
|
21674
22765
|
// ../aft-bridge/dist/migration.js
|
|
21675
|
-
import { closeSync as
|
|
22766
|
+
import { closeSync as closeSync8, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync8, renameSync as renameSync5, rmSync as rmSync3, statSync as statSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21676
22767
|
import { homedir as homedir12, tmpdir as tmpdir3 } from "node:os";
|
|
21677
|
-
import { basename as basename2, dirname as
|
|
22768
|
+
import { basename as basename2, dirname as dirname9, join as join15, resolve as resolve10 } from "node:path";
|
|
21678
22769
|
|
|
21679
22770
|
// ../aft-bridge/dist/paths.js
|
|
21680
|
-
import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync as
|
|
22771
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync as realpathSync5, renameSync as renameSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
21681
22772
|
import { homedir as homedir10 } from "node:os";
|
|
21682
|
-
import { dirname as
|
|
22773
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join13, resolve as resolve9 } from "node:path";
|
|
21683
22774
|
function homeDir3() {
|
|
21684
22775
|
if (process.platform === "win32")
|
|
21685
22776
|
return process.env.USERPROFILE || process.env.HOME || homedir10();
|
|
@@ -21687,14 +22778,14 @@ function homeDir3() {
|
|
|
21687
22778
|
}
|
|
21688
22779
|
function configHome() {
|
|
21689
22780
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
21690
|
-
if (xdg &&
|
|
22781
|
+
if (xdg && isAbsolute5(xdg))
|
|
21691
22782
|
return xdg;
|
|
21692
22783
|
return join13(homeDir3(), ".config");
|
|
21693
22784
|
}
|
|
21694
22785
|
function legacyOpenCodeConfigDir() {
|
|
21695
22786
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
21696
22787
|
if (envDir)
|
|
21697
|
-
return
|
|
22788
|
+
return resolve9(envDir);
|
|
21698
22789
|
return join13(configHome(), "opencode");
|
|
21699
22790
|
}
|
|
21700
22791
|
function legacyPiAgentDir() {
|
|
@@ -21742,9 +22833,9 @@ function resolveLegacyAftConfigSources(projectDirectory) {
|
|
|
21742
22833
|
];
|
|
21743
22834
|
const identity = (path) => {
|
|
21744
22835
|
try {
|
|
21745
|
-
return
|
|
22836
|
+
return realpathSync5(path);
|
|
21746
22837
|
} catch {
|
|
21747
|
-
return
|
|
22838
|
+
return resolve9(path);
|
|
21748
22839
|
}
|
|
21749
22840
|
};
|
|
21750
22841
|
const protectedPaths = new Set([
|
|
@@ -21768,7 +22859,7 @@ function repairRootScopedStorageFile(storageRoot, harness, fileName) {
|
|
|
21768
22859
|
if (existsSync7(harnessPath) || !existsSync7(rootPath))
|
|
21769
22860
|
return harnessPath;
|
|
21770
22861
|
try {
|
|
21771
|
-
openPrivateStorageDir(storageRoot,
|
|
22862
|
+
openPrivateStorageDir(storageRoot, dirname7(harnessPath));
|
|
21772
22863
|
renameSync4(rootPath, harnessPath);
|
|
21773
22864
|
} catch {}
|
|
21774
22865
|
return harnessPath;
|
|
@@ -21789,7 +22880,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
|
|
|
21789
22880
|
return false;
|
|
21790
22881
|
if (!lastVersion) {
|
|
21791
22882
|
try {
|
|
21792
|
-
openPrivateStorageDir(storageRoot,
|
|
22883
|
+
openPrivateStorageDir(storageRoot, dirname7(versionFile));
|
|
21793
22884
|
writeFileSync3(versionFile, currentVersion, { mode: PRIVATE_FILE_MODE });
|
|
21794
22885
|
} catch {}
|
|
21795
22886
|
return false;
|
|
@@ -21801,7 +22892,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
21801
22892
|
return;
|
|
21802
22893
|
const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
|
|
21803
22894
|
try {
|
|
21804
|
-
openPrivateStorageDir(storageRoot,
|
|
22895
|
+
openPrivateStorageDir(storageRoot, dirname7(versionFile));
|
|
21805
22896
|
writeFileSync3(versionFile, currentVersion, { mode: PRIVATE_FILE_MODE });
|
|
21806
22897
|
} catch {}
|
|
21807
22898
|
}
|
|
@@ -21850,10 +22941,10 @@ function decodeFileUrl(target) {
|
|
|
21850
22941
|
}
|
|
21851
22942
|
|
|
21852
22943
|
// ../aft-bridge/dist/resolver.js
|
|
21853
|
-
import { closeSync as
|
|
21854
|
-
import { createRequire } from "node:module";
|
|
22944
|
+
import { closeSync as closeSync7, existsSync as existsSync8, promises as fsPromises, openSync as openSync6, readFileSync as readFileSync7, readSync as readSync6 } from "node:fs";
|
|
22945
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
21855
22946
|
import { homedir as homedir11 } from "node:os";
|
|
21856
|
-
import { delimiter, dirname as
|
|
22947
|
+
import { delimiter, dirname as dirname8, isAbsolute as isAbsolute6, join as join14 } from "node:path";
|
|
21857
22948
|
var ensureBinaryForResolver = ensureBinary;
|
|
21858
22949
|
var npmPlatformPackageReader = null;
|
|
21859
22950
|
var cacheCopiesInFlight = new Map;
|
|
@@ -21927,7 +23018,7 @@ function pathCandidates(env, ext) {
|
|
|
21927
23018
|
const seen = new Set;
|
|
21928
23019
|
const candidates = [];
|
|
21929
23020
|
for (const dir of rawPath.split(delimiter)) {
|
|
21930
|
-
if (!dir || !
|
|
23021
|
+
if (!dir || !isAbsolute6(dir))
|
|
21931
23022
|
continue;
|
|
21932
23023
|
const candidate = join14(dir, `aft${ext}`);
|
|
21933
23024
|
if (seen.has(candidate))
|
|
@@ -21941,9 +23032,9 @@ function pathCandidates(env, ext) {
|
|
|
21941
23032
|
function isNativeExecutable(binaryPath) {
|
|
21942
23033
|
let fd = null;
|
|
21943
23034
|
try {
|
|
21944
|
-
fd =
|
|
23035
|
+
fd = openSync6(binaryPath, "r");
|
|
21945
23036
|
const buf = Buffer.alloc(4);
|
|
21946
|
-
const read =
|
|
23037
|
+
const read = readSync6(fd, buf, 0, 4, 0);
|
|
21947
23038
|
if (read < 2)
|
|
21948
23039
|
return false;
|
|
21949
23040
|
const b0 = buf[0];
|
|
@@ -21964,7 +23055,7 @@ function isNativeExecutable(binaryPath) {
|
|
|
21964
23055
|
} finally {
|
|
21965
23056
|
if (fd !== null) {
|
|
21966
23057
|
try {
|
|
21967
|
-
|
|
23058
|
+
closeSync7(fd);
|
|
21968
23059
|
} catch {}
|
|
21969
23060
|
}
|
|
21970
23061
|
}
|
|
@@ -21994,7 +23085,7 @@ function explicitBinaryOverride(env) {
|
|
|
21994
23085
|
}
|
|
21995
23086
|
function ownPackageVersion() {
|
|
21996
23087
|
try {
|
|
21997
|
-
const req =
|
|
23088
|
+
const req = createRequire2(import.meta.url);
|
|
21998
23089
|
return req("../package.json").version;
|
|
21999
23090
|
} catch {
|
|
22000
23091
|
return null;
|
|
@@ -22004,10 +23095,10 @@ function npmPlatformPackage(ext) {
|
|
|
22004
23095
|
if (npmPlatformPackageReader)
|
|
22005
23096
|
return npmPlatformPackageReader(ext);
|
|
22006
23097
|
try {
|
|
22007
|
-
const req =
|
|
23098
|
+
const req = createRequire2(import.meta.url);
|
|
22008
23099
|
const manifestPath = req.resolve(`@cortexkit/aft-${platformKey()}/package.json`);
|
|
22009
23100
|
const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
22010
|
-
const binaryPath = join14(
|
|
23101
|
+
const binaryPath = join14(dirname8(manifestPath), "bin", `aft${ext}`);
|
|
22011
23102
|
if (typeof manifest.version !== "string" || !existsSync8(binaryPath))
|
|
22012
23103
|
return null;
|
|
22013
23104
|
return { binaryPath, version: normalizeBareVersion(manifest.version) };
|
|
@@ -22314,19 +23405,19 @@ function acquireConfigMigrationLock(lockDir) {
|
|
|
22314
23405
|
}
|
|
22315
23406
|
}
|
|
22316
23407
|
function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
22317
|
-
mkdirSync5(
|
|
22318
|
-
const tmpPath = join15(
|
|
23408
|
+
mkdirSync5(dirname9(targetPath), { recursive: true });
|
|
23409
|
+
const tmpPath = join15(dirname9(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
22319
23410
|
let fd = null;
|
|
22320
23411
|
try {
|
|
22321
|
-
fd =
|
|
23412
|
+
fd = openSync7(tmpPath, "wx", 384);
|
|
22322
23413
|
writeFileSync4(fd, readFileSync8(sourcePath));
|
|
22323
|
-
|
|
23414
|
+
closeSync8(fd);
|
|
22324
23415
|
fd = null;
|
|
22325
23416
|
renameSync5(tmpPath, targetPath);
|
|
22326
23417
|
} catch (err) {
|
|
22327
23418
|
if (fd !== null) {
|
|
22328
23419
|
try {
|
|
22329
|
-
|
|
23420
|
+
closeSync8(fd);
|
|
22330
23421
|
} catch {}
|
|
22331
23422
|
}
|
|
22332
23423
|
try {
|
|
@@ -22336,19 +23427,19 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
|
22336
23427
|
}
|
|
22337
23428
|
}
|
|
22338
23429
|
function atomicWriteConfigFile(targetPath, content) {
|
|
22339
|
-
mkdirSync5(
|
|
22340
|
-
const tmpPath = join15(
|
|
23430
|
+
mkdirSync5(dirname9(targetPath), { recursive: true });
|
|
23431
|
+
const tmpPath = join15(dirname9(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
22341
23432
|
let fd = null;
|
|
22342
23433
|
try {
|
|
22343
|
-
fd =
|
|
23434
|
+
fd = openSync7(tmpPath, "wx", 384);
|
|
22344
23435
|
writeFileSync4(fd, content);
|
|
22345
|
-
|
|
23436
|
+
closeSync8(fd);
|
|
22346
23437
|
fd = null;
|
|
22347
23438
|
renameSync5(tmpPath, targetPath);
|
|
22348
23439
|
} catch (err) {
|
|
22349
23440
|
if (fd !== null) {
|
|
22350
23441
|
try {
|
|
22351
|
-
|
|
23442
|
+
closeSync8(fd);
|
|
22352
23443
|
} catch {}
|
|
22353
23444
|
}
|
|
22354
23445
|
try {
|
|
@@ -22456,13 +23547,13 @@ function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
|
|
|
22456
23547
|
}
|
|
22457
23548
|
function migrateAftConfigFile(opts) {
|
|
22458
23549
|
const warnings = [];
|
|
22459
|
-
const resolvedTarget =
|
|
22460
|
-
const existingSources = opts.legacySources.filter((source) => existsSync9(source.path) &&
|
|
23550
|
+
const resolvedTarget = resolve10(opts.targetPath);
|
|
23551
|
+
const existingSources = opts.legacySources.filter((source) => existsSync9(source.path) && resolve10(source.path) !== resolvedTarget);
|
|
22461
23552
|
const info = opts.logger?.info ?? opts.logger?.log;
|
|
22462
23553
|
if (existingSources.length === 0) {
|
|
22463
23554
|
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
22464
23555
|
}
|
|
22465
|
-
mkdirSync5(
|
|
23556
|
+
mkdirSync5(dirname9(opts.targetPath), { recursive: true });
|
|
22466
23557
|
const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
|
|
22467
23558
|
try {
|
|
22468
23559
|
const sources = existingSources.map((source) => ({
|
|
@@ -22521,11 +23612,11 @@ function spawnErrorLabel(error) {
|
|
|
22521
23612
|
function migrationLogPath(newRoot, harness, logger) {
|
|
22522
23613
|
const desired = join15(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
|
|
22523
23614
|
try {
|
|
22524
|
-
mkdirSync5(
|
|
23615
|
+
mkdirSync5(dirname9(desired), { recursive: true });
|
|
22525
23616
|
return desired;
|
|
22526
23617
|
} catch (err) {
|
|
22527
23618
|
const fallback = join15(tmpdir3(), `aft-migration-${harness}-${Date.now()}.jsonl`);
|
|
22528
|
-
logger?.warn?.(`Failed to create AFT migration log directory ${
|
|
23619
|
+
logger?.warn?.(`Failed to create AFT migration log directory ${dirname9(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
|
|
22529
23620
|
return fallback;
|
|
22530
23621
|
}
|
|
22531
23622
|
}
|
|
@@ -22584,13 +23675,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
22584
23675
|
// ../aft-bridge/dist/migration-notices.js
|
|
22585
23676
|
import { readFileSync as readFileSync9, renameSync as renameSync6, rmdirSync, statSync as statSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22586
23677
|
import { homedir as homedir13 } from "node:os";
|
|
22587
|
-
import { dirname as
|
|
23678
|
+
import { dirname as dirname10, join as join16, resolve as resolve11 } from "node:path";
|
|
22588
23679
|
function migrationNoticeStorePath(env = process.env) {
|
|
22589
23680
|
const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join16(homedir13(), ".local", "state");
|
|
22590
23681
|
return join16(stateHome, "cortexkit", "aft", "migration-notices.json");
|
|
22591
23682
|
}
|
|
22592
23683
|
function noticeKey(configPath, digest) {
|
|
22593
|
-
return `${
|
|
23684
|
+
return `${resolve11(configPath)}|${MIGRATION_POLICY_ID}|${digest}`;
|
|
22594
23685
|
}
|
|
22595
23686
|
function readRecords(storePath) {
|
|
22596
23687
|
try {
|
|
@@ -22603,7 +23694,7 @@ function readRecords(storePath) {
|
|
|
22603
23694
|
var LOCK_STALE_MS = 1e4;
|
|
22604
23695
|
function withStoreLock(storePath, action) {
|
|
22605
23696
|
const lockPath = `${storePath}.lock`;
|
|
22606
|
-
openPrivateStorageDir(
|
|
23697
|
+
openPrivateStorageDir(dirname10(storePath));
|
|
22607
23698
|
const deadline = Date.now() + 2000;
|
|
22608
23699
|
for (;; ) {
|
|
22609
23700
|
try {
|
|
@@ -22657,7 +23748,7 @@ function deliverMigrationNoticeOnce(options) {
|
|
|
22657
23748
|
}
|
|
22658
23749
|
}
|
|
22659
23750
|
// ../aft-bridge/dist/onnx-probe.js
|
|
22660
|
-
import { closeSync as
|
|
23751
|
+
import { closeSync as closeSync9, existsSync as existsSync10, openSync as openSync8, readSync as readSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
22661
23752
|
var MH_MAGIC = 4277009102;
|
|
22662
23753
|
var MH_MAGIC_64 = 4277009103;
|
|
22663
23754
|
var FAT_MAGIC = 3405691582;
|
|
@@ -22670,7 +23761,7 @@ var MAX_HEADER_BYTES = 4 * 1024 * 1024;
|
|
|
22670
23761
|
var UNCHECKED = { loadable: true, checked: false };
|
|
22671
23762
|
function readAt(fd, offset, length) {
|
|
22672
23763
|
const buffer = Buffer.alloc(length);
|
|
22673
|
-
const read =
|
|
23764
|
+
const read = readSync7(fd, buffer, 0, length, offset);
|
|
22674
23765
|
return buffer.subarray(0, read);
|
|
22675
23766
|
}
|
|
22676
23767
|
function cpuTypeForArch(arch) {
|
|
@@ -22744,7 +23835,7 @@ function isUncheckableDependency(path) {
|
|
|
22744
23835
|
function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
|
|
22745
23836
|
let fd;
|
|
22746
23837
|
try {
|
|
22747
|
-
fd =
|
|
23838
|
+
fd = openSync8(realpathSync6(libPath), "r");
|
|
22748
23839
|
} catch (err) {
|
|
22749
23840
|
return {
|
|
22750
23841
|
loadable: false,
|
|
@@ -22778,13 +23869,13 @@ function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
|
|
|
22778
23869
|
} catch {
|
|
22779
23870
|
return UNCHECKED;
|
|
22780
23871
|
} finally {
|
|
22781
|
-
|
|
23872
|
+
closeSync9(fd);
|
|
22782
23873
|
}
|
|
22783
23874
|
}
|
|
22784
23875
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
22785
23876
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
22786
|
-
import { chmodSync as chmodSync2, closeSync as
|
|
22787
|
-
import { basename as basename3, dirname as
|
|
23877
|
+
import { chmodSync as chmodSync2, closeSync as closeSync10, copyFileSync as copyFileSync4, createWriteStream as createWriteStream3, existsSync as existsSync11, lstatSync as lstatSync3, openSync as openSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, readlinkSync as readlinkSync2, realpathSync as realpathSync7, renameSync as renameSync7, rmSync as rmSync4, statSync as statSync10, symlinkSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23878
|
+
import { basename as basename3, dirname as dirname11, isAbsolute as isAbsolute7, join as join17, relative as relative4, resolve as resolve12, win32 as win324 } from "node:path";
|
|
22788
23879
|
import { Readable as Readable3 } from "node:stream";
|
|
22789
23880
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
22790
23881
|
var ORT_VERSION = "1.24.4";
|
|
@@ -23023,14 +24114,14 @@ function parseOnnxVersionFromDirectoryPath(value) {
|
|
|
23023
24114
|
return null;
|
|
23024
24115
|
}
|
|
23025
24116
|
function isPathInsideRoot(root, candidate) {
|
|
23026
|
-
const rel =
|
|
23027
|
-
return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !
|
|
24117
|
+
const rel = relative4(root, candidate);
|
|
24118
|
+
return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !isAbsolute7(rel) && !win324.isAbsolute(rel);
|
|
23028
24119
|
}
|
|
23029
24120
|
function detectOnnxVersion(libDir, libName) {
|
|
23030
24121
|
try {
|
|
23031
24122
|
const entries = readdirSync4(libDir);
|
|
23032
24123
|
try {
|
|
23033
|
-
const resolved = parseOnnxVersionFromPath(
|
|
24124
|
+
const resolved = parseOnnxVersionFromPath(realpathSync7(join17(libDir, libName)));
|
|
23034
24125
|
if (resolved && resolved !== INVALID_ORT_VERSION)
|
|
23035
24126
|
return resolved;
|
|
23036
24127
|
} catch {}
|
|
@@ -23047,7 +24138,7 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
23047
24138
|
const base = join17(libDir, libName);
|
|
23048
24139
|
if (existsSync11(base)) {
|
|
23049
24140
|
try {
|
|
23050
|
-
const real =
|
|
24141
|
+
const real = realpathSync7(base);
|
|
23051
24142
|
const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
|
|
23052
24143
|
if (version)
|
|
23053
24144
|
return version;
|
|
@@ -23084,18 +24175,18 @@ function pathEntriesForPlatform() {
|
|
|
23084
24175
|
return pathEnvValue().split(delimiter).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter((entry) => {
|
|
23085
24176
|
if (!entry || entry === "." || entry.includes("\x00"))
|
|
23086
24177
|
return false;
|
|
23087
|
-
return
|
|
24178
|
+
return isAbsolute7(entry) || win324.isAbsolute(entry);
|
|
23088
24179
|
});
|
|
23089
24180
|
}
|
|
23090
24181
|
function isWindowsSystem32Directory(dir) {
|
|
23091
24182
|
if (process.platform !== "win32")
|
|
23092
24183
|
return false;
|
|
23093
|
-
const normalizedDir =
|
|
24184
|
+
const normalizedDir = win324.resolve(dir).replace(/[\\/]+$/, "").toLowerCase();
|
|
23094
24185
|
const windowsRoots = [process.env.SystemRoot, process.env.windir, "C:\\Windows"];
|
|
23095
24186
|
return windowsRoots.some((root) => {
|
|
23096
24187
|
if (!root)
|
|
23097
24188
|
return false;
|
|
23098
|
-
return normalizedDir ===
|
|
24189
|
+
return normalizedDir === win324.resolve(root, "System32").replace(/[\\/]+$/, "").toLowerCase();
|
|
23099
24190
|
});
|
|
23100
24191
|
}
|
|
23101
24192
|
function directoryContainsLibrary(dir, libName) {
|
|
@@ -23155,7 +24246,7 @@ function findSystemOnnxRuntime(libName, searchPathsOverride) {
|
|
|
23155
24246
|
const normalizeCase = process.platform === "win32" || process.platform === "darwin";
|
|
23156
24247
|
const seen = new Set;
|
|
23157
24248
|
const uniquePaths = searchPaths.filter((p) => {
|
|
23158
|
-
let key =
|
|
24249
|
+
let key = resolve12(p).replace(/[/\\]+$/, "");
|
|
23159
24250
|
if (normalizeCase)
|
|
23160
24251
|
key = key.toLowerCase();
|
|
23161
24252
|
if (seen.has(key))
|
|
@@ -23212,7 +24303,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
23212
24303
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
23213
24304
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
23214
24305
|
}
|
|
23215
|
-
privateMkdirSync(
|
|
24306
|
+
privateMkdirSync(dirname11(destPath));
|
|
23216
24307
|
let bytesWritten = 0;
|
|
23217
24308
|
const guard = new TransformStream({
|
|
23218
24309
|
transform(chunk, transformController) {
|
|
@@ -23239,7 +24330,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
23239
24330
|
}
|
|
23240
24331
|
}
|
|
23241
24332
|
function validateExtractedTree(stagingRoot) {
|
|
23242
|
-
const realRoot =
|
|
24333
|
+
const realRoot = realpathSync7(stagingRoot);
|
|
23243
24334
|
let totalBytes = 0;
|
|
23244
24335
|
const walk = (dir) => {
|
|
23245
24336
|
const entries = readdirSync4(dir);
|
|
@@ -23248,15 +24339,15 @@ function validateExtractedTree(stagingRoot) {
|
|
|
23248
24339
|
const lst = lstatSync3(fullPath);
|
|
23249
24340
|
if (lst.isSymbolicLink()) {
|
|
23250
24341
|
const linkTarget = readlinkSync2(fullPath);
|
|
23251
|
-
const resolvedTarget =
|
|
23252
|
-
const rel =
|
|
23253
|
-
if (relativePathEscapesRoot(rel) ||
|
|
24342
|
+
const resolvedTarget = resolve12(dirname11(fullPath), linkTarget);
|
|
24343
|
+
const rel = relative4(realRoot, resolvedTarget);
|
|
24344
|
+
if (relativePathEscapesRoot(rel) || win324.isAbsolute(rel)) {
|
|
23254
24345
|
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
23255
24346
|
}
|
|
23256
24347
|
continue;
|
|
23257
24348
|
}
|
|
23258
|
-
const rel =
|
|
23259
|
-
if (relativePathEscapesRoot(rel) ||
|
|
24349
|
+
const rel = relative4(realRoot, fullPath);
|
|
24350
|
+
if (relativePathEscapesRoot(rel) || win324.isAbsolute(rel)) {
|
|
23260
24351
|
throw new Error(`extracted entry ${fullPath} escapes staging root`);
|
|
23261
24352
|
}
|
|
23262
24353
|
if (lst.isDirectory()) {
|
|
@@ -23388,14 +24479,14 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
23388
24479
|
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
23389
24480
|
}
|
|
23390
24481
|
}
|
|
23391
|
-
const targetRoot =
|
|
24482
|
+
const targetRoot = realpathSync7(targetDir);
|
|
23392
24483
|
for (const link of symlinks) {
|
|
23393
24484
|
const dst = join17(targetDir, link.name);
|
|
23394
24485
|
try {
|
|
23395
24486
|
unlinkSync5(dst);
|
|
23396
24487
|
} catch {}
|
|
23397
24488
|
const dstForContainment = join17(targetRoot, link.name);
|
|
23398
|
-
const resolvedTarget =
|
|
24489
|
+
const resolvedTarget = resolve12(dirname11(dstForContainment), link.target);
|
|
23399
24490
|
if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
|
|
23400
24491
|
const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
|
|
23401
24492
|
if (requiredLibs.has(link.name)) {
|
|
@@ -23473,13 +24564,13 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
23473
24564
|
function acquireLock(lockPath) {
|
|
23474
24565
|
const tryClaim = () => {
|
|
23475
24566
|
try {
|
|
23476
|
-
const fd =
|
|
24567
|
+
const fd = openSync9(lockPath, "wx", PRIVATE_FILE_MODE);
|
|
23477
24568
|
try {
|
|
23478
24569
|
writeFileSync6(fd, `${process.pid}
|
|
23479
24570
|
${new Date().toISOString()}
|
|
23480
24571
|
`);
|
|
23481
24572
|
} finally {
|
|
23482
|
-
|
|
24573
|
+
closeSync10(fd);
|
|
23483
24574
|
}
|
|
23484
24575
|
return true;
|
|
23485
24576
|
} catch (err) {
|
|
@@ -25308,7 +26399,7 @@ function isBgCompletion(value) {
|
|
|
25308
26399
|
function formatCompletion(completion) {
|
|
25309
26400
|
const status = formatStatus(completion);
|
|
25310
26401
|
const duration = formatDuration(completion);
|
|
25311
|
-
const header = `- task ${completion.task_id} (${status}${duration ? `, ${duration}` : ""})`;
|
|
26402
|
+
const header = formatUnknownPtyExit(completion.task_id, completion) ?? `- task ${completion.task_id} (${status}${duration ? `, ${duration}` : ""})`;
|
|
25312
26403
|
const previewBlock = formatOutputPreview(completion);
|
|
25313
26404
|
const descendantWarning = completion.live_descendants_summary ? ` ${completion.live_descendants_summary}` : "";
|
|
25314
26405
|
const captureWarning = completion.output_incomplete || completion.status === "failed" && completion.status_reason ? ` [${completion.status_reason || "PTY output may be incomplete"}]` : "";
|
|
@@ -25375,9 +26466,9 @@ import {
|
|
|
25375
26466
|
// package.json
|
|
25376
26467
|
var package_default = {
|
|
25377
26468
|
name: "@cortexkit/aft-pi",
|
|
25378
|
-
version: "0.
|
|
26469
|
+
version: "0.65.0",
|
|
25379
26470
|
type: "module",
|
|
25380
|
-
description: "Pi coding agent extension for Agent
|
|
26471
|
+
description: "Pi coding agent extension for Agent Foundational Toolkit (AFT) — tree-sitter and LSP-powered code analysis",
|
|
25381
26472
|
main: "dist/index.js",
|
|
25382
26473
|
types: "dist/index.d.ts",
|
|
25383
26474
|
license: "MIT",
|
|
@@ -25399,18 +26490,18 @@ var package_default = {
|
|
|
25399
26490
|
"test:e2e": "bun test src/__tests__/e2e/"
|
|
25400
26491
|
},
|
|
25401
26492
|
dependencies: {
|
|
25402
|
-
"@cortexkit/aft-bridge": "0.
|
|
26493
|
+
"@cortexkit/aft-bridge": "0.65.0",
|
|
25403
26494
|
"comment-json": "^5.0.0",
|
|
25404
26495
|
diff: "^8.0.4",
|
|
25405
26496
|
zod: "^4.4.3"
|
|
25406
26497
|
},
|
|
25407
26498
|
optionalDependencies: {
|
|
25408
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
25409
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
25410
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
25411
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
25412
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
25413
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
26499
|
+
"@cortexkit/aft-darwin-arm64": "0.65.0",
|
|
26500
|
+
"@cortexkit/aft-darwin-x64": "0.65.0",
|
|
26501
|
+
"@cortexkit/aft-linux-arm64": "0.65.0",
|
|
26502
|
+
"@cortexkit/aft-linux-x64": "0.65.0",
|
|
26503
|
+
"@cortexkit/aft-win32-arm64": "0.65.0",
|
|
26504
|
+
"@cortexkit/aft-win32-x64": "0.65.0"
|
|
25414
26505
|
},
|
|
25415
26506
|
devDependencies: {
|
|
25416
26507
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -25490,22 +26581,15 @@ function readStatusBar(value) {
|
|
|
25490
26581
|
const disabled = Array.isArray(bar.disabled_categories) ? bar.disabled_categories.filter((key) => typeof key === "string") : [];
|
|
25491
26582
|
const errors = readOptionalNumber(bar.errors);
|
|
25492
26583
|
const warnings = readOptionalNumber(bar.warnings);
|
|
25493
|
-
const deadCode = readOptionalNumber(bar.dead_code);
|
|
25494
|
-
const unusedExports = readOptionalNumber(bar.unused_exports);
|
|
25495
|
-
const duplicates = readOptionalNumber(bar.duplicates);
|
|
25496
26584
|
const todos = readOptionalNumber(bar.todos);
|
|
25497
|
-
if (errors === null && warnings === null &&
|
|
26585
|
+
if (errors === null && warnings === null && todos === null && disabled.length === 0) {
|
|
25498
26586
|
return;
|
|
25499
26587
|
}
|
|
25500
26588
|
return {
|
|
25501
26589
|
...disabled.length > 0 ? { disabled_categories: disabled } : {},
|
|
25502
26590
|
...errors !== null ? { errors } : {},
|
|
25503
26591
|
...warnings !== null ? { warnings } : {},
|
|
25504
|
-
...
|
|
25505
|
-
...unusedExports !== null ? { unused_exports: unusedExports } : {},
|
|
25506
|
-
...duplicates !== null ? { duplicates } : {},
|
|
25507
|
-
...todos !== null ? { todos } : {},
|
|
25508
|
-
...bar.tier2_stale === true ? { tier2_stale: true } : {}
|
|
26592
|
+
...todos !== null ? { todos } : {}
|
|
25509
26593
|
};
|
|
25510
26594
|
}
|
|
25511
26595
|
function formatFlag(enabled) {
|
|
@@ -25620,11 +26704,11 @@ function coerceAftStatus(response) {
|
|
|
25620
26704
|
warm_entries: readNumber(symbolCache.warm_entries)
|
|
25621
26705
|
},
|
|
25622
26706
|
storage_dir: readNullableString(response.storage_dir),
|
|
25623
|
-
checkpoints_total:
|
|
26707
|
+
checkpoints_total: readOptionalNumber(response.checkpoints_total),
|
|
25624
26708
|
session: {
|
|
25625
26709
|
id: readString(session.id, "__default__"),
|
|
25626
26710
|
tracked_files: readNumber(session.tracked_files),
|
|
25627
|
-
checkpoints:
|
|
26711
|
+
checkpoints: readOptionalNumber(session.checkpoints)
|
|
25628
26712
|
},
|
|
25629
26713
|
compression: readCompression(response.compression),
|
|
25630
26714
|
status_bar: readStatusBar(response.status_bar ?? response.status_bar_values)
|
|
@@ -25661,13 +26745,10 @@ function formatStatusDialogMessage(status) {
|
|
|
25661
26745
|
const rows = [
|
|
25662
26746
|
["errors", sb.errors],
|
|
25663
26747
|
["warnings", sb.warnings],
|
|
25664
|
-
["dead code", sb.dead_code],
|
|
25665
|
-
["unused exports", sb.unused_exports],
|
|
25666
|
-
["duplicates", sb.duplicates],
|
|
25667
26748
|
["todos", sb.todos]
|
|
25668
26749
|
].filter((row) => typeof row[1] === "number");
|
|
25669
26750
|
if (rows.length > 0) {
|
|
25670
|
-
lines.push("",
|
|
26751
|
+
lines.push("", "Code Health");
|
|
25671
26752
|
for (const [label, value] of rows)
|
|
25672
26753
|
lines.push(`- ${label}: ${formatCount(value)}`);
|
|
25673
26754
|
}
|
|
@@ -25713,7 +26794,7 @@ import { Type } from "typebox";
|
|
|
25713
26794
|
// src/config.ts
|
|
25714
26795
|
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
|
|
25715
26796
|
import { homedir as homedir14 } from "node:os";
|
|
25716
|
-
import { isAbsolute as
|
|
26797
|
+
import { isAbsolute as isAbsolute8 } from "node:path";
|
|
25717
26798
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
25718
26799
|
|
|
25719
26800
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
@@ -30505,9 +31586,8 @@ function clampBashWatchSyncMaxMs(value) {
|
|
|
30505
31586
|
}
|
|
30506
31587
|
function resolveBashConfig(config) {
|
|
30507
31588
|
const top = config.bash;
|
|
30508
|
-
const
|
|
30509
|
-
const
|
|
30510
|
-
const reminderInterval = (typeof top === "object" && top !== null ? top.long_running_reminder_interval_ms : undefined) ?? legacy?.long_running_reminder_interval_ms;
|
|
31589
|
+
const reminderEnabled = typeof top === "object" && top !== null ? top.long_running_reminder_enabled : undefined;
|
|
31590
|
+
const reminderInterval = typeof top === "object" && top !== null ? top.long_running_reminder_interval_ms : undefined;
|
|
30511
31591
|
const topDetachOnUserMessage = typeof top === "object" && top !== null ? top.detach_on_user_message ?? true : true;
|
|
30512
31592
|
const rawForegroundWait = typeof top === "object" && top !== null ? top.foreground_wait_window_ms : undefined;
|
|
30513
31593
|
const foregroundWaitWindowMs = Math.max(FOREGROUND_WAIT_WINDOW_MIN_MS, rawForegroundWait ?? FOREGROUND_WAIT_WINDOW_DEFAULT_MS);
|
|
@@ -30551,13 +31631,6 @@ function resolveBashConfig(config) {
|
|
|
30551
31631
|
detach_on_user_message: topDetachOnUserMessage
|
|
30552
31632
|
};
|
|
30553
31633
|
}
|
|
30554
|
-
const hasLegacyFeatureFlag = legacy && (legacy.rewrite !== undefined || legacy.compress !== undefined || legacy.background !== undefined);
|
|
30555
|
-
if (hasLegacyFeatureFlag) {
|
|
30556
|
-
const rewrite = legacy.rewrite === true;
|
|
30557
|
-
const compress = legacy.compress === true;
|
|
30558
|
-
const background = legacy.background === true;
|
|
30559
|
-
return { ...base, enabled: true, rewrite, compress, background };
|
|
30560
|
-
}
|
|
30561
31634
|
return { ...base, enabled: true, rewrite: true, compress: true, background: true };
|
|
30562
31635
|
}
|
|
30563
31636
|
var FormatterEnum = _enum([
|
|
@@ -30587,9 +31660,9 @@ var ConfigureWarningsDeliveryEnum = _enum(["toast", "log", "chat"]);
|
|
|
30587
31660
|
var IndexKindSchema = _enum(["search", "semantic", "callgraph"]);
|
|
30588
31661
|
function isSupportedAbsoluteIndexPath(path) {
|
|
30589
31662
|
if (path === "~" || path.startsWith("~/") || path.startsWith("~\\")) {
|
|
30590
|
-
return
|
|
31663
|
+
return isAbsolute8(homedir14());
|
|
30591
31664
|
}
|
|
30592
|
-
return
|
|
31665
|
+
return isAbsolute8(path);
|
|
30593
31666
|
}
|
|
30594
31667
|
var IndexRootSchema = object({
|
|
30595
31668
|
path: string2().refine(isSupportedAbsoluteIndexPath, {
|
|
@@ -30646,6 +31719,7 @@ var LspServerEntrySchema = object({
|
|
|
30646
31719
|
initialization_options: unknown().optional()
|
|
30647
31720
|
});
|
|
30648
31721
|
var LspConfigSchema = object({
|
|
31722
|
+
max_servers: number2().int().nonnegative().optional(),
|
|
30649
31723
|
idle_minutes: union([
|
|
30650
31724
|
number2().int().transform((value) => Math.min(1440, Math.max(5, value))),
|
|
30651
31725
|
literal("never")
|
|
@@ -30659,13 +31733,6 @@ var LspConfigSchema = object({
|
|
|
30659
31733
|
versions: record(string2().trim().min(1), string2().trim().min(1)).optional()
|
|
30660
31734
|
});
|
|
30661
31735
|
var ExperimentalConfigSchema = object({
|
|
30662
|
-
bash: object({
|
|
30663
|
-
rewrite: boolean2().optional(),
|
|
30664
|
-
compress: boolean2().optional(),
|
|
30665
|
-
background: boolean2().optional(),
|
|
30666
|
-
long_running_reminder_enabled: boolean2().optional(),
|
|
30667
|
-
long_running_reminder_interval_ms: number2().int().positive().optional()
|
|
30668
|
-
}).optional(),
|
|
30669
31736
|
lsp_ty: boolean2().optional()
|
|
30670
31737
|
});
|
|
30671
31738
|
var BashEnvSchema = record(string2(), string2()).superRefine((env, context) => {
|
|
@@ -30736,7 +31803,7 @@ var SubcConfigSchema = object({
|
|
|
30736
31803
|
connection_file: string2().optional()
|
|
30737
31804
|
});
|
|
30738
31805
|
var GhShimConfigSchema = object({
|
|
30739
|
-
binary_path: string2().trim().refine(
|
|
31806
|
+
binary_path: string2().trim().refine(isAbsolute8, "gh_shim.binary_path must be absolute").optional()
|
|
30740
31807
|
});
|
|
30741
31808
|
var RemoteExecConfigSchema = object({
|
|
30742
31809
|
enabled: boolean2().optional(),
|
|
@@ -30763,7 +31830,6 @@ var PiConfigSchema = object({
|
|
|
30763
31830
|
var InspectConfigSchema = object({
|
|
30764
31831
|
enabled: boolean2().optional(),
|
|
30765
31832
|
diagnostics_timeout_ms: number2().int().positive().optional().transform((value) => value === undefined ? undefined : clampInspectDiagnosticsTimeoutMs(value)),
|
|
30766
|
-
tier2_idle_minutes: number2().min(0).optional(),
|
|
30767
31833
|
tier2_pass_timeout_ms: number2().int().positive().optional(),
|
|
30768
31834
|
categories: object({
|
|
30769
31835
|
diagnostics: boolean2().optional(),
|
|
@@ -30781,14 +31847,12 @@ var InspectConfigSchema = object({
|
|
|
30781
31847
|
function clampIdleRootTtlMinutes(value) {
|
|
30782
31848
|
return Math.min(30, Math.max(5, value));
|
|
30783
31849
|
}
|
|
31850
|
+
function clampIdleCheckoutHours(value) {
|
|
31851
|
+
return Math.min(IDLE_CHECKOUT_HOURS_MAX, Math.max(IDLE_CHECKOUT_HOURS_MIN, value));
|
|
31852
|
+
}
|
|
30784
31853
|
var IdleConfigSchema = object({
|
|
30785
|
-
root_ttl_minutes: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleRootTtlMinutes(value))
|
|
30786
|
-
|
|
30787
|
-
var ViewsConfigSchema = object({
|
|
30788
|
-
enabled: boolean2().optional()
|
|
30789
|
-
});
|
|
30790
|
-
var WorktreeConfigSchema = object({
|
|
30791
|
-
ram_overlay: boolean2().optional()
|
|
31854
|
+
root_ttl_minutes: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleRootTtlMinutes(value)),
|
|
31855
|
+
checkout_hours: number2().int().optional().transform((value) => value === undefined ? undefined : clampIdleCheckoutHours(value))
|
|
30792
31856
|
});
|
|
30793
31857
|
var BackupConfigSchema = object({
|
|
30794
31858
|
enabled: boolean2().optional(),
|
|
@@ -30810,12 +31874,10 @@ var AftConfigFieldsSchema = object({
|
|
|
30810
31874
|
indexes: IndexesConfigSchema.optional(),
|
|
30811
31875
|
index: IndexConfigSchema.optional(),
|
|
30812
31876
|
storage: object({ cache_dir: string2().nullable().optional() }).optional(),
|
|
30813
|
-
views: ViewsConfigSchema.optional(),
|
|
30814
31877
|
callgraph_chunk_size: number2().optional(),
|
|
30815
31878
|
inspect: InspectConfigSchema.optional(),
|
|
30816
31879
|
idle: IdleConfigSchema.optional(),
|
|
30817
31880
|
backup: BackupConfigSchema.optional(),
|
|
30818
|
-
worktree: WorktreeConfigSchema.optional(),
|
|
30819
31881
|
sandbox: SandboxConfigSchema.optional(),
|
|
30820
31882
|
bash: BashConfigSchema.optional(),
|
|
30821
31883
|
experimental: ExperimentalConfigSchema.optional(),
|
|
@@ -30863,6 +31925,11 @@ function applyActiveHarnessOverride(config, trusted) {
|
|
|
30863
31925
|
delete merged.indexes;
|
|
30864
31926
|
else
|
|
30865
31927
|
merged.indexes = indexes;
|
|
31928
|
+
const idle = mergeIdleConfig(base.idle, override.idle);
|
|
31929
|
+
if (idle === undefined)
|
|
31930
|
+
delete merged.idle;
|
|
31931
|
+
else
|
|
31932
|
+
merged.idle = idle;
|
|
30866
31933
|
merged.sandbox = mergeSandboxConfig(base.sandbox, override.sandbox, true);
|
|
30867
31934
|
if (!trusted && override.sandbox?.write_allow !== undefined) {
|
|
30868
31935
|
merged.sandbox = { ...merged.sandbox, write_allow: override.sandbox.write_allow };
|
|
@@ -30965,7 +32032,10 @@ function loadConfigFromPath(configPath, tier) {
|
|
|
30965
32032
|
recordConfigParseFailure(configPath, errorMsg);
|
|
30966
32033
|
throw new ConfigRejectedError(["invalid_config_document"], configPath);
|
|
30967
32034
|
}
|
|
30968
|
-
const removed = removedConfigKeyErrors(cleanConfig);
|
|
32035
|
+
const removed = removedConfigKeyErrors(cleanConfig, undefined, false);
|
|
32036
|
+
if (tier === "user") {
|
|
32037
|
+
removed.push(...removedConfigKeyErrors(cleanConfig).filter((error) => [...REMOVED_DATA_LAYER_PATHS, ...DATA_LAYER_RETIRED_PATHS].some((path) => error.endsWith(`${path}; run npx @cortexkit/aft doctor --fix`))));
|
|
32038
|
+
}
|
|
30969
32039
|
if (removed.length) {
|
|
30970
32040
|
throw new ConfigRejectedError(removed);
|
|
30971
32041
|
}
|
|
@@ -30988,8 +32058,10 @@ function loadConfigFromPath(configPath, tier) {
|
|
|
30988
32058
|
warn2(text);
|
|
30989
32059
|
}
|
|
30990
32060
|
}
|
|
30991
|
-
if (
|
|
30992
|
-
|
|
32061
|
+
if (tier === "user") {
|
|
32062
|
+
if (translation.legacyInput) {
|
|
32063
|
+
log2(`Config ${configPath} uses retired keys (${translation.retiredKeys.join(", ")}); applied their current equivalents in memory`);
|
|
32064
|
+
}
|
|
30993
32065
|
} else if (translation.legacyInput) {
|
|
30994
32066
|
configLoadNotices.push({
|
|
30995
32067
|
configPath,
|
|
@@ -31075,6 +32147,9 @@ function mergeSemanticConfig(base, override) {
|
|
|
31075
32147
|
}
|
|
31076
32148
|
function mergeLspConfig(base, override) {
|
|
31077
32149
|
const projectSafe = {};
|
|
32150
|
+
if (override?.max_servers !== undefined && override.max_servers <= (base?.max_servers ?? 8)) {
|
|
32151
|
+
projectSafe.max_servers = override.max_servers;
|
|
32152
|
+
}
|
|
31078
32153
|
if (override?.python !== undefined)
|
|
31079
32154
|
projectSafe.python = override.python;
|
|
31080
32155
|
if (override?.diagnostics_on_edit !== undefined) {
|
|
@@ -31095,14 +32170,12 @@ function mergeLspConfig(base, override) {
|
|
|
31095
32170
|
return;
|
|
31096
32171
|
return Object.fromEntries(Object.entries(lsp).filter(([, v]) => v !== undefined));
|
|
31097
32172
|
}
|
|
31098
|
-
function
|
|
31099
|
-
|
|
31100
|
-
|
|
31101
|
-
|
|
31102
|
-
|
|
31103
|
-
|
|
31104
|
-
return overrideWorktree;
|
|
31105
|
-
return { ...baseWorktree, ...overrideWorktree };
|
|
32173
|
+
function mergeIdleConfig(baseIdle, overrideIdle) {
|
|
32174
|
+
const merged = {
|
|
32175
|
+
root_ttl_minutes: overrideIdle?.root_ttl_minutes ?? baseIdle?.root_ttl_minutes,
|
|
32176
|
+
checkout_hours: overrideIdle?.checkout_hours ?? baseIdle?.checkout_hours
|
|
32177
|
+
};
|
|
32178
|
+
return merged.root_ttl_minutes === undefined && merged.checkout_hours === undefined ? undefined : merged;
|
|
31106
32179
|
}
|
|
31107
32180
|
function mergeInspectConfig(baseInspect, overrideInspect) {
|
|
31108
32181
|
const diagnosticsTimeoutConfigured = baseInspect?.diagnostics_timeout_ms !== undefined || overrideInspect?.diagnostics_timeout_ms !== undefined;
|
|
@@ -31182,19 +32255,10 @@ function mergeBashConfig(baseBash, overrideBash, mode = "tier") {
|
|
|
31182
32255
|
return merged;
|
|
31183
32256
|
}
|
|
31184
32257
|
function mergeExperimentalConfig(base, override) {
|
|
31185
|
-
const bash = {
|
|
31186
|
-
...base?.bash,
|
|
31187
|
-
...override?.bash
|
|
31188
|
-
};
|
|
31189
32258
|
const experimental = {
|
|
31190
32259
|
...base,
|
|
31191
32260
|
...override
|
|
31192
32261
|
};
|
|
31193
|
-
if (Object.values(bash).some((value) => value !== undefined)) {
|
|
31194
|
-
experimental.bash = bash;
|
|
31195
|
-
} else {
|
|
31196
|
-
delete experimental.bash;
|
|
31197
|
-
}
|
|
31198
32262
|
if (Object.values(experimental).every((value) => value === undefined))
|
|
31199
32263
|
return;
|
|
31200
32264
|
return Object.fromEntries(Object.entries(experimental).filter(([, value]) => value !== undefined));
|
|
@@ -31221,11 +32285,9 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
31221
32285
|
"revision_reads",
|
|
31222
32286
|
"validate_on_edit",
|
|
31223
32287
|
"configure_warnings_delivery",
|
|
31224
|
-
"views",
|
|
31225
32288
|
"callgraph_chunk_size",
|
|
31226
32289
|
"inspect",
|
|
31227
32290
|
"idle",
|
|
31228
|
-
"worktree",
|
|
31229
32291
|
"git",
|
|
31230
32292
|
"pi",
|
|
31231
32293
|
"experimental",
|
|
@@ -31340,7 +32402,7 @@ function mergeConfigs(base, override) {
|
|
|
31340
32402
|
bash.project_env = projectEnv;
|
|
31341
32403
|
}
|
|
31342
32404
|
const inspect = mergeInspectConfig(base.inspect, override.inspect);
|
|
31343
|
-
const
|
|
32405
|
+
const idle = mergeIdleConfig(base.idle, override.idle);
|
|
31344
32406
|
const sandbox = mergeSandboxConfig(base.sandbox, override.sandbox);
|
|
31345
32407
|
const remoteExec = mergeRemoteExecConfig(base.remote_exec, override.remote_exec);
|
|
31346
32408
|
const backup = mergeProjectBackupConfig(base.backup, override.backup);
|
|
@@ -31350,7 +32412,7 @@ function mergeConfigs(base, override) {
|
|
|
31350
32412
|
delete safeOverride.indexes;
|
|
31351
32413
|
delete safeOverride.bash;
|
|
31352
32414
|
delete safeOverride.inspect;
|
|
31353
|
-
delete safeOverride.
|
|
32415
|
+
delete safeOverride.idle;
|
|
31354
32416
|
delete safeOverride.pi;
|
|
31355
32417
|
return {
|
|
31356
32418
|
...base,
|
|
@@ -31360,7 +32422,7 @@ function mergeConfigs(base, override) {
|
|
|
31360
32422
|
...lsp ? { lsp } : {},
|
|
31361
32423
|
...bash !== undefined ? { bash } : {},
|
|
31362
32424
|
...inspect !== undefined ? { inspect } : {},
|
|
31363
|
-
...
|
|
32425
|
+
...idle !== undefined ? { idle } : {},
|
|
31364
32426
|
...sandbox !== undefined ? { sandbox } : {},
|
|
31365
32427
|
...remoteExec !== undefined ? { remote_exec: remoteExec } : {},
|
|
31366
32428
|
...backup !== undefined ? { backup } : {},
|
|
@@ -31680,9 +32742,9 @@ function detectPiHarness(api) {
|
|
|
31680
32742
|
}
|
|
31681
32743
|
|
|
31682
32744
|
// src/omp-approval.ts
|
|
31683
|
-
import { realpathSync as
|
|
32745
|
+
import { realpathSync as realpathSync8 } from "node:fs";
|
|
31684
32746
|
import { homedir as homedir15 } from "node:os";
|
|
31685
|
-
import { basename as basename4, dirname as
|
|
32747
|
+
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute9, join as join18, relative as relative5, resolve as resolve13, sep as sep2 } from "node:path";
|
|
31686
32748
|
var OMP_TOOL_TIERS = {
|
|
31687
32749
|
read: "read",
|
|
31688
32750
|
grep: "read",
|
|
@@ -31727,7 +32789,7 @@ function expandPath(value) {
|
|
|
31727
32789
|
if (decoded === "~")
|
|
31728
32790
|
return homedir15();
|
|
31729
32791
|
if (decoded.startsWith("~/") || decoded.startsWith(`~${sep2}`))
|
|
31730
|
-
return
|
|
32792
|
+
return resolve13(homedir15(), decoded.slice(2));
|
|
31731
32793
|
return decoded;
|
|
31732
32794
|
}
|
|
31733
32795
|
function realLocation(path) {
|
|
@@ -31735,12 +32797,12 @@ function realLocation(path) {
|
|
|
31735
32797
|
let current = path;
|
|
31736
32798
|
for (;; ) {
|
|
31737
32799
|
try {
|
|
31738
|
-
return missing.length > 0 ? join18(
|
|
32800
|
+
return missing.length > 0 ? join18(realpathSync8(current), ...missing) : realpathSync8(current);
|
|
31739
32801
|
} catch (error) {
|
|
31740
32802
|
if (error.code !== "ENOENT")
|
|
31741
32803
|
throw error;
|
|
31742
32804
|
}
|
|
31743
|
-
const parent =
|
|
32805
|
+
const parent = dirname12(current);
|
|
31744
32806
|
if (parent === current)
|
|
31745
32807
|
return path;
|
|
31746
32808
|
missing.unshift(basename4(current));
|
|
@@ -31755,11 +32817,11 @@ function namesOutsidePath(root, value) {
|
|
|
31755
32817
|
const expanded = expandPath(value);
|
|
31756
32818
|
if (expanded.startsWith("file:"))
|
|
31757
32819
|
return false;
|
|
31758
|
-
const absolute =
|
|
31759
|
-
if (relativePathEscapesRoot(
|
|
32820
|
+
const absolute = isAbsolute9(expanded) ? expanded : resolve13(root, expanded);
|
|
32821
|
+
if (relativePathEscapesRoot(relative5(root, absolute)))
|
|
31760
32822
|
return true;
|
|
31761
32823
|
try {
|
|
31762
|
-
return relativePathEscapesRoot(
|
|
32824
|
+
return relativePathEscapesRoot(relative5(realLocation(root), realLocation(absolute)));
|
|
31763
32825
|
} catch {
|
|
31764
32826
|
return true;
|
|
31765
32827
|
}
|
|
@@ -31791,11 +32853,10 @@ function unquote(value) {
|
|
|
31791
32853
|
const first = value[0];
|
|
31792
32854
|
return value.length >= 2 && (first === '"' || first === "'") && value.endsWith(first) ? value.slice(1, -1) : value;
|
|
31793
32855
|
}
|
|
31794
|
-
function
|
|
32856
|
+
function hashlinePatchSections(patch) {
|
|
31795
32857
|
if (patch.trim().length === 0)
|
|
31796
32858
|
return;
|
|
31797
|
-
const
|
|
31798
|
-
let sections = 0;
|
|
32859
|
+
const sections = [];
|
|
31799
32860
|
const rows = patch.split(`
|
|
31800
32861
|
`);
|
|
31801
32862
|
for (let index = 0;index < rows.length; index++) {
|
|
@@ -31811,28 +32872,31 @@ function hashlinePatchPaths(patch) {
|
|
|
31811
32872
|
const hash = inner.indexOf("#");
|
|
31812
32873
|
if (hash <= 0)
|
|
31813
32874
|
return;
|
|
31814
|
-
|
|
31815
|
-
sections++;
|
|
32875
|
+
sections.push({ path: inner.slice(0, hash), moves: [] });
|
|
31816
32876
|
continue;
|
|
31817
32877
|
}
|
|
31818
32878
|
const directive = /^(PUT|CUT|REM|MV)(?:\s|$)/.exec(line);
|
|
31819
32879
|
if (directive) {
|
|
31820
|
-
|
|
32880
|
+
const current = sections[sections.length - 1];
|
|
32881
|
+
if (current === undefined)
|
|
31821
32882
|
return;
|
|
31822
32883
|
if (directive[1] === "MV") {
|
|
31823
32884
|
const destination = line.slice(2).trim();
|
|
31824
32885
|
if (destination.length === 0 || /\s/.test(destination))
|
|
31825
32886
|
return;
|
|
31826
|
-
|
|
32887
|
+
current.moves.push(unquote(destination));
|
|
31827
32888
|
}
|
|
31828
32889
|
continue;
|
|
31829
32890
|
}
|
|
31830
|
-
if (trimmed.length === 0 && sections === 0)
|
|
32891
|
+
if (trimmed.length === 0 && sections.length === 0)
|
|
31831
32892
|
continue;
|
|
31832
|
-
if (sections === 0 || !line.startsWith("+"))
|
|
32893
|
+
if (sections.length === 0 || !line.startsWith("+"))
|
|
31833
32894
|
return;
|
|
31834
32895
|
}
|
|
31835
|
-
return sections > 0 ?
|
|
32896
|
+
return sections.length > 0 ? sections : undefined;
|
|
32897
|
+
}
|
|
32898
|
+
function hashlinePatchPaths(patch) {
|
|
32899
|
+
return hashlinePatchSections(patch)?.flatMap((section) => [section.path, ...section.moves]);
|
|
31836
32900
|
}
|
|
31837
32901
|
var APPLY_PATCH_TARGETS = [
|
|
31838
32902
|
"*** Add File:",
|
|
@@ -32328,8 +33392,8 @@ function renderInner(s, error, theme, innerWidth) {
|
|
|
32328
33392
|
left2.push(kv("symbol cache", `${formatCountShort(s.symbol_cache.local_entries)} local · ${formatCountShort(s.symbol_cache.warm_entries)} warm`, theme));
|
|
32329
33393
|
right2.push(theme.fg("muted", "Current session"));
|
|
32330
33394
|
right2.push(kv("tracked files", String(s.session.tracked_files), theme));
|
|
32331
|
-
right2.push(kv("checkpoints",
|
|
32332
|
-
right2.push(kv("all-session",
|
|
33395
|
+
right2.push(kv("checkpoints", s.session.checkpoints?.toString() ?? "—", theme));
|
|
33396
|
+
right2.push(kv("all-session", s.checkpoints_total?.toString() ?? "—", theme));
|
|
32333
33397
|
for (const line of renderColumns(left2, right2, colWidth))
|
|
32334
33398
|
lines.push(line);
|
|
32335
33399
|
lines.push("");
|
|
@@ -32497,9 +33561,9 @@ import { join as join21 } from "node:path";
|
|
|
32497
33561
|
// src/lsp-cache.ts
|
|
32498
33562
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
32499
33563
|
import {
|
|
32500
|
-
closeSync as
|
|
33564
|
+
closeSync as closeSync11,
|
|
32501
33565
|
mkdirSync as mkdirSync6,
|
|
32502
|
-
openSync as
|
|
33566
|
+
openSync as openSync10,
|
|
32503
33567
|
readFileSync as readFileSync12,
|
|
32504
33568
|
renameSync as renameSync9,
|
|
32505
33569
|
rmSync as rmSync6,
|
|
@@ -32600,14 +33664,14 @@ function acquireInstallLock(lockKey) {
|
|
|
32600
33664
|
const token = randomUUID3();
|
|
32601
33665
|
const tryClaim = () => {
|
|
32602
33666
|
try {
|
|
32603
|
-
const fd =
|
|
33667
|
+
const fd = openSync10(lock, "wx");
|
|
32604
33668
|
try {
|
|
32605
33669
|
writeFileSync8(fd, `${process.pid}
|
|
32606
33670
|
${new Date().toISOString()}
|
|
32607
33671
|
${token}
|
|
32608
33672
|
`);
|
|
32609
33673
|
} finally {
|
|
32610
|
-
|
|
33674
|
+
closeSync11(fd);
|
|
32611
33675
|
}
|
|
32612
33676
|
let retained = false;
|
|
32613
33677
|
let released = false;
|
|
@@ -32770,11 +33834,11 @@ function claimLspAutoInstallPass() {
|
|
|
32770
33834
|
};
|
|
32771
33835
|
const tryClaim = () => {
|
|
32772
33836
|
try {
|
|
32773
|
-
const fd =
|
|
33837
|
+
const fd = openSync10(lock, "wx");
|
|
32774
33838
|
try {
|
|
32775
33839
|
writeFileSync8(fd, JSON.stringify(owner));
|
|
32776
33840
|
} finally {
|
|
32777
|
-
|
|
33841
|
+
closeSync11(fd);
|
|
32778
33842
|
}
|
|
32779
33843
|
return true;
|
|
32780
33844
|
} catch (err) {
|
|
@@ -33178,18 +34242,26 @@ async function runInstall(spec, version, cwd, installLock, before, signal, deadl
|
|
|
33178
34242
|
warn2(`[lsp] install ${target} aborted before spawn`);
|
|
33179
34243
|
return false;
|
|
33180
34244
|
}
|
|
33181
|
-
const
|
|
33182
|
-
if (!
|
|
34245
|
+
const preferredNpm = resolveNpm();
|
|
34246
|
+
if (!preferredNpm) {
|
|
33183
34247
|
warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
|
|
33184
34248
|
return false;
|
|
33185
34249
|
}
|
|
34250
|
+
const selected = await selectLspNpm(preferredNpm, undefined, signal);
|
|
34251
|
+
if (signal?.aborted)
|
|
34252
|
+
return false;
|
|
34253
|
+
if (!selected.npm) {
|
|
34254
|
+
warn2(`[lsp] ${selected.refusal}`);
|
|
34255
|
+
return false;
|
|
34256
|
+
}
|
|
34257
|
+
const npm = selected.npm;
|
|
33186
34258
|
const invocation = await lspNpmInstallInvocation(npm, target, before);
|
|
33187
34259
|
if (signal?.aborted) {
|
|
33188
34260
|
warn2(`[lsp] install ${target} aborted during npm capability probe`);
|
|
33189
34261
|
return false;
|
|
33190
34262
|
}
|
|
33191
34263
|
if (!invocation) {
|
|
33192
|
-
warn2(`[lsp] npm cannot
|
|
34264
|
+
warn2(`[lsp] ${lspNpmRefusal(npm.command, "probe failed: cannot verify publication-cutoff support")}`);
|
|
33193
34265
|
return false;
|
|
33194
34266
|
}
|
|
33195
34267
|
ensureInstallAnchor(cwd);
|
|
@@ -33271,6 +34343,9 @@ async function runInstall(spec, version, cwd, installLock, before, signal, deadl
|
|
|
33271
34343
|
log2(`[lsp] installed ${target}`);
|
|
33272
34344
|
finish(true);
|
|
33273
34345
|
} else {
|
|
34346
|
+
const rejectedOption = /--(?:no-save|before|ignore-scripts)\b/.exec(stderrBuf)?.[0];
|
|
34347
|
+
if (rejectedOption)
|
|
34348
|
+
warn2(`[lsp] ${lspNpmRefusal(npm.command, `rejected option: ${rejectedOption}`)}`);
|
|
33274
34349
|
error2(`[lsp] install ${target} exited with code ${code}; last stderr:
|
|
33275
34350
|
${stderrBuf.trim()}`);
|
|
33276
34351
|
finish(false);
|
|
@@ -34113,9 +35188,9 @@ import { StringEnum } from "@earendil-works/pi-ai";
|
|
|
34113
35188
|
import { Type as Type4 } from "typebox";
|
|
34114
35189
|
|
|
34115
35190
|
// src/tools/hoisted.ts
|
|
34116
|
-
import { stat as
|
|
35191
|
+
import { stat as stat4 } from "node:fs/promises";
|
|
34117
35192
|
import { homedir as homedir16 } from "node:os";
|
|
34118
|
-
import { isAbsolute as
|
|
35193
|
+
import { isAbsolute as isAbsolute10, relative as relative6, resolve as resolve14, sep as sep3 } from "node:path";
|
|
34119
35194
|
import {
|
|
34120
35195
|
renderDiff as renderDiff2
|
|
34121
35196
|
} from "@earendil-works/pi-coding-agent";
|
|
@@ -34293,7 +35368,7 @@ function modelSupportsImages(extCtx) {
|
|
|
34293
35368
|
}
|
|
34294
35369
|
var NON_VISION_IMAGE_NOTE = "[Current model does not support images. The image will be omitted from this request.]";
|
|
34295
35370
|
function containsPath(parent, child) {
|
|
34296
|
-
const rel =
|
|
35371
|
+
const rel = relative6(parent, child);
|
|
34297
35372
|
return !relativePathEscapesRoot(rel);
|
|
34298
35373
|
}
|
|
34299
35374
|
function normalizePathInput(path) {
|
|
@@ -34303,17 +35378,17 @@ function normalizePathInput(path) {
|
|
|
34303
35378
|
if (decoded === "~")
|
|
34304
35379
|
return homedir16();
|
|
34305
35380
|
if (decoded.startsWith(`~${sep3}`) || decoded.startsWith("~/")) {
|
|
34306
|
-
return
|
|
35381
|
+
return resolve14(homedir16(), decoded.slice(2));
|
|
34307
35382
|
}
|
|
34308
35383
|
return decoded;
|
|
34309
35384
|
}
|
|
34310
35385
|
function absoluteSearchPath(cwd, target) {
|
|
34311
35386
|
const expanded = normalizePathInput(target);
|
|
34312
|
-
return
|
|
35387
|
+
return isAbsolute10(expanded) ? expanded : resolve14(cwd, expanded);
|
|
34313
35388
|
}
|
|
34314
35389
|
async function searchPathExists(cwd, target) {
|
|
34315
35390
|
try {
|
|
34316
|
-
await
|
|
35391
|
+
await stat4(absoluteSearchPath(cwd, target));
|
|
34317
35392
|
return true;
|
|
34318
35393
|
} catch {
|
|
34319
35394
|
return false;
|
|
@@ -34365,7 +35440,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
|
34365
35440
|
if (!target)
|
|
34366
35441
|
return;
|
|
34367
35442
|
const expanded = normalizePathInput(target);
|
|
34368
|
-
const absoluteTarget =
|
|
35443
|
+
const absoluteTarget = isAbsolute10(expanded) ? expanded : resolve14(extCtx.cwd, expanded);
|
|
34369
35444
|
if (containsPath(extCtx.cwd, absoluteTarget))
|
|
34370
35445
|
return;
|
|
34371
35446
|
if (options.restrictToProjectRoot === false)
|
|
@@ -34374,7 +35449,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
|
34374
35449
|
}
|
|
34375
35450
|
async function assertExternalReadPermission(ctx, extCtx, target, restrictToProjectRoot = ctx.config.restrict_to_project_root ?? false) {
|
|
34376
35451
|
const expanded = normalizePathInput(target);
|
|
34377
|
-
const absoluteTarget =
|
|
35452
|
+
const absoluteTarget = isAbsolute10(expanded) ? expanded : resolve14(extCtx.cwd, expanded);
|
|
34378
35453
|
if (!restrictToProjectRoot || containsPath(extCtx.cwd, absoluteTarget))
|
|
34379
35454
|
return;
|
|
34380
35455
|
try {
|
|
@@ -34707,7 +35782,13 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
34707
35782
|
const response = await callToolCallForSession(bridge, "edit", rawArgs, sessionId, extCtx);
|
|
34708
35783
|
if (response.success === false)
|
|
34709
35784
|
throw toolErrorFromResponse("edit", response);
|
|
34710
|
-
return
|
|
35785
|
+
return buildHashlineMutationResult(response);
|
|
35786
|
+
},
|
|
35787
|
+
renderCall(args, theme, context) {
|
|
35788
|
+
return renderHashlineEditCall(args, theme, context);
|
|
35789
|
+
},
|
|
35790
|
+
renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
|
|
35791
|
+
return renderMutationResult(result, theme, context, options);
|
|
34711
35792
|
}
|
|
34712
35793
|
});
|
|
34713
35794
|
}
|
|
@@ -34883,6 +35964,71 @@ ${formatDiagnosticsText(diagnostics)}`;
|
|
|
34883
35964
|
}
|
|
34884
35965
|
};
|
|
34885
35966
|
}
|
|
35967
|
+
function buildHashlineMutationResult(response) {
|
|
35968
|
+
const result = buildMutationResult(response);
|
|
35969
|
+
const rows = asRecords2(asRecord3(response.metadata)?.files);
|
|
35970
|
+
const files = rows.map(hashlineFileDetails);
|
|
35971
|
+
if (files.length === 0)
|
|
35972
|
+
return result;
|
|
35973
|
+
const changed = files.filter((file) => file.diff !== undefined);
|
|
35974
|
+
const diff = changed.length === 0 ? undefined : files.length === 1 ? files[0]?.diff : files.map((file) => file.diff === undefined ? mutationFileHeader(file) : `${mutationFileHeader(file)}
|
|
35975
|
+
${file.diff}`).join(`
|
|
35976
|
+
`);
|
|
35977
|
+
const firstChangedLine = rows.map((row) => firstChangedLineOfPatch(asString2(row.patch) ?? "")).find((line) => line !== undefined);
|
|
35978
|
+
return {
|
|
35979
|
+
...result,
|
|
35980
|
+
details: {
|
|
35981
|
+
...result.details,
|
|
35982
|
+
diff,
|
|
35983
|
+
firstChangedLine,
|
|
35984
|
+
additions: files.reduce((sum, file) => sum + file.additions, 0),
|
|
35985
|
+
deletions: files.reduce((sum, file) => sum + file.deletions, 0),
|
|
35986
|
+
files
|
|
35987
|
+
}
|
|
35988
|
+
};
|
|
35989
|
+
}
|
|
35990
|
+
function hashlineFileDetails(row) {
|
|
35991
|
+
const patch = asString2(row.patch) ?? "";
|
|
35992
|
+
const diff = formatUnifiedDiffForPi(patch);
|
|
35993
|
+
return {
|
|
35994
|
+
path: asString2(row.relativePath) ?? "",
|
|
35995
|
+
sourcePath: asString2(row.sourcePath) ?? asString2(row.movePath),
|
|
35996
|
+
diff: diff.length > 0 ? diff : undefined,
|
|
35997
|
+
additions: asNumber2(row.additions) ?? 0,
|
|
35998
|
+
deletions: asNumber2(row.deletions) ?? 0
|
|
35999
|
+
};
|
|
36000
|
+
}
|
|
36001
|
+
function firstChangedLineOfPatch(patch) {
|
|
36002
|
+
let newLine;
|
|
36003
|
+
for (const line of patch.split(`
|
|
36004
|
+
`)) {
|
|
36005
|
+
const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
|
|
36006
|
+
if (hunk) {
|
|
36007
|
+
newLine = Number(hunk[1]);
|
|
36008
|
+
continue;
|
|
36009
|
+
}
|
|
36010
|
+
if (newLine === undefined)
|
|
36011
|
+
continue;
|
|
36012
|
+
if (line.startsWith("+") || line.startsWith("-"))
|
|
36013
|
+
return Math.max(1, newLine);
|
|
36014
|
+
if (line.startsWith(" "))
|
|
36015
|
+
newLine++;
|
|
36016
|
+
}
|
|
36017
|
+
return;
|
|
36018
|
+
}
|
|
36019
|
+
function mutationFileName(file) {
|
|
36020
|
+
return file.sourcePath ? `${shortenHomePath(file.sourcePath)} → ${shortenHomePath(file.path)}` : shortenHomePath(file.path);
|
|
36021
|
+
}
|
|
36022
|
+
function mutationFileHeader(file) {
|
|
36023
|
+
return `${mutationFileName(file)} (+${file.additions}/-${file.deletions})`;
|
|
36024
|
+
}
|
|
36025
|
+
function mutationFilesLabel(files) {
|
|
36026
|
+
const first = files?.[0];
|
|
36027
|
+
if (!first || !files)
|
|
36028
|
+
return;
|
|
36029
|
+
const name = mutationFileName(first);
|
|
36030
|
+
return files.length > 1 ? `${name} and ${files.length - 1} more` : name;
|
|
36031
|
+
}
|
|
34886
36032
|
function formatGlobSkipReasonsNote(reasons) {
|
|
34887
36033
|
if (!Array.isArray(reasons))
|
|
34888
36034
|
return;
|
|
@@ -34934,6 +36080,29 @@ function renderMutationCall(toolName, filePath, theme, context) {
|
|
|
34934
36080
|
text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
|
|
34935
36081
|
return text;
|
|
34936
36082
|
}
|
|
36083
|
+
function renderHashlineEditCall(args, theme, context) {
|
|
36084
|
+
const text = reuseText2(context.lastComponent);
|
|
36085
|
+
const target = hashlinePatchTarget(asString2(asRecord3(args)?.patch));
|
|
36086
|
+
const targetDisplay = target ? `${theme.fg("accent", target.name)}${target.more > 0 ? theme.fg("muted", ` and ${target.more} more`) : ""}` : theme.fg("toolOutput", "...");
|
|
36087
|
+
text.setText(`${theme.fg("toolTitle", theme.bold("edit"))} ${targetDisplay}`);
|
|
36088
|
+
return text;
|
|
36089
|
+
}
|
|
36090
|
+
function hashlinePatchTarget(patch) {
|
|
36091
|
+
if (!patch)
|
|
36092
|
+
return;
|
|
36093
|
+
const sections = hashlinePatchSections(patch) ?? hashlinePatchSections(patch.slice(0, patch.lastIndexOf(`
|
|
36094
|
+
`) + 1));
|
|
36095
|
+
const first = sections?.[0];
|
|
36096
|
+
if (!sections || !first)
|
|
36097
|
+
return;
|
|
36098
|
+
const destination = first.moves[first.moves.length - 1];
|
|
36099
|
+
const name = destination ? `${shortenHomePath(first.path)} → ${shortenHomePath(destination)}` : shortenHomePath(first.path);
|
|
36100
|
+
const others = new Set(sections.flatMap((section) => [section.path, ...section.moves]));
|
|
36101
|
+
others.delete(first.path);
|
|
36102
|
+
if (destination)
|
|
36103
|
+
others.delete(destination);
|
|
36104
|
+
return { name, more: others.size };
|
|
36105
|
+
}
|
|
34937
36106
|
function renderMutationResult(result, theme, context, options = { expanded: true }) {
|
|
34938
36107
|
if (context.isError) {
|
|
34939
36108
|
const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
|
|
@@ -34969,15 +36138,21 @@ ${summary}${suffix}`);
|
|
|
34969
36138
|
const container = reuseContainer2(context.lastComponent);
|
|
34970
36139
|
container.clear();
|
|
34971
36140
|
container.addChild(new Spacer2(1));
|
|
34972
|
-
|
|
34973
|
-
|
|
34974
|
-
|
|
34975
|
-
|
|
34976
|
-
|
|
36141
|
+
const files = details?.files ?? [];
|
|
36142
|
+
if (files.length > 1) {
|
|
36143
|
+
for (const file of files) {
|
|
36144
|
+
const counts = theme.fg("muted", ` (+${file.additions}/-${file.deletions})`);
|
|
36145
|
+
container.addChild(new Text3(`${theme.fg("accent", mutationFileName(file))}${counts}`, 1, 0));
|
|
36146
|
+
if (file.diff !== undefined) {
|
|
36147
|
+
container.addChild(new Text3(renderTerminalDiff(file.diff), 1, 0));
|
|
36148
|
+
}
|
|
36149
|
+
}
|
|
36150
|
+
} else {
|
|
36151
|
+
container.addChild(new Text3(renderTerminalDiff(diff), 1, 0));
|
|
34977
36152
|
}
|
|
34978
|
-
container.addChild(new Text3(normalizeTerminalText(renderedDiff), 1, 0));
|
|
34979
36153
|
const rawPath = context.args && typeof context.args === "object" && typeof context.args.path === "string" ? context.args.path : undefined;
|
|
34980
|
-
const
|
|
36154
|
+
const summaryLabel = rawPath ? shortenHomePath(rawPath) : mutationFilesLabel(details?.files);
|
|
36155
|
+
const summaryPath = summaryLabel ? ` ${summaryLabel}` : "";
|
|
34981
36156
|
const summary = `edited${summaryPath} (+${details?.additions ?? 0}/-${details?.deletions ?? 0})`;
|
|
34982
36157
|
return collapsibleResult({
|
|
34983
36158
|
summary,
|
|
@@ -34986,11 +36161,20 @@ ${summary}${suffix}`);
|
|
|
34986
36161
|
context
|
|
34987
36162
|
});
|
|
34988
36163
|
}
|
|
36164
|
+
function renderTerminalDiff(diff) {
|
|
36165
|
+
let renderedDiff;
|
|
36166
|
+
try {
|
|
36167
|
+
renderedDiff = renderDiff2(diff);
|
|
36168
|
+
} catch {
|
|
36169
|
+
renderedDiff = diff;
|
|
36170
|
+
}
|
|
36171
|
+
return normalizeTerminalText(renderedDiff);
|
|
36172
|
+
}
|
|
34989
36173
|
async function resolvePathArg(cwd, path) {
|
|
34990
36174
|
const expanded = normalizePathInput(path);
|
|
34991
36175
|
const abs = absoluteSearchPath(cwd, path);
|
|
34992
36176
|
try {
|
|
34993
|
-
await
|
|
36177
|
+
await stat4(abs);
|
|
34994
36178
|
return abs;
|
|
34995
36179
|
} catch {
|
|
34996
36180
|
return expanded;
|
|
@@ -35565,7 +36749,7 @@ function getBashSpawnHook(pi) {
|
|
|
35565
36749
|
function backgroundWaitSentence(c) {
|
|
35566
36750
|
if (c.watch) {
|
|
35567
36751
|
const noPolling = c.status ? ", and never loop `bash_status` to wait" : "";
|
|
35568
|
-
return `then \`bash_watch\` handles only a short remaining wait (in a main session a watch defaults to 30s, max bash.watch_sync_max_ms, 120s by default; in a delegated session a watch without a timeout waits up to the worker wait limit, bash.worker_wait_max_ms, 30 minutes by default, then reports it is still running; watch again to keep waiting); for anything longer end the turn and let the completion reminder wake you, or use bash({wait:true}) when the result is needed before anything else — never background a command and immediately \`bash_watch\` it (that wastes a turn for what foreground returns in one)${noPolling}.`;
|
|
36752
|
+
return `then \`bash_watch\` handles only a short remaining wait (in a main session a watch defaults to 30s, max bash.watch_sync_max_ms, 120s by default; in a delegated session a watch without a timeout waits up to the worker wait limit, bash.worker_wait_max_ms, 30 minutes by default, then reports it is still running; watch again to keep waiting); for anything longer, first do the work that doesn't need this result, end the turn only when everything left needs it, and let the completion reminder wake you, or use bash({wait:true}) when the result is needed before anything else — never background a command and immediately \`bash_watch\` it (that wastes a turn for what foreground returns in one)${noPolling}.`;
|
|
35569
36753
|
}
|
|
35570
36754
|
return c.status ? "the task keeps running after the call returns, a completion reminder arrives when it exits, and `bash_status` reports its state and output. Use bash({wait:true}) instead when the result is needed before anything else." : "the task keeps running after the call returns and a completion reminder arrives when it exits. Use bash({wait:true}) instead when the result is needed before anything else.";
|
|
35571
36755
|
}
|
|
@@ -35602,7 +36786,7 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false, registeredName =
|
|
|
35602
36786
|
"PTY mode"
|
|
35603
36787
|
].join(", ") : bashCfg.compress ? "workdir and compressed output" : "workdir";
|
|
35604
36788
|
const detachSentence = bashCfg.detach_on_user_message ? "Any new message detaches this wait. Set `bash.detach_on_user_message: false` to keep it blocking; even then, a message containing the literal `&detach` forces detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`." : "Because `bash.detach_on_user_message` is false, a new message leaves this wait blocking; include the literal `&detach` anywhere to force detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`.";
|
|
35605
|
-
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (it blocks up to \`bash.worker_wait_max_ms\`, 30 minutes by default, then reports the command is still running; watch again to keep waiting); ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other
|
|
36789
|
+
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (it blocks up to \`bash.worker_wait_max_ms\`, 30 minutes by default, then reports the command is still running; watch again to keep waiting); ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other work to do while it runs, and any work that doesn't need this command's result counts; ${backgroundWaitSentence(companions)} Do not change or delete files a background command uses until it finishes. A \`nohup … &\` launch still holds the call if the child keeps stdout/stderr; redirect both or use background:true. \`pty: true\` runs interactive programs (REPLs, TUIs), implies background${ptyDriveClause(companions)}.` : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
|
|
35606
36790
|
const remoteRuns = () => !isPowerShell && remoteRunsOffered(ctx.config);
|
|
35607
36791
|
pi.registerTool({
|
|
35608
36792
|
name: registeredName,
|
|
@@ -35671,6 +36855,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
35671
36855
|
}
|
|
35672
36856
|
}
|
|
35673
36857
|
const bridgeCommand = spawnContext.command;
|
|
36858
|
+
const workdir = spawnContext.cwd ?? params.workdir;
|
|
35674
36859
|
let streamed = "";
|
|
35675
36860
|
let usedHostFallback = false;
|
|
35676
36861
|
let response;
|
|
@@ -35678,7 +36863,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
35678
36863
|
response = await withOutsidePermissionOperation(() => callBashWithPermissionLoop(bridge, {
|
|
35679
36864
|
command: bridgeCommand,
|
|
35680
36865
|
timeout,
|
|
35681
|
-
workdir
|
|
36866
|
+
workdir,
|
|
35682
36867
|
env: spawnContext.env,
|
|
35683
36868
|
description: params.description,
|
|
35684
36869
|
background: effectiveBackground,
|
|
@@ -35770,7 +36955,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
35770
36955
|
task_id: taskId
|
|
35771
36956
|
};
|
|
35772
36957
|
const output = response.output ?? "";
|
|
35773
|
-
return bashResult(usedHostFallback || isPowerShell ? output : withBashHints(output, bridgeCommand, aftSearchRegistered, extCtx.cwd), details);
|
|
36958
|
+
return bashResult(usedHostFallback || isPowerShell ? output : withBashHints(output, bridgeCommand, aftSearchRegistered, extCtx.cwd, workdir), details);
|
|
35774
36959
|
},
|
|
35775
36960
|
renderCall(args, theme, context) {
|
|
35776
36961
|
return renderBashCall(asString2(args?.command), asString2(args?.description), theme, context);
|
|
@@ -35798,8 +36983,8 @@ function registerBashCompanionTools(pi, ctx, enabled = { bashStatus: true, bashW
|
|
|
35798
36983
|
pi.registerTool(createBashKillTool(ctx));
|
|
35799
36984
|
}
|
|
35800
36985
|
}
|
|
35801
|
-
function withBashHints(output, command, aftSearchRegistered, projectRoot) {
|
|
35802
|
-
return maybeAppendGrepSearchHint(maybeAppendConflictsHint(output), command, aftSearchRegistered, projectRoot);
|
|
36986
|
+
function withBashHints(output, command, aftSearchRegistered, projectRoot, workdir) {
|
|
36987
|
+
return maybeAppendGrepSearchHint(maybeAppendConflictsHint(output), command, aftSearchRegistered, projectRoot, undefined, workdir);
|
|
35803
36988
|
}
|
|
35804
36989
|
function createBashStatusTool(ctx) {
|
|
35805
36990
|
return {
|
|
@@ -35833,7 +37018,7 @@ function createBashWatchTool(ctx) {
|
|
|
35833
37018
|
return {
|
|
35834
37019
|
name: "bash_watch",
|
|
35835
37020
|
label: "bash_watch",
|
|
35836
|
-
description: `Watch a background bash task. ${WATCH_SYNC_DEFAULTS_DESCRIPTION}. In a main session sync waits are for a short remaining wait on a task; for anything longer end the turn
|
|
37021
|
+
description: `Watch a background bash task. ${WATCH_SYNC_DEFAULTS_DESCRIPTION}. In a main session sync waits are for a short remaining wait on a task; for anything longer, first do the work that doesn't need the result, end the turn only when everything left needs it, and let the completion reminder wake you, or use \`bash({wait:true})\` when the result is needed before anything else. The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn.${bashCompanionRegistered(ctx.config, "bash_status") ? " Never loop bash_status to wait." : ""}`,
|
|
35837
37022
|
promptSnippet: "Wait for or watch a background bash task",
|
|
35838
37023
|
parameters: BashWatchParams,
|
|
35839
37024
|
async execute(_toolCallId, params, signal, _onUpdate, extCtx) {
|
|
@@ -36300,7 +37485,7 @@ function formatWaitSummary(taskId, waited, details, watchRole) {
|
|
|
36300
37485
|
async function formatBashStatus(extCtx, taskId, details, requestedOutputMode, watchRole) {
|
|
36301
37486
|
const exit = typeof details.exit_code === "number" ? ` (exit ${details.exit_code})` : "";
|
|
36302
37487
|
const dur = typeof details.duration_ms === "number" ? ` ${Math.round(details.duration_ms / 1000)}s` : "";
|
|
36303
|
-
let text = `Task ${taskId}: ${details.status}${exit}${dur}`;
|
|
37488
|
+
let text = formatUnknownPtyExit(taskId, details) ?? `Task ${taskId}: ${details.status}${exit}${dur}`;
|
|
36304
37489
|
if (details.live_descendants_summary) {
|
|
36305
37490
|
text += ` · ${details.live_descendants_summary}`;
|
|
36306
37491
|
}
|
|
@@ -36606,7 +37791,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
36606
37791
|
recursive: coerceBoolean(params.recursive)
|
|
36607
37792
|
}, extCtx);
|
|
36608
37793
|
if (response.success === false) {
|
|
36609
|
-
throw
|
|
37794
|
+
throw toolErrorFromResponse("delete", response);
|
|
36610
37795
|
}
|
|
36611
37796
|
const deletedEntries = response.deleted ?? [];
|
|
36612
37797
|
const skipped = response.skipped_files ?? [];
|
|
@@ -37244,7 +38429,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
37244
38429
|
const navigateTool = withPathAliasPreparation({
|
|
37245
38430
|
name: "aft_callgraph",
|
|
37246
38431
|
label: "callgraph",
|
|
37247
|
-
description: `Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. ${zoomEnabled ? "Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships." : "Use aft_callgraph call_tree (depth 1) for one-level forward calls-out; use it for reverse callers or multi-level traces."} All ops require both \`path\` and \`symbol\`. Use \`callers\` for call sites (before renaming/signature changes), \`impact\` for blast radius (what breaks if a symbol changes), \`call_tree\` for what a function calls, \`trace_to\` for how execution reaches a symbol
|
|
38432
|
+
description: `Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. ${zoomEnabled ? "Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships." : "Use aft_callgraph call_tree (depth 1) for one-level forward calls-out; use it for reverse callers or multi-level traces."} All ops require both \`path\` and \`symbol\`. Use \`callers\` for call sites (before renaming/signature changes), \`impact\` for blast radius (what breaks if a symbol changes), \`call_tree\` for what a function calls, \`trace_to\` for how execution reaches a symbol (each path runs back to a root labelled main, test, attribute, public API, or no caller found), \`trace_to_symbol\` for the shortest path from one symbol to another (requires \`toSymbol\`; if ambiguous, the error returns candidate files — retry with \`toPath\`), \`trace_data\` to follow a value across assignments/params. Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly. By default, unresolved external/stdlib leaf calls in call_tree are collapsed into one summary per parent; pass includeUnresolved=true to show every unresolved edge individually. When a list is cut, the reply ends with \`shown N of M <unit> (<reason>) · narrow: <knobs>\`; absence of that line means the list is complete.`,
|
|
37248
38433
|
parameters: navigateParamsSchema(),
|
|
37249
38434
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
37250
38435
|
if (isEmptyParam(params.path)) {
|
|
@@ -37882,7 +39067,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
37882
39067
|
|
|
37883
39068
|
// src/tools/semantic.ts
|
|
37884
39069
|
import { homedir as homedir17 } from "node:os";
|
|
37885
|
-
import { isAbsolute as
|
|
39070
|
+
import { isAbsolute as isAbsolute11, resolve as resolve15 } from "node:path";
|
|
37886
39071
|
import { Type as Type13 } from "typebox";
|
|
37887
39072
|
var FILESYSTEM_SCAN_STATUS = "answered by a filesystem scan";
|
|
37888
39073
|
function semanticHonestyNote(response, theme) {
|
|
@@ -37905,8 +39090,8 @@ function selectedProjectRoot(cwd, pathArg) {
|
|
|
37905
39090
|
const target = pathArg?.trim();
|
|
37906
39091
|
if (!target)
|
|
37907
39092
|
return canonicalizeProjectRoot(cwd);
|
|
37908
|
-
const expanded = target === "~" ? homedir17() : /^~[/\\]/u.test(target) ?
|
|
37909
|
-
return canonicalizeProjectRoot(
|
|
39093
|
+
const expanded = target === "~" ? homedir17() : /^~[/\\]/u.test(target) ? resolve15(homedir17(), target.slice(2)) : target;
|
|
39094
|
+
return canonicalizeProjectRoot(isAbsolute11(expanded) ? expanded : resolve15(cwd, expanded));
|
|
37910
39095
|
}
|
|
37911
39096
|
function continuityKey(projectRoot, query, includeTests, topK) {
|
|
37912
39097
|
return JSON.stringify([
|
|
@@ -38503,93 +39688,52 @@ function registerShutdownCleanup(fn) {
|
|
|
38503
39688
|
}
|
|
38504
39689
|
|
|
38505
39690
|
// src/workflow-hints.ts
|
|
38506
|
-
var HEADING = "
|
|
39691
|
+
var HEADING = "# Agent Foundational Toolkit";
|
|
38507
39692
|
function hashlineTagSourceHint(bashRewritesMintTags, isRegistered = () => true) {
|
|
38508
39693
|
return `**Hashline edit tags**: ${hashlineTagSourceSentence(bashRewritesMintTags, isRegistered)}`;
|
|
38509
39694
|
}
|
|
38510
39695
|
var HASHLINE_TAG_SOURCE_HINT = hashlineTagSourceHint(true);
|
|
38511
39696
|
function buildWorkflowHints(opts) {
|
|
38512
|
-
const
|
|
38513
|
-
const
|
|
38514
|
-
const
|
|
38515
|
-
const
|
|
38516
|
-
|
|
38517
|
-
|
|
38518
|
-
const hasRead = !opts.absentTools.has(readName);
|
|
38519
|
-
const hasGrep = !opts.absentTools.has(grepName);
|
|
38520
|
-
const hasSearch = !opts.absentTools.has("aft_search");
|
|
38521
|
-
const hasNavigate = !opts.absentTools.has("aft_callgraph");
|
|
38522
|
-
const hasInspect = !opts.absentTools.has("aft_inspect");
|
|
38523
|
-
const hasBash = !opts.absentTools.has(bashName);
|
|
38524
|
-
const hasBgBash = opts.bashBackgroundEnabled && hasBash && !opts.absentTools.has("bash_status");
|
|
38525
|
-
const hasWatch = !opts.absentTools.has("bash_watch");
|
|
38526
|
-
const hasWrite = !opts.absentTools.has("bash_write");
|
|
38527
|
-
if (hasBash && opts.bashCompressionEnabled) {
|
|
38528
|
-
sections.push([
|
|
38529
|
-
"**Test/build output**: bash output is auto-compressed for non-piped commands. Piped commands run verbatim and show the pipeline's output. For AFT's test/build summary, run the runner without filters:",
|
|
38530
|
-
"- `bun test | grep fail` → run `bun test`",
|
|
38531
|
-
"- `cargo test 2>&1 | tail -20` → run `cargo test`",
|
|
38532
|
-
"- `npm run build | head -50` → run `npm run build`"
|
|
38533
|
-
].join(`
|
|
38534
|
-
`));
|
|
39697
|
+
const has = (name) => !opts.absentTools.has(name);
|
|
39698
|
+
const hasBash = has("bash");
|
|
39699
|
+
const hasBackground = hasBash && opts.bashBackgroundEnabled && has("bash_watch") && has("bash_status");
|
|
39700
|
+
const finding = [];
|
|
39701
|
+
if (has("aft_search")) {
|
|
39702
|
+
finding.push("start with aft_search; it takes concepts, identifiers, regex and literals.");
|
|
38535
39703
|
}
|
|
38536
|
-
if (
|
|
38537
|
-
|
|
38538
|
-
}
|
|
38539
|
-
if (hasOutline && (hasGrep || hasSearch) && (hasZoom || hasRead)) {
|
|
38540
|
-
const searchName = hasSearch ? "aft_search" : grepName;
|
|
38541
|
-
const locate = hasSearch ? "`aft_search` is the primary code-search tool: one call auto-routes concepts, identifiers, regex, error strings, and literals." : `\`${grepName}\` (the tool — indexed and ranked) locates code.`;
|
|
38542
|
-
const zoomSteer = hasZoom ? ", or `aft_zoom`" : "";
|
|
38543
|
-
const searchSteer = hasSearch ? `use \`aft_search\` (concepts, identifiers, regex, literals), \`${readName}\`, \`aft_outline\`${zoomSteer} instead` : `use the \`${grepName}\` tool, \`${readName}\`, \`aft_outline\`${zoomSteer} instead`;
|
|
38544
|
-
const bashSteer = hasBash ? ` If you are about to run grep, rg, sed, awk, find, or cat through ${bashName} to locate or read code: STOP — ${searchSteer}.` : "";
|
|
38545
|
-
sections.push([
|
|
38546
|
-
`**Code exploration**: ${locate} Then \`aft_outline\` for structure → \`${hasZoom ? "aft_zoom" : readName}\` for symbol(s). DO NOT run \`grep\`/\`rg\`/\`find\`/\`sed\`/\`cat\` through \`bash\` to locate or read code — the bash path is unindexed, unranked, serial, and routinely surfaces the wrong hit. Keep \`bash\` for shell facts (git state, file metadata, processes).${bashSteer} Reflex translations:`,
|
|
38547
|
-
`- \`grep -rn "handleAuth" src/\` in bash → \`${searchName}({ query: "handleAuth" })\``,
|
|
38548
|
-
`- \`find . -name "*.ts" | xargs grep watcher\` in bash → \`${searchName}({ query: "watcher invalidation" })\` (concepts work too)`,
|
|
38549
|
-
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({ path: "app.ts", startLine: 100, endLine: 160 })\``
|
|
38550
|
-
].join(`
|
|
38551
|
-
`));
|
|
39704
|
+
if (has("aft_outline") && has("aft_zoom") && has("read")) {
|
|
39705
|
+
finding.push(`${finding.length ? "Then " : ""}aft_outline for structure, aft_zoom for named symbols, read for line ranges.`);
|
|
38552
39706
|
}
|
|
38553
|
-
if (
|
|
38554
|
-
|
|
38555
|
-
sections.push("**AFT status bar**: tool results may end with a one-line health bar `[AFT E<errors> W<warnings> | D<dead-code> U<unused-exports> C<clone/dup-groups> | T<todos>]` — an IDE-style glance that appears when a count changes. `E`/`W` are live LSP diagnostics for files touched this session (your universal compile-error signal across every language with an LSP). A `~` before `D` means the dead-code/unused/dup counts predate your latest edit — run `aft_inspect` for current numbers and detail. A `?` in place of a number means that count has no value yet (not zero). When `E>0`, you likely just introduced errors; investigate before moving on.");
|
|
38556
|
-
}
|
|
38557
|
-
if (hasNavigate) {
|
|
38558
|
-
sections.push([
|
|
38559
|
-
"Use `aft_callgraph` for code-relationship questions instead of grep + read chains:",
|
|
38560
|
-
"- `callers` — find all call sites before changing a function signature",
|
|
38561
|
-
"- `impact` — blast radius (which functions/files will need updates)",
|
|
38562
|
-
"- `trace_to` — how execution reaches this code from entry points (routes, exports, main)",
|
|
38563
|
-
"- `trace_to_symbol` — shortest call path from one symbol to another",
|
|
38564
|
-
"- `trace_data` — follow a value through assignments and parameters across files"
|
|
38565
|
-
].join(`
|
|
38566
|
-
`));
|
|
39707
|
+
if (hasBash) {
|
|
39708
|
+
finding.push("Don't find or read code with grep, find, sed or cat in bash.");
|
|
38567
39709
|
}
|
|
38568
|
-
|
|
38569
|
-
|
|
38570
|
-
|
|
38571
|
-
"- `background: true` is ONLY for when you have OTHER useful work to do while it runs: start it, do the other work, and the completion reminder delivers the result (or spawn a subagent for the side work). Do NOT background a command and then immediately `bash_watch` it — that spends a whole extra turn waiting for something foreground returns in one.",
|
|
38572
|
-
"- `bash_watch` is for blocking on an ALREADY-backgrounded task once you've run out of parallel work (sync — the user can interrupt), or reacting to a specific early output line (async: background:true + pattern). Never loop `bash_status` to wait — it's a one-shot inspector."
|
|
38573
|
-
].join(`
|
|
38574
|
-
`));
|
|
38575
|
-
}
|
|
38576
|
-
if (hasBgBash && hasWrite) {
|
|
38577
|
-
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`bash_status({ task_id, output_mode: "screen" })\`, and send input with \`bash_write({ task_id, input: "..." })\`.`);
|
|
39710
|
+
const commands = [];
|
|
39711
|
+
if (hasBash && opts.bashCompressionEnabled) {
|
|
39712
|
+
commands.push("don't pipe test or build output into tail, grep or head; AFT already shortens it.");
|
|
38578
39713
|
}
|
|
38579
|
-
if (
|
|
38580
|
-
|
|
39714
|
+
if (hasBackground) {
|
|
39715
|
+
commands.push("Pass wait: true when you need a long command's result before anything else; a new message from the user detaches the wait.", "Otherwise a long command moves to the background and a reminder brings its result.", "Block on it with bash_watch only when nothing else is left; bash_watch with background: true and a pattern tells you when a line appears.", "Never loop bash_status.");
|
|
38581
39716
|
}
|
|
38582
|
-
|
|
39717
|
+
const sections = [
|
|
39718
|
+
finding.length ? `**Finding code**: ${finding.join(" ")}` : "",
|
|
39719
|
+
has("aft_callgraph") ? "**Relationships**: aft_callgraph for callers, impact, and execution or data-flow traces." : "",
|
|
39720
|
+
has("aft_outline") && has("aft_zoom") ? "**Web pages**: aft_outline with a URL as target, then aft_zoom on a heading." : "",
|
|
39721
|
+
"",
|
|
39722
|
+
has("aft_inspect") ? "**After edits**: run aft_inspect for diagnostics; the project's typecheck and tests are the final check." : "",
|
|
39723
|
+
has("aft_inspect") ? "**Status bar**: `[AFT E.. W.. | T..]` appears when a count changes. E above 0 usually means your last edit broke something; `?` means unknown, not zero." : "",
|
|
39724
|
+
commands.length ? `**Commands**: ${commands.join(" ")}` : ""
|
|
39725
|
+
];
|
|
39726
|
+
const hashline = opts.hashlineEffective === true ? hashlineTagSourceHint(hasBash && opts.bashRewriteEnabled !== false, has) : "";
|
|
39727
|
+
if (!sections.some(Boolean) && !hashline)
|
|
38583
39728
|
return null;
|
|
38584
|
-
|
|
38585
|
-
|
|
38586
|
-
|
|
38587
|
-
**Parallel tool calls**: when several read-only operations are independent, emit them in ONE response instead of serializing — file reads, structure and symbol lookups, code search, diagnostics, and git status/diff/log. Sequence only when a call depends on a prior result or when a command mutates state.`);
|
|
38588
|
-
return `${HEADING}
|
|
38589
|
-
|
|
38590
|
-
${sections.join(`
|
|
39729
|
+
sections[3] = "**Parallel calls**: send independent read-only calls in one response.";
|
|
39730
|
+
const text = `${HEADING}
|
|
38591
39731
|
|
|
39732
|
+
${sections.filter(Boolean).join(`
|
|
38592
39733
|
`)}`;
|
|
39734
|
+
return hashline ? `${text}
|
|
39735
|
+
|
|
39736
|
+
${hashline}` : text;
|
|
38593
39737
|
}
|
|
38594
39738
|
function buildHintsFromConfig(config, absentTools, hashlineEffective = false) {
|
|
38595
39739
|
const bashCfg = resolveBashConfig(config);
|
|
@@ -38694,18 +39838,18 @@ function createVersionMismatchHandler(getPool, ensureCompatibleBinary = ensureBi
|
|
|
38694
39838
|
}
|
|
38695
39839
|
var PLUGIN_VERSION = (() => {
|
|
38696
39840
|
try {
|
|
38697
|
-
const req =
|
|
39841
|
+
const req = createRequire3(import.meta.url);
|
|
38698
39842
|
return req("../package.json").version;
|
|
38699
39843
|
} catch {
|
|
38700
39844
|
return "0.0.0";
|
|
38701
39845
|
}
|
|
38702
39846
|
})();
|
|
38703
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
39847
|
+
var ANNOUNCEMENT_VERSION = "0.65.0";
|
|
38704
39848
|
var ANNOUNCEMENT_FEATURES = [
|
|
38705
|
-
"
|
|
38706
|
-
"
|
|
38707
|
-
"
|
|
38708
|
-
"
|
|
39849
|
+
"OpenCode 2: AFT's read-only tools also work inside Code Mode as `tools.aft.code.*`.",
|
|
39850
|
+
"TypeScript monorepos run one language server per TypeScript version instead of one per package; `lsp.max_servers` caps the total.",
|
|
39851
|
+
"Search and grep stay light on large growing files, and shell waits survive the computer going to sleep.",
|
|
39852
|
+
"`views.enabled`, `views.callgraph_copies` and `worktree.ram_overlay` are now refused in user config; `npx @cortexkit/aft doctor --fix` removes them."
|
|
38709
39853
|
];
|
|
38710
39854
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
38711
39855
|
var pendingEagerWarnings = new Map;
|