@cortexkit/aft-pi 0.46.0 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bash-wait-detach.d.ts +5 -0
- package/dist/bash-wait-detach.d.ts.map +1 -0
- package/dist/config.d.ts +4 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1629 -657
- package/dist/logger.d.ts.map +1 -1
- package/dist/lsp-auto-install.d.ts +11 -7
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-github-install.d.ts +2 -0
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +10 -1
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +2 -2
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +3 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/refactor.d.ts +3 -3
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -9697,6 +9697,16 @@ function isEmptyParam(value) {
|
|
|
9697
9697
|
return Object.keys(value).length === 0;
|
|
9698
9698
|
return false;
|
|
9699
9699
|
}
|
|
9700
|
+
// ../aft-bridge/dist/config-keys.js
|
|
9701
|
+
var OPENCODE_ONLY_KEYS = ["hoist_builtin_tools", "auto_update"];
|
|
9702
|
+
function stripHarnessSpecificConfigKeys(value, keys) {
|
|
9703
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9704
|
+
return value;
|
|
9705
|
+
const stripped = { ...value };
|
|
9706
|
+
for (const key of keys)
|
|
9707
|
+
delete stripped[key];
|
|
9708
|
+
return stripped;
|
|
9709
|
+
}
|
|
9700
9710
|
// ../aft-bridge/dist/config-tiers.js
|
|
9701
9711
|
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
9702
9712
|
import { resolve as resolve2 } from "node:path";
|
|
@@ -10017,6 +10027,117 @@ async function fetchLatestTag() {
|
|
|
10017
10027
|
clearTimeout(timeout);
|
|
10018
10028
|
}
|
|
10019
10029
|
}
|
|
10030
|
+
// ../aft-bridge/dist/durable-log.js
|
|
10031
|
+
import { appendFile, mkdir, rename, rm, stat } from "node:fs/promises";
|
|
10032
|
+
import { homedir as homedir5 } from "node:os";
|
|
10033
|
+
import { dirname, join as join4 } from "node:path";
|
|
10034
|
+
var DEFAULT_LOG_BYTES = 20 * 1024 * 1024;
|
|
10035
|
+
var DEFAULT_LOG_GENERATIONS = 5;
|
|
10036
|
+
function homeDir() {
|
|
10037
|
+
if (process.platform === "win32")
|
|
10038
|
+
return process.env.USERPROFILE || process.env.HOME || homedir5();
|
|
10039
|
+
return process.env.HOME || homedir5();
|
|
10040
|
+
}
|
|
10041
|
+
function dataHome() {
|
|
10042
|
+
if (process.env.XDG_DATA_HOME)
|
|
10043
|
+
return process.env.XDG_DATA_HOME;
|
|
10044
|
+
if (process.platform === "win32") {
|
|
10045
|
+
return process.env.LOCALAPPDATA || process.env.APPDATA || join4(homeDir(), "AppData", "Local");
|
|
10046
|
+
}
|
|
10047
|
+
return join4(homeDir(), ".local", "share");
|
|
10048
|
+
}
|
|
10049
|
+
function resolveAftStorageRoot(configuredRoot) {
|
|
10050
|
+
if (configuredRoot)
|
|
10051
|
+
return configuredRoot;
|
|
10052
|
+
if (process.env.AFT_CACHE_DIR)
|
|
10053
|
+
return join4(process.env.AFT_CACHE_DIR, "aft");
|
|
10054
|
+
return join4(dataHome(), "cortexkit", "aft");
|
|
10055
|
+
}
|
|
10056
|
+
function resolveAftLogPath(filename, configuredRoot) {
|
|
10057
|
+
return join4(resolveAftStorageRoot(configuredRoot), "logs", filename);
|
|
10058
|
+
}
|
|
10059
|
+
|
|
10060
|
+
class RotatingLogSink {
|
|
10061
|
+
path;
|
|
10062
|
+
maxBytes;
|
|
10063
|
+
generations;
|
|
10064
|
+
estimatedBytes = null;
|
|
10065
|
+
queue = Promise.resolve();
|
|
10066
|
+
disabled = false;
|
|
10067
|
+
failureReported = false;
|
|
10068
|
+
constructor(path2, options = {}) {
|
|
10069
|
+
this.path = path2;
|
|
10070
|
+
this.maxBytes = options.maxBytes ?? DEFAULT_LOG_BYTES;
|
|
10071
|
+
this.generations = options.generations ?? DEFAULT_LOG_GENERATIONS;
|
|
10072
|
+
}
|
|
10073
|
+
append(data) {
|
|
10074
|
+
if (this.disabled || data.length === 0)
|
|
10075
|
+
return;
|
|
10076
|
+
this.queue = this.queue.then(() => this.write(data)).catch((error2) => {
|
|
10077
|
+
this.disabled = true;
|
|
10078
|
+
if (!this.failureReported) {
|
|
10079
|
+
this.failureReported = true;
|
|
10080
|
+
try {
|
|
10081
|
+
process.stderr.write(`[aft-plugin] durable log disabled for ${this.path}: ${error2 instanceof Error ? error2.message : String(error2)}
|
|
10082
|
+
`);
|
|
10083
|
+
} catch {}
|
|
10084
|
+
}
|
|
10085
|
+
});
|
|
10086
|
+
}
|
|
10087
|
+
async drain() {
|
|
10088
|
+
await this.queue;
|
|
10089
|
+
}
|
|
10090
|
+
async write(data) {
|
|
10091
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
10092
|
+
if (this.estimatedBytes === null) {
|
|
10093
|
+
try {
|
|
10094
|
+
this.estimatedBytes = (await stat(this.path)).size;
|
|
10095
|
+
} catch (error2) {
|
|
10096
|
+
if (!isMissing(error2))
|
|
10097
|
+
throw error2;
|
|
10098
|
+
this.estimatedBytes = 0;
|
|
10099
|
+
}
|
|
10100
|
+
}
|
|
10101
|
+
const bytes = Buffer.byteLength(data);
|
|
10102
|
+
if (this.estimatedBytes > 0 && this.estimatedBytes + bytes > this.maxBytes) {
|
|
10103
|
+
await this.rotate();
|
|
10104
|
+
}
|
|
10105
|
+
await appendFile(this.path, data, "utf8");
|
|
10106
|
+
this.estimatedBytes = (this.estimatedBytes ?? 0) + bytes;
|
|
10107
|
+
}
|
|
10108
|
+
async rotate() {
|
|
10109
|
+
if (this.generations <= 0) {
|
|
10110
|
+
await removeIfPresent(this.path);
|
|
10111
|
+
this.estimatedBytes = 0;
|
|
10112
|
+
return;
|
|
10113
|
+
}
|
|
10114
|
+
await removeIfPresent(`${this.path}.${this.generations}`);
|
|
10115
|
+
for (let generation = this.generations - 1;generation >= 1; generation -= 1) {
|
|
10116
|
+
await renameIfPresent(`${this.path}.${generation}`, `${this.path}.${generation + 1}`);
|
|
10117
|
+
}
|
|
10118
|
+
await renameIfPresent(this.path, `${this.path}.1`);
|
|
10119
|
+
this.estimatedBytes = 0;
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10122
|
+
function isMissing(error2) {
|
|
10123
|
+
return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
|
|
10124
|
+
}
|
|
10125
|
+
async function removeIfPresent(path2) {
|
|
10126
|
+
try {
|
|
10127
|
+
await rm(path2, { force: true });
|
|
10128
|
+
} catch (error2) {
|
|
10129
|
+
if (!isMissing(error2))
|
|
10130
|
+
throw error2;
|
|
10131
|
+
}
|
|
10132
|
+
}
|
|
10133
|
+
async function renameIfPresent(from, to) {
|
|
10134
|
+
try {
|
|
10135
|
+
await rename(from, to);
|
|
10136
|
+
} catch (error2) {
|
|
10137
|
+
if (!isMissing(error2))
|
|
10138
|
+
throw error2;
|
|
10139
|
+
}
|
|
10140
|
+
}
|
|
10020
10141
|
// ../aft-bridge/dist/edit-summary.js
|
|
10021
10142
|
function formatEditSummary(data) {
|
|
10022
10143
|
if (data.rolled_back === true) {
|
|
@@ -10087,32 +10208,32 @@ function stripJsoncSymbols(value) {
|
|
|
10087
10208
|
// ../aft-bridge/dist/migration.js
|
|
10088
10209
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
10089
10210
|
import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10090
|
-
import { homedir as
|
|
10091
|
-
import { basename, dirname as
|
|
10211
|
+
import { homedir as homedir8, tmpdir } from "node:os";
|
|
10212
|
+
import { basename, dirname as dirname3, join as join7, resolve as resolve4 } from "node:path";
|
|
10092
10213
|
|
|
10093
10214
|
// ../aft-bridge/dist/paths.js
|
|
10094
10215
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync } from "node:fs";
|
|
10095
|
-
import { homedir as
|
|
10096
|
-
import { dirname, isAbsolute as isAbsolute2, join as
|
|
10097
|
-
function
|
|
10216
|
+
import { homedir as homedir6 } from "node:os";
|
|
10217
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, resolve as resolve3 } from "node:path";
|
|
10218
|
+
function homeDir2() {
|
|
10098
10219
|
if (process.platform === "win32")
|
|
10099
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
10100
|
-
return process.env.HOME ||
|
|
10220
|
+
return process.env.USERPROFILE || process.env.HOME || homedir6();
|
|
10221
|
+
return process.env.HOME || homedir6();
|
|
10101
10222
|
}
|
|
10102
10223
|
function configHome() {
|
|
10103
10224
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
10104
10225
|
if (xdg && isAbsolute2(xdg))
|
|
10105
10226
|
return xdg;
|
|
10106
|
-
return
|
|
10227
|
+
return join5(homeDir2(), ".config");
|
|
10107
10228
|
}
|
|
10108
10229
|
function legacyOpenCodeConfigDir() {
|
|
10109
10230
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
10110
10231
|
if (envDir)
|
|
10111
10232
|
return resolve3(envDir);
|
|
10112
|
-
return
|
|
10233
|
+
return join5(configHome(), "opencode");
|
|
10113
10234
|
}
|
|
10114
10235
|
function legacyPiAgentDir() {
|
|
10115
|
-
return
|
|
10236
|
+
return join5(homeDir2(), ".pi", "agent");
|
|
10116
10237
|
}
|
|
10117
10238
|
function legacySources(basePath, label, harness) {
|
|
10118
10239
|
return [
|
|
@@ -10121,10 +10242,10 @@ function legacySources(basePath, label, harness) {
|
|
|
10121
10242
|
];
|
|
10122
10243
|
}
|
|
10123
10244
|
function resolveCortexKitUserConfigPath() {
|
|
10124
|
-
return
|
|
10245
|
+
return join5(configHome(), "cortexkit", "aft.jsonc");
|
|
10125
10246
|
}
|
|
10126
10247
|
function resolveCortexKitProjectConfigPath(projectDirectory) {
|
|
10127
|
-
return
|
|
10248
|
+
return join5(projectDirectory, ".cortexkit", "aft.jsonc");
|
|
10128
10249
|
}
|
|
10129
10250
|
function resolveCortexKitConfigPaths(projectDirectory) {
|
|
10130
10251
|
return {
|
|
@@ -10135,25 +10256,25 @@ function resolveCortexKitConfigPaths(projectDirectory) {
|
|
|
10135
10256
|
function resolveLegacyAftConfigSources(projectDirectory) {
|
|
10136
10257
|
return {
|
|
10137
10258
|
user: [
|
|
10138
|
-
...legacySources(
|
|
10139
|
-
...legacySources(
|
|
10259
|
+
...legacySources(join5(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
|
|
10260
|
+
...legacySources(join5(legacyPiAgentDir(), "aft"), "Pi user", "pi")
|
|
10140
10261
|
],
|
|
10141
10262
|
project: [
|
|
10142
|
-
...legacySources(
|
|
10143
|
-
...legacySources(
|
|
10263
|
+
...legacySources(join5(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
|
|
10264
|
+
...legacySources(join5(projectDirectory, ".pi", "aft"), "Pi project", "pi")
|
|
10144
10265
|
]
|
|
10145
10266
|
};
|
|
10146
10267
|
}
|
|
10147
10268
|
function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
|
|
10148
|
-
return
|
|
10269
|
+
return join5(storageRoot, harness, ...segments);
|
|
10149
10270
|
}
|
|
10150
10271
|
function repairRootScopedStorageFile(storageRoot, harness, fileName) {
|
|
10151
10272
|
const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
|
|
10152
|
-
const rootPath =
|
|
10273
|
+
const rootPath = join5(storageRoot, fileName);
|
|
10153
10274
|
if (existsSync3(harnessPath) || !existsSync3(rootPath))
|
|
10154
10275
|
return harnessPath;
|
|
10155
10276
|
try {
|
|
10156
|
-
mkdirSync2(
|
|
10277
|
+
mkdirSync2(dirname2(harnessPath), { recursive: true });
|
|
10157
10278
|
renameSync2(rootPath, harnessPath);
|
|
10158
10279
|
} catch {}
|
|
10159
10280
|
return harnessPath;
|
|
@@ -10174,7 +10295,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
|
|
|
10174
10295
|
return false;
|
|
10175
10296
|
if (!lastVersion) {
|
|
10176
10297
|
try {
|
|
10177
|
-
mkdirSync2(
|
|
10298
|
+
mkdirSync2(dirname2(versionFile), { recursive: true });
|
|
10178
10299
|
writeFileSync(versionFile, currentVersion);
|
|
10179
10300
|
} catch {}
|
|
10180
10301
|
return false;
|
|
@@ -10186,17 +10307,60 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
10186
10307
|
return;
|
|
10187
10308
|
const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
|
|
10188
10309
|
try {
|
|
10189
|
-
mkdirSync2(
|
|
10310
|
+
mkdirSync2(dirname2(versionFile), { recursive: true });
|
|
10190
10311
|
writeFileSync(versionFile, currentVersion);
|
|
10191
10312
|
} catch {}
|
|
10192
10313
|
}
|
|
10314
|
+
function decodeFileUrl(target) {
|
|
10315
|
+
if (!target.startsWith("file:"))
|
|
10316
|
+
return target;
|
|
10317
|
+
const rest = target.slice("file:".length);
|
|
10318
|
+
let pathPart;
|
|
10319
|
+
if (rest.startsWith("//")) {
|
|
10320
|
+
const after = rest.slice(2);
|
|
10321
|
+
const slash = after.indexOf("/");
|
|
10322
|
+
const authority = slash === -1 ? after : after.slice(0, slash);
|
|
10323
|
+
const p = slash === -1 ? "" : after.slice(slash);
|
|
10324
|
+
if (authority === "" || authority === "localhost")
|
|
10325
|
+
pathPart = p;
|
|
10326
|
+
else if (process.platform === "win32")
|
|
10327
|
+
pathPart = `//${authority}${p}`;
|
|
10328
|
+
else
|
|
10329
|
+
return target;
|
|
10330
|
+
} else if (rest.startsWith("/")) {
|
|
10331
|
+
pathPart = rest;
|
|
10332
|
+
} else {
|
|
10333
|
+
return target;
|
|
10334
|
+
}
|
|
10335
|
+
const bytes = [];
|
|
10336
|
+
for (let i = 0;i < pathPart.length; ) {
|
|
10337
|
+
if (pathPart[i] === "%" && i + 2 < pathPart.length) {
|
|
10338
|
+
const hex = pathPart.slice(i + 1, i + 3);
|
|
10339
|
+
if (/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
10340
|
+
bytes.push(Number.parseInt(hex, 16));
|
|
10341
|
+
i += 3;
|
|
10342
|
+
continue;
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
const cp = pathPart.codePointAt(i);
|
|
10346
|
+
const ch = String.fromCodePoint(cp);
|
|
10347
|
+
for (const b of Buffer.from(ch, "utf8"))
|
|
10348
|
+
bytes.push(b);
|
|
10349
|
+
i += ch.length;
|
|
10350
|
+
}
|
|
10351
|
+
const decoded = Buffer.from(bytes).toString("utf8");
|
|
10352
|
+
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(decoded)) {
|
|
10353
|
+
return decoded.slice(1);
|
|
10354
|
+
}
|
|
10355
|
+
return decoded;
|
|
10356
|
+
}
|
|
10193
10357
|
|
|
10194
10358
|
// ../aft-bridge/dist/resolver.js
|
|
10195
10359
|
import { execSync } from "node:child_process";
|
|
10196
10360
|
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
10197
10361
|
import { createRequire as createRequire2 } from "node:module";
|
|
10198
|
-
import { homedir as
|
|
10199
|
-
import { join as
|
|
10362
|
+
import { homedir as homedir7 } from "node:os";
|
|
10363
|
+
import { join as join6 } from "node:path";
|
|
10200
10364
|
var ensureBinaryForResolver = ensureBinary;
|
|
10201
10365
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
10202
10366
|
try {
|
|
@@ -10205,9 +10369,9 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
10205
10369
|
return null;
|
|
10206
10370
|
const tag = version.startsWith("v") ? version : `v${version}`;
|
|
10207
10371
|
const cacheDir = getCacheDir();
|
|
10208
|
-
const versionedDir =
|
|
10372
|
+
const versionedDir = join6(cacheDir, tag);
|
|
10209
10373
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
10210
|
-
const cachedPath =
|
|
10374
|
+
const cachedPath = join6(versionedDir, `aft${ext}`);
|
|
10211
10375
|
if (existsSync4(cachedPath)) {
|
|
10212
10376
|
const cachedVersion = readBinaryVersion(cachedPath);
|
|
10213
10377
|
if (cachedVersion === version)
|
|
@@ -10237,18 +10401,18 @@ function normalizeBareVersion(version) {
|
|
|
10237
10401
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
10238
10402
|
}
|
|
10239
10403
|
function homeDirFromEnv(env) {
|
|
10240
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
10404
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir7();
|
|
10241
10405
|
}
|
|
10242
10406
|
function cacheDirFromEnv(env) {
|
|
10243
10407
|
if (process.platform === "win32") {
|
|
10244
|
-
const base2 = env.LOCALAPPDATA || env.APPDATA ||
|
|
10245
|
-
return
|
|
10408
|
+
const base2 = env.LOCALAPPDATA || env.APPDATA || join6(homeDirFromEnv(env), "AppData", "Local");
|
|
10409
|
+
return join6(base2, "aft", "bin");
|
|
10246
10410
|
}
|
|
10247
|
-
const base = env.XDG_CACHE_HOME ||
|
|
10248
|
-
return
|
|
10411
|
+
const base = env.XDG_CACHE_HOME || join6(homeDirFromEnv(env), ".cache");
|
|
10412
|
+
return join6(base, "aft", "bin");
|
|
10249
10413
|
}
|
|
10250
10414
|
function cachedBinaryPathFromEnv(version, env, ext) {
|
|
10251
|
-
const binaryPath =
|
|
10415
|
+
const binaryPath = join6(cacheDirFromEnv(env), version, `aft${ext}`);
|
|
10252
10416
|
return existsSync4(binaryPath) ? binaryPath : null;
|
|
10253
10417
|
}
|
|
10254
10418
|
function isExpectedCachedBinary2(binaryPath, expectedVersion) {
|
|
@@ -10367,7 +10531,7 @@ function findBinarySync(expectedVersion) {
|
|
|
10367
10531
|
return usable;
|
|
10368
10532
|
}
|
|
10369
10533
|
} catch {}
|
|
10370
|
-
const cargoPath =
|
|
10534
|
+
const cargoPath = join6(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
|
|
10371
10535
|
if (existsSync4(cargoPath)) {
|
|
10372
10536
|
const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
|
|
10373
10537
|
if (usable)
|
|
@@ -10409,26 +10573,26 @@ async function findBinary(expectedVersion) {
|
|
|
10409
10573
|
var spawnSyncForMigration = spawnSync2;
|
|
10410
10574
|
var TARGET_MARKER = ".migrated_from_legacy";
|
|
10411
10575
|
var DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
10412
|
-
function
|
|
10576
|
+
function dataHome2() {
|
|
10413
10577
|
if (process.env.XDG_DATA_HOME)
|
|
10414
10578
|
return process.env.XDG_DATA_HOME;
|
|
10415
10579
|
if (process.platform === "win32") {
|
|
10416
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA ||
|
|
10580
|
+
return process.env.LOCALAPPDATA || process.env.APPDATA || join7(homeDir3(), "AppData", "Local");
|
|
10417
10581
|
}
|
|
10418
|
-
return
|
|
10582
|
+
return join7(homeDir3(), ".local", "share");
|
|
10419
10583
|
}
|
|
10420
|
-
function
|
|
10584
|
+
function homeDir3() {
|
|
10421
10585
|
if (process.platform === "win32")
|
|
10422
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
10423
|
-
return process.env.HOME ||
|
|
10586
|
+
return process.env.USERPROFILE || process.env.HOME || homedir8();
|
|
10587
|
+
return process.env.HOME || homedir8();
|
|
10424
10588
|
}
|
|
10425
10589
|
function resolveLegacyStorageRoot(harness) {
|
|
10426
10590
|
if (harness === "pi")
|
|
10427
|
-
return
|
|
10428
|
-
return
|
|
10591
|
+
return join7(homeDir3(), ".pi", "agent", "aft");
|
|
10592
|
+
return join7(dataHome2(), "opencode", "storage", "plugin", "aft");
|
|
10429
10593
|
}
|
|
10430
10594
|
function resolveCortexKitStorageRoot() {
|
|
10431
|
-
return
|
|
10595
|
+
return join7(dataHome2(), "cortexkit", "aft");
|
|
10432
10596
|
}
|
|
10433
10597
|
function stripJsoncForParse(input) {
|
|
10434
10598
|
let out = "";
|
|
@@ -10557,8 +10721,8 @@ function acquireConfigMigrationLock(lockDir) {
|
|
|
10557
10721
|
}
|
|
10558
10722
|
}
|
|
10559
10723
|
function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
10560
|
-
mkdirSync4(
|
|
10561
|
-
const tmpPath =
|
|
10724
|
+
mkdirSync4(dirname3(targetPath), { recursive: true });
|
|
10725
|
+
const tmpPath = join7(dirname3(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
10562
10726
|
let fd = null;
|
|
10563
10727
|
try {
|
|
10564
10728
|
fd = openSync3(tmpPath, "wx", 384);
|
|
@@ -10579,8 +10743,8 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
|
10579
10743
|
}
|
|
10580
10744
|
}
|
|
10581
10745
|
function atomicWriteConfigFile(targetPath, content) {
|
|
10582
|
-
mkdirSync4(
|
|
10583
|
-
const tmpPath =
|
|
10746
|
+
mkdirSync4(dirname3(targetPath), { recursive: true });
|
|
10747
|
+
const tmpPath = join7(dirname3(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
10584
10748
|
let fd = null;
|
|
10585
10749
|
try {
|
|
10586
10750
|
fd = openSync3(tmpPath, "wx", 384);
|
|
@@ -10705,7 +10869,7 @@ function migrateAftConfigFile(opts) {
|
|
|
10705
10869
|
if (existingSources.length === 0) {
|
|
10706
10870
|
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
10707
10871
|
}
|
|
10708
|
-
mkdirSync4(
|
|
10872
|
+
mkdirSync4(dirname3(opts.targetPath), { recursive: true });
|
|
10709
10873
|
const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
|
|
10710
10874
|
try {
|
|
10711
10875
|
const sources = existingSources.map((source) => ({
|
|
@@ -10762,13 +10926,13 @@ function spawnErrorLabel(error2) {
|
|
|
10762
10926
|
return [code, error2.message].filter(Boolean).join(": ");
|
|
10763
10927
|
}
|
|
10764
10928
|
function migrationLogPath(newRoot, harness, logger) {
|
|
10765
|
-
const desired =
|
|
10929
|
+
const desired = join7(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
|
|
10766
10930
|
try {
|
|
10767
|
-
mkdirSync4(
|
|
10931
|
+
mkdirSync4(dirname3(desired), { recursive: true });
|
|
10768
10932
|
return desired;
|
|
10769
10933
|
} catch (err) {
|
|
10770
|
-
const fallback =
|
|
10771
|
-
logger?.warn?.(`Failed to create AFT migration log directory ${
|
|
10934
|
+
const fallback = join7(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
|
|
10935
|
+
logger?.warn?.(`Failed to create AFT migration log directory ${dirname3(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
|
|
10772
10936
|
return fallback;
|
|
10773
10937
|
}
|
|
10774
10938
|
}
|
|
@@ -10830,13 +10994,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
10830
10994
|
}
|
|
10831
10995
|
// ../aft-bridge/dist/npm-resolver.js
|
|
10832
10996
|
import { readdirSync, statSync as statSync3 } from "node:fs";
|
|
10833
|
-
import { homedir as
|
|
10834
|
-
import { delimiter, dirname as
|
|
10997
|
+
import { homedir as homedir9 } from "node:os";
|
|
10998
|
+
import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join8 } from "node:path";
|
|
10835
10999
|
function defaultDeps() {
|
|
10836
11000
|
return {
|
|
10837
11001
|
platform: process.platform,
|
|
10838
11002
|
env: process.env,
|
|
10839
|
-
home:
|
|
11003
|
+
home: homedir9(),
|
|
10840
11004
|
execPath: process.execPath
|
|
10841
11005
|
};
|
|
10842
11006
|
}
|
|
@@ -10857,14 +11021,14 @@ function npmFromPath(deps) {
|
|
|
10857
11021
|
const dir = entry.trim().replace(/^"|"$/g, "");
|
|
10858
11022
|
if (!dir || !isAbsolute3(dir))
|
|
10859
11023
|
continue;
|
|
10860
|
-
if (isFile(
|
|
11024
|
+
if (isFile(join8(dir, name)))
|
|
10861
11025
|
return dir;
|
|
10862
11026
|
}
|
|
10863
11027
|
return null;
|
|
10864
11028
|
}
|
|
10865
11029
|
function npmAdjacentToNode(deps) {
|
|
10866
|
-
const dir =
|
|
10867
|
-
return isFile(
|
|
11030
|
+
const dir = dirname4(deps.execPath);
|
|
11031
|
+
return isFile(join8(dir, npmBinaryName(deps.platform))) ? dir : null;
|
|
10868
11032
|
}
|
|
10869
11033
|
function highestVersionedNodeBin(installsDir, name) {
|
|
10870
11034
|
let entries;
|
|
@@ -10873,8 +11037,8 @@ function highestVersionedNodeBin(installsDir, name) {
|
|
|
10873
11037
|
} catch {
|
|
10874
11038
|
return null;
|
|
10875
11039
|
}
|
|
10876
|
-
const candidates = entries.filter((v) => isFile(
|
|
10877
|
-
return candidates.length > 0 ?
|
|
11040
|
+
const candidates = entries.filter((v) => isFile(join8(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
|
|
11041
|
+
return candidates.length > 0 ? join8(installsDir, candidates[0], "bin") : null;
|
|
10878
11042
|
}
|
|
10879
11043
|
function compareVersionsDesc(a, b) {
|
|
10880
11044
|
const pa = a.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
|
|
@@ -10899,22 +11063,22 @@ function wellKnownNpmDirs(deps) {
|
|
|
10899
11063
|
const programFiles = env.ProgramFiles || "C:\\Program Files";
|
|
10900
11064
|
const appData = env.APPDATA;
|
|
10901
11065
|
const localAppData = env.LOCALAPPDATA;
|
|
10902
|
-
push(
|
|
11066
|
+
push(join8(programFiles, "nodejs"));
|
|
10903
11067
|
if (appData)
|
|
10904
|
-
push(
|
|
11068
|
+
push(join8(appData, "npm"));
|
|
10905
11069
|
if (localAppData)
|
|
10906
|
-
push(
|
|
11070
|
+
push(join8(localAppData, "Volta", "bin"));
|
|
10907
11071
|
if (env.NVM_SYMLINK)
|
|
10908
11072
|
push(env.NVM_SYMLINK);
|
|
10909
11073
|
} else {
|
|
10910
11074
|
if (env.NVM_BIN)
|
|
10911
11075
|
push(env.NVM_BIN);
|
|
10912
|
-
push(highestVersionedNodeBin(
|
|
10913
|
-
push(highestVersionedNodeBin(
|
|
10914
|
-
push(highestVersionedNodeBin(
|
|
10915
|
-
push(
|
|
10916
|
-
push(
|
|
10917
|
-
const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin",
|
|
11076
|
+
push(highestVersionedNodeBin(join8(home, ".nvm", "versions", "node"), name));
|
|
11077
|
+
push(highestVersionedNodeBin(join8(home, ".local", "share", "mise", "installs", "node"), name));
|
|
11078
|
+
push(highestVersionedNodeBin(join8(home, ".asdf", "installs", "nodejs"), name));
|
|
11079
|
+
push(join8(home, ".volta", "bin"));
|
|
11080
|
+
push(join8(home, ".asdf", "shims"));
|
|
11081
|
+
const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join8(home, ".local", "bin")]);
|
|
10918
11082
|
for (const dir of systemDirs)
|
|
10919
11083
|
push(dir);
|
|
10920
11084
|
}
|
|
@@ -10924,12 +11088,12 @@ function resolveNpm(deps = defaultDeps()) {
|
|
|
10924
11088
|
const name = npmBinaryName(deps.platform);
|
|
10925
11089
|
const onPath = npmFromPath(deps);
|
|
10926
11090
|
if (onPath)
|
|
10927
|
-
return { command:
|
|
11091
|
+
return { command: join8(onPath, name), binDir: onPath };
|
|
10928
11092
|
const adjacent = npmAdjacentToNode(deps);
|
|
10929
11093
|
if (adjacent)
|
|
10930
|
-
return { command:
|
|
11094
|
+
return { command: join8(adjacent, name), binDir: adjacent };
|
|
10931
11095
|
for (const dir of wellKnownNpmDirs(deps)) {
|
|
10932
|
-
const candidate =
|
|
11096
|
+
const candidate = join8(dir, name);
|
|
10933
11097
|
if (isFile(candidate))
|
|
10934
11098
|
return { command: candidate, binDir: dir };
|
|
10935
11099
|
}
|
|
@@ -10946,7 +11110,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env) {
|
|
|
10946
11110
|
import { execFileSync } from "node:child_process";
|
|
10947
11111
|
import { createHash as createHash3 } from "node:crypto";
|
|
10948
11112
|
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync4, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
10949
|
-
import { basename as basename2, dirname as
|
|
11113
|
+
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join9, relative as relative2, resolve as resolve5, win32 } from "node:path";
|
|
10950
11114
|
import { Readable as Readable2 } from "node:stream";
|
|
10951
11115
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
10952
11116
|
var ORT_VERSION = "1.24.4";
|
|
@@ -11009,10 +11173,10 @@ function getManualInstallHint() {
|
|
|
11009
11173
|
}
|
|
11010
11174
|
async function ensureOnnxRuntime(storageDir) {
|
|
11011
11175
|
const info = getPlatformInfo();
|
|
11012
|
-
const ortVersionDir =
|
|
11176
|
+
const ortVersionDir = join9(storageDir, "onnxruntime", ORT_VERSION);
|
|
11013
11177
|
const libName = info?.libName ?? "libonnxruntime.dylib";
|
|
11014
11178
|
const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
|
|
11015
|
-
const libPath =
|
|
11179
|
+
const libPath = join9(resolvedOrtDir, libName);
|
|
11016
11180
|
if (existsSync6(libPath)) {
|
|
11017
11181
|
const meta = readOnnxInstalledMeta(ortVersionDir);
|
|
11018
11182
|
if (meta?.sha256) {
|
|
@@ -11042,9 +11206,9 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
11042
11206
|
warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
|
|
11043
11207
|
return null;
|
|
11044
11208
|
}
|
|
11045
|
-
const onnxBaseDir =
|
|
11209
|
+
const onnxBaseDir = join9(storageDir, "onnxruntime");
|
|
11046
11210
|
mkdirSync5(onnxBaseDir, { recursive: true });
|
|
11047
|
-
const lockPath =
|
|
11211
|
+
const lockPath = join9(onnxBaseDir, ONNX_LOCK_FILE);
|
|
11048
11212
|
cleanupAbandonedStagingDirs(onnxBaseDir);
|
|
11049
11213
|
if (!acquireLock(lockPath)) {
|
|
11050
11214
|
warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
|
|
@@ -11063,7 +11227,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
11063
11227
|
for (const entry of entries) {
|
|
11064
11228
|
if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
|
|
11065
11229
|
continue;
|
|
11066
|
-
const stagingDir =
|
|
11230
|
+
const stagingDir = join9(onnxBaseDir, entry);
|
|
11067
11231
|
const parts = entry.split(".");
|
|
11068
11232
|
const pidStr = parts[parts.length - 2];
|
|
11069
11233
|
const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
|
|
@@ -11100,7 +11264,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
11100
11264
|
}
|
|
11101
11265
|
function cleanupIncompleteTargetIfUnowned(ortDir) {
|
|
11102
11266
|
try {
|
|
11103
|
-
if (existsSync6(ortDir) && !existsSync6(
|
|
11267
|
+
if (existsSync6(ortDir) && !existsSync6(join9(ortDir, ONNX_INSTALLED_META_FILE))) {
|
|
11104
11268
|
log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
|
|
11105
11269
|
rmSync3(ortDir, { recursive: true, force: true });
|
|
11106
11270
|
}
|
|
@@ -11144,7 +11308,7 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
11144
11308
|
if (version)
|
|
11145
11309
|
return version;
|
|
11146
11310
|
}
|
|
11147
|
-
const base =
|
|
11311
|
+
const base = join9(libDir, libName);
|
|
11148
11312
|
if (existsSync6(base)) {
|
|
11149
11313
|
try {
|
|
11150
11314
|
const real = realpathSync(base);
|
|
@@ -11183,6 +11347,17 @@ function pathEntriesForPlatform() {
|
|
|
11183
11347
|
return isAbsolute4(entry) || win32.isAbsolute(entry);
|
|
11184
11348
|
});
|
|
11185
11349
|
}
|
|
11350
|
+
function isWindowsSystem32Directory(dir) {
|
|
11351
|
+
if (process.platform !== "win32")
|
|
11352
|
+
return false;
|
|
11353
|
+
const normalizedDir = win32.resolve(dir).replace(/[\\/]+$/, "").toLowerCase();
|
|
11354
|
+
const windowsRoots = [process.env.SystemRoot, process.env.windir, "C:\\Windows"];
|
|
11355
|
+
return windowsRoots.some((root) => {
|
|
11356
|
+
if (!root)
|
|
11357
|
+
return false;
|
|
11358
|
+
return normalizedDir === win32.resolve(root, "System32").replace(/[\\/]+$/, "").toLowerCase();
|
|
11359
|
+
});
|
|
11360
|
+
}
|
|
11186
11361
|
function directoryContainsLibrary(dir, libName) {
|
|
11187
11362
|
try {
|
|
11188
11363
|
const entries = readdirSync2(dir);
|
|
@@ -11196,10 +11371,10 @@ function directoryContainsLibrary(dir, libName) {
|
|
|
11196
11371
|
}
|
|
11197
11372
|
}
|
|
11198
11373
|
function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
|
|
11199
|
-
if (existsSync6(
|
|
11374
|
+
if (existsSync6(join9(ortVersionDir, libName)))
|
|
11200
11375
|
return ortVersionDir;
|
|
11201
|
-
const libSubdir =
|
|
11202
|
-
if (existsSync6(
|
|
11376
|
+
const libSubdir = join9(ortVersionDir, "lib");
|
|
11377
|
+
if (existsSync6(join9(libSubdir, libName)))
|
|
11203
11378
|
return libSubdir;
|
|
11204
11379
|
return ortVersionDir;
|
|
11205
11380
|
}
|
|
@@ -11214,12 +11389,12 @@ function findSystemOnnxRuntime(libName) {
|
|
|
11214
11389
|
} else if (process.platform === "win32") {
|
|
11215
11390
|
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
|
|
11216
11391
|
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
11217
|
-
searchPaths.push(
|
|
11392
|
+
searchPaths.push(join9(programFiles, "onnxruntime", "lib"), join9(programFiles, "Microsoft ONNX Runtime", "lib"), join9(programFiles, "Microsoft Machine Learning", "lib"), join9(programFilesX86, "onnxruntime", "lib"), ...(() => {
|
|
11218
11393
|
const nugetPaths = [];
|
|
11219
11394
|
const userProfile = process.env.USERPROFILE ?? "";
|
|
11220
11395
|
if (!userProfile)
|
|
11221
11396
|
return nugetPaths;
|
|
11222
|
-
const nugetPackageDir =
|
|
11397
|
+
const nugetPackageDir = join9(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
|
|
11223
11398
|
if (!existsSync6(nugetPackageDir))
|
|
11224
11399
|
return nugetPaths;
|
|
11225
11400
|
try {
|
|
@@ -11228,7 +11403,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
11228
11403
|
continue;
|
|
11229
11404
|
if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
|
|
11230
11405
|
continue;
|
|
11231
|
-
nugetPaths.push(
|
|
11406
|
+
nugetPaths.push(join9(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join9(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
|
|
11232
11407
|
}
|
|
11233
11408
|
} catch (err) {
|
|
11234
11409
|
warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -11250,7 +11425,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
11250
11425
|
});
|
|
11251
11426
|
const unknownVersionPaths = [];
|
|
11252
11427
|
for (const dir of uniquePaths) {
|
|
11253
|
-
const libPath =
|
|
11428
|
+
const libPath = join9(dir, libName);
|
|
11254
11429
|
if (process.platform === "win32") {
|
|
11255
11430
|
if (!directoryContainsLibrary(dir, libName))
|
|
11256
11431
|
continue;
|
|
@@ -11259,6 +11434,10 @@ function findSystemOnnxRuntime(libName) {
|
|
|
11259
11434
|
}
|
|
11260
11435
|
const version = detectOnnxVersion(dir, libName);
|
|
11261
11436
|
if (!version) {
|
|
11437
|
+
if (isWindowsSystem32Directory(dir)) {
|
|
11438
|
+
warn(`Skipping unversioned Windows system ONNX Runtime at ${dir}; falling through to AFT-managed download.`);
|
|
11439
|
+
continue;
|
|
11440
|
+
}
|
|
11262
11441
|
unknownVersionPaths.push(dir);
|
|
11263
11442
|
continue;
|
|
11264
11443
|
}
|
|
@@ -11286,7 +11465,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
11286
11465
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
11287
11466
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
11288
11467
|
}
|
|
11289
|
-
mkdirSync5(
|
|
11468
|
+
mkdirSync5(dirname5(destPath), { recursive: true });
|
|
11290
11469
|
let bytesWritten = 0;
|
|
11291
11470
|
const guard = new TransformStream({
|
|
11292
11471
|
transform(chunk, transformController) {
|
|
@@ -11316,11 +11495,11 @@ function validateExtractedTree(stagingRoot) {
|
|
|
11316
11495
|
const walk = (dir) => {
|
|
11317
11496
|
const entries = readdirSync2(dir);
|
|
11318
11497
|
for (const entry of entries) {
|
|
11319
|
-
const fullPath =
|
|
11498
|
+
const fullPath = join9(dir, entry);
|
|
11320
11499
|
const lst = lstatSync(fullPath);
|
|
11321
11500
|
if (lst.isSymbolicLink()) {
|
|
11322
11501
|
const linkTarget = readlinkSync(fullPath);
|
|
11323
|
-
const resolvedTarget = resolve5(
|
|
11502
|
+
const resolvedTarget = resolve5(dirname5(fullPath), linkTarget);
|
|
11324
11503
|
const rel2 = relative2(realRoot, resolvedTarget);
|
|
11325
11504
|
if (rel2.startsWith("..") || isAbsolute4(rel2) || win32.isAbsolute(rel2)) {
|
|
11326
11505
|
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
@@ -11351,7 +11530,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
11351
11530
|
const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
|
|
11352
11531
|
try {
|
|
11353
11532
|
mkdirSync5(tmpDir, { recursive: true });
|
|
11354
|
-
const archivePath =
|
|
11533
|
+
const archivePath = join9(tmpDir, `onnxruntime.${info.archiveType}`);
|
|
11355
11534
|
await downloadFileWithCap(url, archivePath);
|
|
11356
11535
|
const archiveSha256 = sha256File(archivePath);
|
|
11357
11536
|
log(`ONNX Runtime archive sha256=${archiveSha256}`);
|
|
@@ -11367,7 +11546,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
11367
11546
|
unlinkSync4(archivePath);
|
|
11368
11547
|
} catch {}
|
|
11369
11548
|
validateExtractedTree(tmpDir);
|
|
11370
|
-
const extractedDir =
|
|
11549
|
+
const extractedDir = join9(tmpDir, info.assetName, "lib");
|
|
11371
11550
|
if (!existsSync6(extractedDir)) {
|
|
11372
11551
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
11373
11552
|
}
|
|
@@ -11376,11 +11555,11 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
11376
11555
|
const realFiles = [];
|
|
11377
11556
|
const symlinks = [];
|
|
11378
11557
|
for (const libFile of libFiles) {
|
|
11379
|
-
const src =
|
|
11558
|
+
const src = join9(extractedDir, libFile);
|
|
11380
11559
|
try {
|
|
11381
|
-
const
|
|
11382
|
-
log(`ORT extract: ${libFile} — isSymlink=${
|
|
11383
|
-
if (
|
|
11560
|
+
const stat2 = lstatSync(src);
|
|
11561
|
+
log(`ORT extract: ${libFile} — isSymlink=${stat2.isSymbolicLink()}, isFile=${stat2.isFile()}, size=${stat2.size}`);
|
|
11562
|
+
if (stat2.isSymbolicLink()) {
|
|
11384
11563
|
symlinks.push({ name: libFile, target: readlinkSync(src) });
|
|
11385
11564
|
} else {
|
|
11386
11565
|
realFiles.push(libFile);
|
|
@@ -11391,7 +11570,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
11391
11570
|
}
|
|
11392
11571
|
}
|
|
11393
11572
|
copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
|
|
11394
|
-
const libPath =
|
|
11573
|
+
const libPath = join9(targetDir, info.libName);
|
|
11395
11574
|
let libHash = null;
|
|
11396
11575
|
try {
|
|
11397
11576
|
libHash = sha256File(libPath);
|
|
@@ -11416,8 +11595,8 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
11416
11595
|
function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
|
|
11417
11596
|
const requiredLibs = new Set([info.libName]);
|
|
11418
11597
|
for (const libFile of realFiles) {
|
|
11419
|
-
const src =
|
|
11420
|
-
const dst =
|
|
11598
|
+
const src = join9(extractedDir, libFile);
|
|
11599
|
+
const dst = join9(targetDir, libFile);
|
|
11421
11600
|
try {
|
|
11422
11601
|
copyFile(src, dst);
|
|
11423
11602
|
if (process.platform !== "win32") {
|
|
@@ -11433,12 +11612,12 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
11433
11612
|
}
|
|
11434
11613
|
const targetRoot = realpathSync(targetDir);
|
|
11435
11614
|
for (const link of symlinks) {
|
|
11436
|
-
const dst =
|
|
11615
|
+
const dst = join9(targetDir, link.name);
|
|
11437
11616
|
try {
|
|
11438
11617
|
unlinkSync4(dst);
|
|
11439
11618
|
} catch {}
|
|
11440
|
-
const dstForContainment =
|
|
11441
|
-
const resolvedTarget = resolve5(
|
|
11619
|
+
const dstForContainment = join9(targetRoot, link.name);
|
|
11620
|
+
const resolvedTarget = resolve5(dirname5(dstForContainment), link.target);
|
|
11442
11621
|
if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
|
|
11443
11622
|
const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
|
|
11444
11623
|
if (requiredLibs.has(link.name)) {
|
|
@@ -11458,7 +11637,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
11458
11637
|
log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
|
|
11459
11638
|
}
|
|
11460
11639
|
}
|
|
11461
|
-
const requiredPath =
|
|
11640
|
+
const requiredPath = join9(targetDir, info.libName);
|
|
11462
11641
|
if (!existsSync6(requiredPath)) {
|
|
11463
11642
|
rmSync3(targetDir, { recursive: true, force: true });
|
|
11464
11643
|
throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
|
|
@@ -11485,13 +11664,13 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
11485
11664
|
...sha256 ? { sha256 } : {},
|
|
11486
11665
|
archiveSha256
|
|
11487
11666
|
};
|
|
11488
|
-
writeFileSync3(
|
|
11667
|
+
writeFileSync3(join9(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
|
|
11489
11668
|
} catch (err) {
|
|
11490
11669
|
log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
|
|
11491
11670
|
}
|
|
11492
11671
|
}
|
|
11493
11672
|
function readOnnxInstalledMeta(installDir) {
|
|
11494
|
-
const path2 =
|
|
11673
|
+
const path2 = join9(installDir, ONNX_INSTALLED_META_FILE);
|
|
11495
11674
|
try {
|
|
11496
11675
|
if (!statSync4(path2).isFile())
|
|
11497
11676
|
return null;
|
|
@@ -11628,7 +11807,7 @@ function isProcessAlive(pid) {
|
|
|
11628
11807
|
}
|
|
11629
11808
|
}
|
|
11630
11809
|
// ../aft-bridge/dist/pool.js
|
|
11631
|
-
import { homedir as
|
|
11810
|
+
import { homedir as homedir10 } from "node:os";
|
|
11632
11811
|
|
|
11633
11812
|
// ../aft-bridge/dist/project-identity.js
|
|
11634
11813
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
@@ -11667,7 +11846,7 @@ var DEFAULT_MAX_POOL_SIZE = 8;
|
|
|
11667
11846
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
11668
11847
|
function canonicalHomeDir() {
|
|
11669
11848
|
try {
|
|
11670
|
-
const home =
|
|
11849
|
+
const home = homedir10();
|
|
11671
11850
|
if (!home)
|
|
11672
11851
|
return null;
|
|
11673
11852
|
return canonicalizeProjectRoot(home);
|
|
@@ -11693,6 +11872,7 @@ class BridgePool {
|
|
|
11693
11872
|
projectConfigLoader;
|
|
11694
11873
|
logger;
|
|
11695
11874
|
cleanupTimer = null;
|
|
11875
|
+
shutdownCalled = false;
|
|
11696
11876
|
constructor(binaryPath, options = {}, configOverrides = {}) {
|
|
11697
11877
|
this.binaryPath = binaryPath;
|
|
11698
11878
|
this.maxPoolSize = options.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE;
|
|
@@ -11714,12 +11894,11 @@ class BridgePool {
|
|
|
11714
11894
|
childEnv: options.childEnv
|
|
11715
11895
|
};
|
|
11716
11896
|
this.configOverrides = configOverrides;
|
|
11717
|
-
|
|
11718
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
|
|
11719
|
-
this.cleanupTimer.unref();
|
|
11720
|
-
}
|
|
11897
|
+
this.startCleanupTimer();
|
|
11721
11898
|
}
|
|
11722
11899
|
getActiveBridgeForRoot(projectRoot) {
|
|
11900
|
+
if (this.shutdownCalled)
|
|
11901
|
+
return null;
|
|
11723
11902
|
const key = normalizeKey(projectRoot);
|
|
11724
11903
|
const entry = this.bridges.get(key);
|
|
11725
11904
|
if (!entry?.bridge.isAlive())
|
|
@@ -11727,7 +11906,21 @@ class BridgePool {
|
|
|
11727
11906
|
entry.lastUsed = Date.now();
|
|
11728
11907
|
return entry.bridge;
|
|
11729
11908
|
}
|
|
11909
|
+
activeBridges() {
|
|
11910
|
+
if (this.shutdownCalled)
|
|
11911
|
+
return [];
|
|
11912
|
+
const alive = [];
|
|
11913
|
+
for (const entry of this.bridges.values()) {
|
|
11914
|
+
if (entry.bridge.isAlive())
|
|
11915
|
+
alive.push(entry.bridge);
|
|
11916
|
+
}
|
|
11917
|
+
return alive;
|
|
11918
|
+
}
|
|
11730
11919
|
getBridge(projectRoot) {
|
|
11920
|
+
if (this.shutdownCalled) {
|
|
11921
|
+
this.shutdownCalled = false;
|
|
11922
|
+
this.startCleanupTimer();
|
|
11923
|
+
}
|
|
11731
11924
|
const key = normalizeKey(projectRoot);
|
|
11732
11925
|
const existing = this.bridges.get(key);
|
|
11733
11926
|
if (existing) {
|
|
@@ -11737,15 +11930,7 @@ class BridgePool {
|
|
|
11737
11930
|
if (this.bridges.size >= this.maxPoolSize) {
|
|
11738
11931
|
this.evictLRU();
|
|
11739
11932
|
}
|
|
11740
|
-
|
|
11741
|
-
if (this.projectConfigLoader) {
|
|
11742
|
-
try {
|
|
11743
|
-
projectOverrides = this.projectConfigLoader(key) ?? {};
|
|
11744
|
-
} catch (err) {
|
|
11745
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
11746
|
-
this.error(`projectConfigLoader failed; using global overrides only: ${message}`);
|
|
11747
|
-
}
|
|
11748
|
-
}
|
|
11933
|
+
const projectOverrides = this.loadProjectOverrides(key);
|
|
11749
11934
|
const mergedOverrides = { ...this.configOverrides, ...projectOverrides };
|
|
11750
11935
|
const bridge = new BinaryBridge(this.binaryPath, key, this.bridgeOptions, mergedOverrides);
|
|
11751
11936
|
this.bridges.set(key, { bridge, lastUsed: Date.now() });
|
|
@@ -11795,6 +11980,7 @@ class BridgePool {
|
|
|
11795
11980
|
}
|
|
11796
11981
|
async closeSession(_projectRoot, _session) {}
|
|
11797
11982
|
async shutdown() {
|
|
11983
|
+
this.shutdownCalled = true;
|
|
11798
11984
|
if (this.cleanupTimer) {
|
|
11799
11985
|
clearInterval(this.cleanupTimer);
|
|
11800
11986
|
this.cleanupTimer = null;
|
|
@@ -11807,6 +11993,9 @@ class BridgePool {
|
|
|
11807
11993
|
this.staleBridges.clear();
|
|
11808
11994
|
await Promise.allSettled(shutdowns);
|
|
11809
11995
|
}
|
|
11996
|
+
isShutdown() {
|
|
11997
|
+
return this.shutdownCalled;
|
|
11998
|
+
}
|
|
11810
11999
|
async replaceBinary(newPath) {
|
|
11811
12000
|
this.binaryPath = newPath;
|
|
11812
12001
|
for (const entry of this.bridges.values()) {
|
|
@@ -11816,6 +12005,12 @@ class BridgePool {
|
|
|
11816
12005
|
this.log(`Binary path updated to ${newPath}. Active bridges marked stale — next calls will use the new binary.`);
|
|
11817
12006
|
return newPath;
|
|
11818
12007
|
}
|
|
12008
|
+
startCleanupTimer() {
|
|
12009
|
+
if (!Number.isFinite(this.idleTimeoutMs) || this.cleanupTimer)
|
|
12010
|
+
return;
|
|
12011
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
|
|
12012
|
+
this.cleanupTimer.unref();
|
|
12013
|
+
}
|
|
11819
12014
|
log(message, meta) {
|
|
11820
12015
|
const logger = this.logger ?? getActiveLogger();
|
|
11821
12016
|
if (logger) {
|
|
@@ -11847,6 +12042,36 @@ class BridgePool {
|
|
|
11847
12042
|
this.configOverrides[key] = value;
|
|
11848
12043
|
}
|
|
11849
12044
|
}
|
|
12045
|
+
async reconfigure(projectRoot, overrides) {
|
|
12046
|
+
const key = normalizeKey(projectRoot);
|
|
12047
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
12048
|
+
this.setConfigureOverride(name, value);
|
|
12049
|
+
}
|
|
12050
|
+
const bridge = this.getActiveBridgeForRoot(key);
|
|
12051
|
+
if (!bridge)
|
|
12052
|
+
return;
|
|
12053
|
+
const projectOverrides = this.loadProjectOverrides(key);
|
|
12054
|
+
const response = await bridge.send("configure", {
|
|
12055
|
+
project_root: key,
|
|
12056
|
+
...this.configOverrides,
|
|
12057
|
+
...projectOverrides,
|
|
12058
|
+
...overrides
|
|
12059
|
+
});
|
|
12060
|
+
if (response.success === false) {
|
|
12061
|
+
throw new Error(`configure refresh failed for ${key}: ${String(response.message ?? "unknown error")}`);
|
|
12062
|
+
}
|
|
12063
|
+
}
|
|
12064
|
+
loadProjectOverrides(key) {
|
|
12065
|
+
if (!this.projectConfigLoader)
|
|
12066
|
+
return {};
|
|
12067
|
+
try {
|
|
12068
|
+
return this.projectConfigLoader(key) ?? {};
|
|
12069
|
+
} catch (err) {
|
|
12070
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12071
|
+
this.error(`projectConfigLoader failed; using global overrides only: ${message}`);
|
|
12072
|
+
return {};
|
|
12073
|
+
}
|
|
12074
|
+
}
|
|
11850
12075
|
get size() {
|
|
11851
12076
|
return this.bridges.size;
|
|
11852
12077
|
}
|
|
@@ -11860,11 +12085,185 @@ class BridgePool {
|
|
|
11860
12085
|
function normalizeKey(projectRoot) {
|
|
11861
12086
|
return canonicalizeProjectRoot(projectRoot);
|
|
11862
12087
|
}
|
|
11863
|
-
//
|
|
12088
|
+
// ../aft-bridge/dist/revivable-transport.js
|
|
12089
|
+
class RevivableTransportPool {
|
|
12090
|
+
createPool;
|
|
12091
|
+
onBinaryReplaced;
|
|
12092
|
+
activePool;
|
|
12093
|
+
revival = null;
|
|
12094
|
+
transports = new Map;
|
|
12095
|
+
configureOverrides = new Map;
|
|
12096
|
+
constructor(initialPool, createPool, onBinaryReplaced) {
|
|
12097
|
+
this.createPool = createPool;
|
|
12098
|
+
this.onBinaryReplaced = onBinaryReplaced;
|
|
12099
|
+
this.activePool = initialPool;
|
|
12100
|
+
}
|
|
12101
|
+
getBridge(projectRoot) {
|
|
12102
|
+
const key = canonicalizeProjectRoot(projectRoot);
|
|
12103
|
+
let transport = this.transports.get(key);
|
|
12104
|
+
if (!transport) {
|
|
12105
|
+
transport = new RevivableProjectTransport(this, key);
|
|
12106
|
+
this.transports.set(key, transport);
|
|
12107
|
+
}
|
|
12108
|
+
return transport;
|
|
12109
|
+
}
|
|
12110
|
+
activeBridges() {
|
|
12111
|
+
return this.activePool.activeBridges();
|
|
12112
|
+
}
|
|
12113
|
+
getActiveBridgeForRoot(projectRoot) {
|
|
12114
|
+
const key = canonicalizeProjectRoot(projectRoot);
|
|
12115
|
+
const bridge = this.currentBridge(key);
|
|
12116
|
+
if (!bridge)
|
|
12117
|
+
return null;
|
|
12118
|
+
const transport = this.getBridge(key);
|
|
12119
|
+
transport.refreshStatusSubscription(bridge);
|
|
12120
|
+
return transport;
|
|
12121
|
+
}
|
|
12122
|
+
async toolCall(projectRoot, runtime, name, rawArgs = {}, options) {
|
|
12123
|
+
return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
|
|
12124
|
+
}
|
|
12125
|
+
setConfigureOverride(key, value) {
|
|
12126
|
+
if (value === undefined)
|
|
12127
|
+
this.configureOverrides.delete(key);
|
|
12128
|
+
else
|
|
12129
|
+
this.configureOverrides.set(key, value);
|
|
12130
|
+
this.activePool.setConfigureOverride(key, value);
|
|
12131
|
+
}
|
|
12132
|
+
async reconfigure(projectRoot, overrides) {
|
|
12133
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
12134
|
+
if (value === undefined)
|
|
12135
|
+
this.configureOverrides.delete(key);
|
|
12136
|
+
else
|
|
12137
|
+
this.configureOverrides.set(key, value);
|
|
12138
|
+
}
|
|
12139
|
+
const pool = await this.ensureActivePool();
|
|
12140
|
+
await pool.reconfigure(projectRoot, overrides);
|
|
12141
|
+
}
|
|
12142
|
+
async replaceBinary(path2) {
|
|
12143
|
+
const replaced = await this.activePool.replaceBinary(path2);
|
|
12144
|
+
this.onBinaryReplaced?.(replaced);
|
|
12145
|
+
return replaced;
|
|
12146
|
+
}
|
|
12147
|
+
closeSession(projectRoot, session) {
|
|
12148
|
+
return this.activePool.closeSession(projectRoot, session);
|
|
12149
|
+
}
|
|
12150
|
+
async shutdown() {
|
|
12151
|
+
const revival = this.revival;
|
|
12152
|
+
if (revival) {
|
|
12153
|
+
await Promise.allSettled([revival]);
|
|
12154
|
+
}
|
|
12155
|
+
await this.activePool.shutdown();
|
|
12156
|
+
for (const transport of this.transports.values()) {
|
|
12157
|
+
transport.refreshStatusSubscription(null);
|
|
12158
|
+
}
|
|
12159
|
+
}
|
|
12160
|
+
isShutdown() {
|
|
12161
|
+
return this.activePool.isShutdown();
|
|
12162
|
+
}
|
|
12163
|
+
async ensureActivePool() {
|
|
12164
|
+
if (!this.activePool.isShutdown())
|
|
12165
|
+
return this.activePool;
|
|
12166
|
+
if (this.revival)
|
|
12167
|
+
return this.revival;
|
|
12168
|
+
warn("transport was shut down but new demand arrived — reviving (host quit hook fired without process exit?)");
|
|
12169
|
+
const revival = this.createPool().then((pool) => {
|
|
12170
|
+
for (const [key, value] of this.configureOverrides) {
|
|
12171
|
+
pool.setConfigureOverride(key, value);
|
|
12172
|
+
}
|
|
12173
|
+
this.activePool = pool;
|
|
12174
|
+
for (const [root, transport] of this.transports) {
|
|
12175
|
+
transport.refreshStatusSubscription(pool.getActiveBridgeForRoot(root));
|
|
12176
|
+
}
|
|
12177
|
+
return pool;
|
|
12178
|
+
});
|
|
12179
|
+
this.revival = revival;
|
|
12180
|
+
revival.then(() => {
|
|
12181
|
+
if (this.revival === revival)
|
|
12182
|
+
this.revival = null;
|
|
12183
|
+
}, () => {
|
|
12184
|
+
if (this.revival === revival)
|
|
12185
|
+
this.revival = null;
|
|
12186
|
+
});
|
|
12187
|
+
return revival;
|
|
12188
|
+
}
|
|
12189
|
+
currentBridge(projectRoot) {
|
|
12190
|
+
return this.activePool.getActiveBridgeForRoot(projectRoot);
|
|
12191
|
+
}
|
|
12192
|
+
async send(projectRoot, command, params, options) {
|
|
12193
|
+
const pool = await this.ensureActivePool();
|
|
12194
|
+
const bridge = pool.getBridge(projectRoot);
|
|
12195
|
+
this.getBridge(projectRoot).refreshStatusSubscription(bridge);
|
|
12196
|
+
return bridge.send(command, params, options);
|
|
12197
|
+
}
|
|
12198
|
+
async toolCallOnProject(projectRoot, sessionId, name, rawArgs, options) {
|
|
12199
|
+
const pool = await this.ensureActivePool();
|
|
12200
|
+
const bridge = pool.getBridge(projectRoot);
|
|
12201
|
+
this.getBridge(projectRoot).refreshStatusSubscription(bridge);
|
|
12202
|
+
return bridge.toolCall(sessionId, name, rawArgs, options);
|
|
12203
|
+
}
|
|
12204
|
+
}
|
|
12205
|
+
|
|
12206
|
+
class RevivableProjectTransport {
|
|
12207
|
+
owner;
|
|
12208
|
+
projectRoot;
|
|
12209
|
+
statusListeners = new Map;
|
|
12210
|
+
statusBridges = new Map;
|
|
12211
|
+
constructor(owner, projectRoot) {
|
|
12212
|
+
this.owner = owner;
|
|
12213
|
+
this.projectRoot = projectRoot;
|
|
12214
|
+
}
|
|
12215
|
+
getCwd() {
|
|
12216
|
+
return this.projectRoot;
|
|
12217
|
+
}
|
|
12218
|
+
getStatusBar() {
|
|
12219
|
+
return this.owner.currentBridge(this.projectRoot)?.getStatusBar();
|
|
12220
|
+
}
|
|
12221
|
+
getCachedStatus() {
|
|
12222
|
+
return this.owner.currentBridge(this.projectRoot)?.getCachedStatus() ?? null;
|
|
12223
|
+
}
|
|
12224
|
+
cacheStatusSnapshot(snapshot) {
|
|
12225
|
+
this.owner.currentBridge(this.projectRoot)?.cacheStatusSnapshot(snapshot);
|
|
12226
|
+
}
|
|
12227
|
+
send(command, params, options) {
|
|
12228
|
+
return this.owner.send(this.projectRoot, command, params, options);
|
|
12229
|
+
}
|
|
12230
|
+
toolCall(sessionId, name, rawArgs, options) {
|
|
12231
|
+
return this.owner.toolCallOnProject(this.projectRoot, sessionId, name, rawArgs, options);
|
|
12232
|
+
}
|
|
12233
|
+
subscribeStatus(listener) {
|
|
12234
|
+
if (this.statusListeners.has(listener))
|
|
12235
|
+
return () => this.removeStatusListener(listener);
|
|
12236
|
+
this.statusListeners.set(listener, () => {});
|
|
12237
|
+
this.bindStatusListener(listener, this.owner.currentBridge(this.projectRoot));
|
|
12238
|
+
return () => this.removeStatusListener(listener);
|
|
12239
|
+
}
|
|
12240
|
+
refreshStatusSubscription(bridge) {
|
|
12241
|
+
for (const listener of this.statusListeners.keys()) {
|
|
12242
|
+
this.bindStatusListener(listener, bridge);
|
|
12243
|
+
}
|
|
12244
|
+
}
|
|
12245
|
+
bindStatusListener(listener, bridge) {
|
|
12246
|
+
if (this.statusBridges.get(listener) === bridge)
|
|
12247
|
+
return;
|
|
12248
|
+
const previousUnsubscribe = this.statusListeners.get(listener);
|
|
12249
|
+
previousUnsubscribe?.();
|
|
12250
|
+
const maybe = bridge;
|
|
12251
|
+
const unsubscribe = maybe && typeof maybe.subscribeStatus === "function" ? maybe.subscribeStatus(listener) : () => {};
|
|
12252
|
+
this.statusListeners.set(listener, unsubscribe);
|
|
12253
|
+
this.statusBridges.set(listener, bridge);
|
|
12254
|
+
}
|
|
12255
|
+
removeStatusListener(listener) {
|
|
12256
|
+
const unsubscribe = this.statusListeners.get(listener);
|
|
12257
|
+
this.statusListeners.delete(listener);
|
|
12258
|
+
this.statusBridges.delete(listener);
|
|
12259
|
+
unsubscribe?.();
|
|
12260
|
+
}
|
|
12261
|
+
}
|
|
12262
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/client.js
|
|
11864
12263
|
import { promises as fs2 } from "node:fs";
|
|
11865
12264
|
import { debuglog } from "node:util";
|
|
11866
12265
|
|
|
11867
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12266
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
11868
12267
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
11869
12268
|
var NONCE_LEN = 32;
|
|
11870
12269
|
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
@@ -11928,8 +12327,146 @@ async function authenticateClient(sock, conn, deadlineMs) {
|
|
|
11928
12327
|
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
11929
12328
|
}
|
|
11930
12329
|
|
|
11931
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12330
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
11932
12331
|
import { promises as fs } from "node:fs";
|
|
12332
|
+
|
|
12333
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
12334
|
+
var PROTOCOL_VERSION = 2;
|
|
12335
|
+
var HEADER_LEN = 21;
|
|
12336
|
+
var FROZEN_PREFIX_LEN = 5;
|
|
12337
|
+
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
12338
|
+
var FrameType;
|
|
12339
|
+
(function(FrameType2) {
|
|
12340
|
+
FrameType2[FrameType2["Request"] = 0] = "Request";
|
|
12341
|
+
FrameType2[FrameType2["Response"] = 1] = "Response";
|
|
12342
|
+
FrameType2[FrameType2["Push"] = 2] = "Push";
|
|
12343
|
+
FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
|
|
12344
|
+
FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
|
|
12345
|
+
FrameType2[FrameType2["Error"] = 5] = "Error";
|
|
12346
|
+
FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
|
|
12347
|
+
FrameType2[FrameType2["Ping"] = 7] = "Ping";
|
|
12348
|
+
FrameType2[FrameType2["Pong"] = 8] = "Pong";
|
|
12349
|
+
FrameType2[FrameType2["Hello"] = 9] = "Hello";
|
|
12350
|
+
FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
|
|
12351
|
+
FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
|
|
12352
|
+
})(FrameType || (FrameType = {}));
|
|
12353
|
+
var FRAME_TYPE_MAX = FrameType.Goodbye;
|
|
12354
|
+
function isPureHeader(ty) {
|
|
12355
|
+
return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
|
|
12356
|
+
}
|
|
12357
|
+
var Priority;
|
|
12358
|
+
(function(Priority2) {
|
|
12359
|
+
Priority2[Priority2["Passive"] = 0] = "Passive";
|
|
12360
|
+
Priority2[Priority2["Interactive"] = 1] = "Interactive";
|
|
12361
|
+
Priority2[Priority2["Background"] = 2] = "Background";
|
|
12362
|
+
})(Priority || (Priority = {}));
|
|
12363
|
+
var AdmissionClass;
|
|
12364
|
+
(function(AdmissionClass2) {
|
|
12365
|
+
AdmissionClass2[AdmissionClass2["Normal"] = 0] = "Normal";
|
|
12366
|
+
AdmissionClass2[AdmissionClass2["Expedite"] = 1] = "Expedite";
|
|
12367
|
+
AdmissionClass2[AdmissionClass2["Sheddable"] = 2] = "Sheddable";
|
|
12368
|
+
})(AdmissionClass || (AdmissionClass = {}));
|
|
12369
|
+
var FLAG_BINARY = 1;
|
|
12370
|
+
var FLAG_PRIORITY_MASK = 6;
|
|
12371
|
+
var FLAG_PRIORITY_SHIFT = 1;
|
|
12372
|
+
var FLAG_LAST = 8;
|
|
12373
|
+
var FLAG_ADMISSION_MASK = 48;
|
|
12374
|
+
var FLAG_ADMISSION_SHIFT = 4;
|
|
12375
|
+
var FLAG_RESERVED_MASK = 192;
|
|
12376
|
+
function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
|
|
12377
|
+
let flags = 0;
|
|
12378
|
+
if (binary)
|
|
12379
|
+
flags |= FLAG_BINARY;
|
|
12380
|
+
flags |= priority << FLAG_PRIORITY_SHIFT;
|
|
12381
|
+
if (last)
|
|
12382
|
+
flags |= FLAG_LAST;
|
|
12383
|
+
flags |= admissionClass << FLAG_ADMISSION_SHIFT;
|
|
12384
|
+
return flags;
|
|
12385
|
+
}
|
|
12386
|
+
function encodeHeader(header) {
|
|
12387
|
+
const buffer = new Uint8Array(HEADER_LEN);
|
|
12388
|
+
const view = new DataView(buffer.buffer);
|
|
12389
|
+
view.setUint32(0, header.len, true);
|
|
12390
|
+
buffer[4] = header.ver;
|
|
12391
|
+
buffer[5] = header.ty;
|
|
12392
|
+
buffer[6] = header.flags;
|
|
12393
|
+
view.setUint16(7, header.channel, true);
|
|
12394
|
+
view.setUint32(9, header.epoch, true);
|
|
12395
|
+
view.setBigUint64(13, header.corr, true);
|
|
12396
|
+
return buffer;
|
|
12397
|
+
}
|
|
12398
|
+
|
|
12399
|
+
class DecodeError extends Error {
|
|
12400
|
+
code;
|
|
12401
|
+
constructor(message, code) {
|
|
12402
|
+
super(message);
|
|
12403
|
+
this.code = code;
|
|
12404
|
+
this.name = "DecodeError";
|
|
12405
|
+
}
|
|
12406
|
+
}
|
|
12407
|
+
function decodeHeader(bytes) {
|
|
12408
|
+
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
12409
|
+
throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`, "too_short_for_prefix");
|
|
12410
|
+
}
|
|
12411
|
+
const ver = bytes[4];
|
|
12412
|
+
if (ver !== PROTOCOL_VERSION)
|
|
12413
|
+
throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
|
|
12414
|
+
if (bytes.length < HEADER_LEN) {
|
|
12415
|
+
throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`, "too_short_for_header");
|
|
12416
|
+
}
|
|
12417
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
12418
|
+
const len = view.getUint32(0, true);
|
|
12419
|
+
const typeByte = bytes[5];
|
|
12420
|
+
if (typeByte > FRAME_TYPE_MAX)
|
|
12421
|
+
throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
|
|
12422
|
+
const ty = typeByte;
|
|
12423
|
+
const flags = bytes[6];
|
|
12424
|
+
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
12425
|
+
throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_flag_bits");
|
|
12426
|
+
}
|
|
12427
|
+
if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
|
|
12428
|
+
throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_priority_bits");
|
|
12429
|
+
}
|
|
12430
|
+
const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
|
|
12431
|
+
if (admission === 3) {
|
|
12432
|
+
throw new DecodeError(`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_admission_class");
|
|
12433
|
+
}
|
|
12434
|
+
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
12435
|
+
throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
|
|
12436
|
+
}
|
|
12437
|
+
const channel = view.getUint16(7, true);
|
|
12438
|
+
const epoch = view.getUint32(9, true);
|
|
12439
|
+
if (channel === 0 && epoch !== 0) {
|
|
12440
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
12441
|
+
}
|
|
12442
|
+
if (isPureHeader(ty) && len !== 0) {
|
|
12443
|
+
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
12444
|
+
}
|
|
12445
|
+
return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
|
|
12446
|
+
}
|
|
12447
|
+
function buildFrame(ty, flags, channel, epoch, corr, body) {
|
|
12448
|
+
return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, epoch, corr, body);
|
|
12449
|
+
}
|
|
12450
|
+
function buildFrameWithVersion(ver, ty, flags, channel, epoch, corr, body) {
|
|
12451
|
+
if (body.length > MAX_FRAME_BODY_LEN) {
|
|
12452
|
+
throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
12453
|
+
}
|
|
12454
|
+
const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
|
|
12455
|
+
decodeHeader(encodeHeader(header));
|
|
12456
|
+
return { header, body };
|
|
12457
|
+
}
|
|
12458
|
+
function encodeFrame(frame) {
|
|
12459
|
+
if (frame.header.len !== frame.body.length) {
|
|
12460
|
+
throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
|
|
12461
|
+
}
|
|
12462
|
+
const header = encodeHeader(frame.header);
|
|
12463
|
+
const output = new Uint8Array(header.length + frame.body.length);
|
|
12464
|
+
output.set(header, 0);
|
|
12465
|
+
output.set(frame.body, header.length);
|
|
12466
|
+
return output;
|
|
12467
|
+
}
|
|
12468
|
+
|
|
12469
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
11933
12470
|
var SCHEMA_VERSION = 1;
|
|
11934
12471
|
var MIN_KEY_LEN = 32;
|
|
11935
12472
|
var DAEMON_ID_LEN = 16;
|
|
@@ -11959,8 +12496,8 @@ function validate(info) {
|
|
|
11959
12496
|
async function verifyOwnerOnly(path2) {
|
|
11960
12497
|
if (process.platform === "win32")
|
|
11961
12498
|
return;
|
|
11962
|
-
const
|
|
11963
|
-
const mode =
|
|
12499
|
+
const stat2 = await fs.stat(path2);
|
|
12500
|
+
const mode = stat2.mode & 511;
|
|
11964
12501
|
if ((mode & 63) !== 0) {
|
|
11965
12502
|
throw new ConnectionFileError(`connection file ${path2} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
11966
12503
|
}
|
|
@@ -11974,6 +12511,10 @@ async function readConnectionFile(path2) {
|
|
|
11974
12511
|
} catch (err) {
|
|
11975
12512
|
throw new ConnectionFileError(`connection file JSON read failed for ${path2}: ${String(err)}`);
|
|
11976
12513
|
}
|
|
12514
|
+
const wireVersion = parsed.wire_version;
|
|
12515
|
+
if (wireVersion !== undefined && wireVersion !== PROTOCOL_VERSION) {
|
|
12516
|
+
throw new ConnectionFileError(`connection file wire_version ${String(wireVersion)} but this client speaks ${PROTOCOL_VERSION}; the client library must be upgraded`);
|
|
12517
|
+
}
|
|
11977
12518
|
const endpointsRaw = parsed.endpoints;
|
|
11978
12519
|
if (!Array.isArray(endpointsRaw)) {
|
|
11979
12520
|
throw new ConnectionFileError("connection file 'endpoints' must be an array");
|
|
@@ -11989,130 +12530,62 @@ async function readConnectionFile(path2) {
|
|
|
11989
12530
|
schema: parsed.schema,
|
|
11990
12531
|
endpoints,
|
|
11991
12532
|
key: toBytes(parsed.key, "key"),
|
|
11992
|
-
daemonId: toBytes(parsed.daemon_id, "daemon_id"),
|
|
11993
|
-
pid: parsed.pid,
|
|
11994
|
-
daemonVer: parsed.daemon_ver ?? ""
|
|
11995
|
-
};
|
|
11996
|
-
validate(info);
|
|
11997
|
-
return info;
|
|
11998
|
-
}
|
|
11999
|
-
|
|
12000
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.4/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
12001
|
-
var PROTOCOL_VERSION = 1;
|
|
12002
|
-
var HEADER_LEN = 17;
|
|
12003
|
-
var FROZEN_PREFIX_LEN = 5;
|
|
12004
|
-
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
12005
|
-
var FrameType;
|
|
12006
|
-
(function(FrameType2) {
|
|
12007
|
-
FrameType2[FrameType2["Request"] = 0] = "Request";
|
|
12008
|
-
FrameType2[FrameType2["Response"] = 1] = "Response";
|
|
12009
|
-
FrameType2[FrameType2["Push"] = 2] = "Push";
|
|
12010
|
-
FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
|
|
12011
|
-
FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
|
|
12012
|
-
FrameType2[FrameType2["Error"] = 5] = "Error";
|
|
12013
|
-
FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
|
|
12014
|
-
FrameType2[FrameType2["Ping"] = 7] = "Ping";
|
|
12015
|
-
FrameType2[FrameType2["Pong"] = 8] = "Pong";
|
|
12016
|
-
FrameType2[FrameType2["Hello"] = 9] = "Hello";
|
|
12017
|
-
FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
|
|
12018
|
-
FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
|
|
12019
|
-
})(FrameType || (FrameType = {}));
|
|
12020
|
-
var FRAME_TYPE_MAX = FrameType.Goodbye;
|
|
12021
|
-
function isPureHeader(ty) {
|
|
12022
|
-
return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
|
|
12023
|
-
}
|
|
12024
|
-
var Priority;
|
|
12025
|
-
(function(Priority2) {
|
|
12026
|
-
Priority2[Priority2["Passive"] = 0] = "Passive";
|
|
12027
|
-
Priority2[Priority2["Interactive"] = 1] = "Interactive";
|
|
12028
|
-
Priority2[Priority2["Background"] = 2] = "Background";
|
|
12029
|
-
})(Priority || (Priority = {}));
|
|
12030
|
-
var FLAG_BINARY = 1;
|
|
12031
|
-
var FLAG_PRIORITY_MASK = 6;
|
|
12032
|
-
var FLAG_PRIORITY_SHIFT = 1;
|
|
12033
|
-
var FLAG_LAST = 8;
|
|
12034
|
-
var FLAG_RESERVED_MASK = 240;
|
|
12035
|
-
function buildFlags(binary, priority, last) {
|
|
12036
|
-
let b = 0;
|
|
12037
|
-
if (binary)
|
|
12038
|
-
b |= FLAG_BINARY;
|
|
12039
|
-
b |= priority << FLAG_PRIORITY_SHIFT;
|
|
12040
|
-
if (last)
|
|
12041
|
-
b |= FLAG_LAST;
|
|
12042
|
-
return b;
|
|
12043
|
-
}
|
|
12044
|
-
function encodeHeader(h) {
|
|
12045
|
-
const buf = new Uint8Array(HEADER_LEN);
|
|
12046
|
-
const view = new DataView(buf.buffer);
|
|
12047
|
-
view.setUint32(0, h.len, true);
|
|
12048
|
-
buf[4] = h.ver;
|
|
12049
|
-
buf[5] = h.ty;
|
|
12050
|
-
buf[6] = h.flags;
|
|
12051
|
-
view.setUint16(7, h.channel, true);
|
|
12052
|
-
view.setBigUint64(9, h.corr, true);
|
|
12053
|
-
return buf;
|
|
12533
|
+
daemonId: toBytes(parsed.daemon_id, "daemon_id"),
|
|
12534
|
+
pid: parsed.pid,
|
|
12535
|
+
daemonVer: parsed.daemon_ver ?? ""
|
|
12536
|
+
};
|
|
12537
|
+
validate(info);
|
|
12538
|
+
return info;
|
|
12054
12539
|
}
|
|
12055
12540
|
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
throw new DecodeError(`unknown frame type byte ${tyByte}`);
|
|
12074
|
-
}
|
|
12075
|
-
const ty = tyByte;
|
|
12076
|
-
const flags = bytes[6];
|
|
12077
|
-
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
12078
|
-
throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2)}`);
|
|
12079
|
-
}
|
|
12080
|
-
if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
|
|
12081
|
-
throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2)}`);
|
|
12541
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
12542
|
+
var connectionToken = new WeakMap;
|
|
12543
|
+
|
|
12544
|
+
class RouteHandle {
|
|
12545
|
+
channel;
|
|
12546
|
+
epoch;
|
|
12547
|
+
constructor(channel, epoch, token) {
|
|
12548
|
+
if (!Number.isInteger(channel) || channel <= 0 || channel > 65535) {
|
|
12549
|
+
throw new RangeError(`route channel must be an integer in 1..65535, got ${channel}`);
|
|
12550
|
+
}
|
|
12551
|
+
if (!Number.isInteger(epoch) || epoch <= 0 || epoch > 4294967295) {
|
|
12552
|
+
throw new RangeError(`route epoch must be an integer in 1..4294967295, got ${epoch}`);
|
|
12553
|
+
}
|
|
12554
|
+
this.channel = channel;
|
|
12555
|
+
this.epoch = epoch;
|
|
12556
|
+
connectionToken.set(this, token);
|
|
12557
|
+
Object.freeze(this);
|
|
12082
12558
|
}
|
|
12083
|
-
|
|
12084
|
-
|
|
12559
|
+
static create(channel, epoch, token) {
|
|
12560
|
+
return new RouteHandle(channel, epoch, token);
|
|
12085
12561
|
}
|
|
12086
|
-
const channel = view.getUint16(7, true);
|
|
12087
|
-
const corr = view.getBigUint64(9, true);
|
|
12088
|
-
return { len, ver, ty, flags, channel, corr };
|
|
12089
12562
|
}
|
|
12090
|
-
function
|
|
12091
|
-
|
|
12563
|
+
function createRouteHandle(channel, epoch, token) {
|
|
12564
|
+
const factory = RouteHandle;
|
|
12565
|
+
return factory.create(channel, epoch, token);
|
|
12092
12566
|
}
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12567
|
+
|
|
12568
|
+
class StaleRouteHandleError extends Error {
|
|
12569
|
+
handle;
|
|
12570
|
+
code = "stale_route_handle";
|
|
12571
|
+
constructor(handle) {
|
|
12572
|
+
super(`route handle (${handle.channel}, ${handle.epoch}) is not live on the current connection`);
|
|
12573
|
+
this.handle = handle;
|
|
12574
|
+
this.name = "StaleRouteHandleError";
|
|
12099
12575
|
}
|
|
12100
|
-
return {
|
|
12101
|
-
header: { len: body.length, ver, ty, flags, channel, corr },
|
|
12102
|
-
body
|
|
12103
|
-
};
|
|
12104
12576
|
}
|
|
12105
|
-
function
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12577
|
+
function newConnectionToken() {
|
|
12578
|
+
return Object.freeze({});
|
|
12579
|
+
}
|
|
12580
|
+
function belongsToConnection(handle, token) {
|
|
12581
|
+
return connectionToken.get(handle) === token;
|
|
12582
|
+
}
|
|
12583
|
+
function sameRouteHandle(left, right) {
|
|
12584
|
+
return left === right;
|
|
12111
12585
|
}
|
|
12112
12586
|
|
|
12113
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12587
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
12114
12588
|
import net from "node:net";
|
|
12115
|
-
|
|
12116
12589
|
class SocketClosedError extends Error {
|
|
12117
12590
|
}
|
|
12118
12591
|
|
|
@@ -12181,6 +12654,24 @@ class SubcSocket {
|
|
|
12181
12654
|
});
|
|
12182
12655
|
});
|
|
12183
12656
|
}
|
|
12657
|
+
async readFrame(headerDeadlineMs, bodyDeadline, onHeader) {
|
|
12658
|
+
const prefix = await this.readExact(FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
12659
|
+
const version = prefix[4];
|
|
12660
|
+
if (version !== PROTOCOL_VERSION)
|
|
12661
|
+
throw new DecodeError(`unsupported envelope version ${version}`, "unsupported_version");
|
|
12662
|
+
const remainder = await this.readExact(HEADER_LEN - FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
12663
|
+
const headerBytes = new Uint8Array(HEADER_LEN);
|
|
12664
|
+
headerBytes.set(prefix);
|
|
12665
|
+
headerBytes.set(remainder, FROZEN_PREFIX_LEN);
|
|
12666
|
+
const header = decodeHeader(headerBytes);
|
|
12667
|
+
if (header.len > MAX_FRAME_BODY_LEN) {
|
|
12668
|
+
throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
12669
|
+
}
|
|
12670
|
+
onHeader?.();
|
|
12671
|
+
const bodyDeadlineMs = typeof bodyDeadline === "number" ? bodyDeadline : Date.now() + bodyDeadline.afterHeaderMs;
|
|
12672
|
+
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineMs);
|
|
12673
|
+
return { header, body };
|
|
12674
|
+
}
|
|
12184
12675
|
readExact(n, deadlineMs) {
|
|
12185
12676
|
if (this.waiter) {
|
|
12186
12677
|
return Promise.reject(new Error("concurrent readExact is not supported"));
|
|
@@ -12302,7 +12793,7 @@ class SubcSocket {
|
|
|
12302
12793
|
}
|
|
12303
12794
|
}
|
|
12304
12795
|
|
|
12305
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12796
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/client.js
|
|
12306
12797
|
var debug = debuglog("subc-client");
|
|
12307
12798
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
12308
12799
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
@@ -12348,7 +12839,11 @@ class SubcClient {
|
|
|
12348
12839
|
opts;
|
|
12349
12840
|
nextCorr = 1n;
|
|
12350
12841
|
pending = new Map;
|
|
12842
|
+
lateResponses = new Map;
|
|
12351
12843
|
routes = new Map;
|
|
12844
|
+
liveRoutes = new Map;
|
|
12845
|
+
connectionToken = newConnectionToken();
|
|
12846
|
+
ingressEpochDropCount = 0;
|
|
12352
12847
|
closedErr = null;
|
|
12353
12848
|
closeStarted = false;
|
|
12354
12849
|
reconnecting = null;
|
|
@@ -12376,40 +12871,71 @@ class SubcClient {
|
|
|
12376
12871
|
}
|
|
12377
12872
|
async routeOpen(target, identity, opts = {}) {
|
|
12378
12873
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
12874
|
+
const consumerCapabilities = opts.consumerCapabilities;
|
|
12379
12875
|
const body = this.encode({
|
|
12380
12876
|
op: "route.open",
|
|
12381
12877
|
target,
|
|
12382
12878
|
identity,
|
|
12383
|
-
...consumerIdentity ? { consumer_identity: consumerIdentity } : {}
|
|
12879
|
+
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
12880
|
+
...consumerCapabilities !== undefined ? { consumer_capabilities: consumerCapabilities } : {}
|
|
12384
12881
|
});
|
|
12385
|
-
|
|
12386
|
-
const
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12882
|
+
let installed = null;
|
|
12883
|
+
const install = (frame) => {
|
|
12884
|
+
if (frame.header.ty !== FrameType.Response)
|
|
12885
|
+
return true;
|
|
12886
|
+
const parsed = this.parseJson(frame);
|
|
12887
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
|
|
12888
|
+
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
12889
|
+
}
|
|
12890
|
+
installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
12891
|
+
return true;
|
|
12892
|
+
};
|
|
12893
|
+
const closeLateRoute = (frame) => {
|
|
12894
|
+
if (frame.header.ty !== FrameType.Response)
|
|
12895
|
+
return;
|
|
12896
|
+
try {
|
|
12897
|
+
const parsed = this.parseJson(frame);
|
|
12898
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
|
|
12899
|
+
return;
|
|
12900
|
+
const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
12901
|
+
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
|
|
12902
|
+
this.liveRoutes.delete(lateHandle.channel);
|
|
12903
|
+
this.sendRouteGoodbye(lateHandle, true);
|
|
12904
|
+
} catch {
|
|
12905
|
+
this.closeConnectionAfterCleanupFailure();
|
|
12906
|
+
}
|
|
12907
|
+
};
|
|
12908
|
+
await this.controlRpc(body, install, closeLateRoute);
|
|
12909
|
+
if (!installed)
|
|
12910
|
+
throw new SubcError("route.open response was not installed");
|
|
12911
|
+
return installed;
|
|
12391
12912
|
}
|
|
12392
|
-
async request(
|
|
12913
|
+
async request(handle, body, opts = {}) {
|
|
12914
|
+
this.assertLiveHandle(handle);
|
|
12393
12915
|
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
12394
12916
|
const priority = opts.priority ?? Priority.Interactive;
|
|
12395
|
-
const
|
|
12917
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
12918
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
12396
12919
|
return this.parseJson(reply);
|
|
12397
12920
|
}
|
|
12398
12921
|
async call(moduleId, method, params, opts = {}) {
|
|
12399
12922
|
const body = params === undefined ? { method } : { method, params };
|
|
12400
12923
|
let retriedUnknownChannel = false;
|
|
12401
12924
|
for (;; ) {
|
|
12402
|
-
const
|
|
12925
|
+
const routeHandle = await this.cachedRouteHandle(moduleId, opts);
|
|
12403
12926
|
try {
|
|
12404
|
-
return await this.managedRequest(
|
|
12927
|
+
return await this.managedRequest(routeHandle, body, opts);
|
|
12405
12928
|
} catch (err) {
|
|
12406
12929
|
if (!(err instanceof SubcCallError))
|
|
12407
12930
|
throw this.terminalCallError("managed call failed", err);
|
|
12408
12931
|
if (err.code === "unknown_channel" && !retriedUnknownChannel && !this.closeStarted) {
|
|
12409
12932
|
retriedUnknownChannel = true;
|
|
12410
|
-
this.
|
|
12933
|
+
this.evictRouteHandle(routeHandle);
|
|
12411
12934
|
continue;
|
|
12412
12935
|
}
|
|
12936
|
+
if (err.code === "unknown_channel" && retriedUnknownChannel) {
|
|
12937
|
+
this.evictRouteHandle(routeHandle);
|
|
12938
|
+
}
|
|
12413
12939
|
if (err.kind === "not_sent") {
|
|
12414
12940
|
try {
|
|
12415
12941
|
await this.reconnectAfterDrop(err);
|
|
@@ -12425,29 +12951,31 @@ class SubcClient {
|
|
|
12425
12951
|
}
|
|
12426
12952
|
}
|
|
12427
12953
|
}
|
|
12428
|
-
subscribe(
|
|
12954
|
+
subscribe(handle, body, onEvent, opts = {}) {
|
|
12955
|
+
this.assertLiveHandle(handle);
|
|
12429
12956
|
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
12430
12957
|
const priority = opts.priority ?? Priority.Interactive;
|
|
12431
|
-
const
|
|
12432
|
-
const
|
|
12958
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
12959
|
+
const corr = this.allocateCorr();
|
|
12960
|
+
const key = pendingKey(handle, corr);
|
|
12433
12961
|
const closed = new Promise((resolve7, reject) => {
|
|
12434
12962
|
if (this.closedErr) {
|
|
12435
12963
|
reject(this.closedErr);
|
|
12436
12964
|
return;
|
|
12437
12965
|
}
|
|
12438
12966
|
this.pending.set(key, {
|
|
12439
|
-
|
|
12967
|
+
handle,
|
|
12440
12968
|
resolve: () => resolve7(),
|
|
12441
12969
|
reject,
|
|
12442
12970
|
onProgress: onEvent,
|
|
12443
12971
|
timer: null,
|
|
12444
12972
|
subscription: true
|
|
12445
12973
|
});
|
|
12446
|
-
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false),
|
|
12974
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
12447
12975
|
this.sock.write(encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
12448
|
-
const
|
|
12449
|
-
if (
|
|
12450
|
-
this.rejectPending(key,
|
|
12976
|
+
const pending = this.pending.get(key);
|
|
12977
|
+
if (pending)
|
|
12978
|
+
this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
|
|
12451
12979
|
});
|
|
12452
12980
|
});
|
|
12453
12981
|
let cancelled = false;
|
|
@@ -12455,45 +12983,77 @@ class SubcClient {
|
|
|
12455
12983
|
if (cancelled)
|
|
12456
12984
|
return;
|
|
12457
12985
|
cancelled = true;
|
|
12458
|
-
|
|
12459
|
-
this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {});
|
|
12986
|
+
this.cancel(handle, corr, priority);
|
|
12460
12987
|
};
|
|
12461
12988
|
return { unsubscribe, closed };
|
|
12462
12989
|
}
|
|
12463
|
-
|
|
12990
|
+
cancel(handle, corr, priority = Priority.Interactive) {
|
|
12991
|
+
this.assertLiveHandle(handle);
|
|
12992
|
+
const cancel = buildFrame(FrameType.Cancel, buildFlags(false, priority, false), handle.channel, handle.epoch, corr, EMPTY_BODY);
|
|
12993
|
+
this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {
|
|
12994
|
+
return;
|
|
12995
|
+
});
|
|
12996
|
+
}
|
|
12997
|
+
async routePoll(handle, kind) {
|
|
12998
|
+
this.assertLiveHandle(handle);
|
|
12999
|
+
const body = this.encode({
|
|
13000
|
+
op: "route.poll",
|
|
13001
|
+
route_channel: handle.channel,
|
|
13002
|
+
route_epoch: handle.epoch,
|
|
13003
|
+
kind
|
|
13004
|
+
});
|
|
13005
|
+
const reply = await this.controlRpc(body, (frame) => {
|
|
13006
|
+
if (frame.header.ty !== FrameType.Response)
|
|
13007
|
+
return true;
|
|
13008
|
+
const parsed = this.parseJson(frame);
|
|
13009
|
+
return parsed.route_channel === handle.channel && parsed.route_epoch === handle.epoch;
|
|
13010
|
+
});
|
|
13011
|
+
return this.parseJson(reply);
|
|
13012
|
+
}
|
|
13013
|
+
async closeRoute(handle, opts = {}) {
|
|
13014
|
+
this.assertLiveHandle(handle);
|
|
13015
|
+
for (const [key, cached] of this.routes) {
|
|
13016
|
+
if (cached.handle && sameRouteHandle(cached.handle, handle)) {
|
|
13017
|
+
cached.closed = true;
|
|
13018
|
+
cached.handle = null;
|
|
13019
|
+
this.routes.delete(key);
|
|
13020
|
+
}
|
|
13021
|
+
}
|
|
13022
|
+
if (opts.drain)
|
|
13023
|
+
await this.drainUnaryOnHandle(handle);
|
|
13024
|
+
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
|
|
13025
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
13026
|
+
this.liveRoutes.delete(handle.channel);
|
|
13027
|
+
this.sendRouteGoodbye(handle);
|
|
13028
|
+
}
|
|
13029
|
+
async closeManagedRoute(target, identity, opts = {}) {
|
|
12464
13030
|
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
|
|
12465
13031
|
const cached = this.routes.get(key);
|
|
12466
13032
|
if (!cached)
|
|
12467
13033
|
return;
|
|
12468
13034
|
cached.closed = true;
|
|
12469
13035
|
this.routes.delete(key);
|
|
12470
|
-
const
|
|
12471
|
-
cached.
|
|
12472
|
-
if (
|
|
12473
|
-
await this.
|
|
13036
|
+
const handle = cached.handle;
|
|
13037
|
+
cached.handle = null;
|
|
13038
|
+
if (handle)
|
|
13039
|
+
await this.closeRoute(handle, opts);
|
|
12474
13040
|
}
|
|
12475
|
-
async closeRouteChannel(
|
|
12476
|
-
|
|
12477
|
-
return;
|
|
12478
|
-
if (opts.drain) {
|
|
12479
|
-
await this.drainUnaryOnChannel(channel);
|
|
12480
|
-
}
|
|
12481
|
-
this.failChannel(channel, new SubcError("route closed by closeRoute", "route_closed"));
|
|
12482
|
-
this.sendRouteGoodbye(channel);
|
|
13041
|
+
async closeRouteChannel(handle, opts = {}) {
|
|
13042
|
+
await this.closeRoute(handle, opts);
|
|
12483
13043
|
}
|
|
12484
13044
|
close() {
|
|
12485
13045
|
this.closeStarted = true;
|
|
12486
13046
|
this.fail(new SubcError("client closed"));
|
|
12487
13047
|
this.sock.close();
|
|
12488
13048
|
}
|
|
12489
|
-
|
|
13049
|
+
drainUnaryOnHandle(handle) {
|
|
12490
13050
|
const waiters = [];
|
|
12491
13051
|
for (const pending of this.pending.values()) {
|
|
12492
|
-
if (pending.
|
|
13052
|
+
if (pending.handle === handle && !pending.subscription) {
|
|
12493
13053
|
waiters.push(new Promise((resolve7) => {
|
|
12494
|
-
const
|
|
13054
|
+
const previous = pending.onSettle;
|
|
12495
13055
|
pending.onSettle = () => {
|
|
12496
|
-
|
|
13056
|
+
previous?.();
|
|
12497
13057
|
resolve7();
|
|
12498
13058
|
};
|
|
12499
13059
|
}));
|
|
@@ -12503,11 +13063,21 @@ class SubcClient {
|
|
|
12503
13063
|
return;
|
|
12504
13064
|
});
|
|
12505
13065
|
}
|
|
12506
|
-
sendRouteGoodbye(
|
|
12507
|
-
|
|
13066
|
+
sendRouteGoodbye(handle, closeOnQueueFailure = false) {
|
|
13067
|
+
this.assertLiveConnection(handle);
|
|
13068
|
+
if (this.closedErr) {
|
|
13069
|
+
if (closeOnQueueFailure)
|
|
13070
|
+
this.closeConnectionAfterCleanupFailure();
|
|
12508
13071
|
return;
|
|
12509
|
-
|
|
12510
|
-
|
|
13072
|
+
}
|
|
13073
|
+
const goodbye = buildFrame(FrameType.Goodbye, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, 0n, EMPTY_BODY);
|
|
13074
|
+
const write = this.sock.writeTracked(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
13075
|
+
if (!write.queued && closeOnQueueFailure)
|
|
13076
|
+
this.closeConnectionAfterCleanupFailure();
|
|
13077
|
+
write.completed.catch(() => {
|
|
13078
|
+
if (closeOnQueueFailure && !write.queued)
|
|
13079
|
+
this.closeConnectionAfterCleanupFailure();
|
|
13080
|
+
});
|
|
12511
13081
|
}
|
|
12512
13082
|
static async openConnection(opts) {
|
|
12513
13083
|
const conn = await readConnectionFile(opts.connectionFile);
|
|
@@ -12522,37 +13092,48 @@ class SubcClient {
|
|
|
12522
13092
|
}
|
|
12523
13093
|
return { sock, conn };
|
|
12524
13094
|
}
|
|
12525
|
-
async controlRpc(body) {
|
|
12526
|
-
return this.send(
|
|
13095
|
+
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
13096
|
+
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
12527
13097
|
}
|
|
12528
|
-
send(
|
|
13098
|
+
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse) {
|
|
13099
|
+
if (handle)
|
|
13100
|
+
this.assertLiveHandle(handle);
|
|
12529
13101
|
if (this.closedErr)
|
|
12530
13102
|
return Promise.reject(this.closedErr);
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
13103
|
+
let corr;
|
|
13104
|
+
try {
|
|
13105
|
+
corr = this.allocateCorr();
|
|
13106
|
+
} catch (error2) {
|
|
13107
|
+
return Promise.reject(error2);
|
|
13108
|
+
}
|
|
13109
|
+
const key = pendingKey(handle, corr);
|
|
13110
|
+
const channel = handle?.channel ?? 0;
|
|
13111
|
+
const epoch = handle?.epoch ?? 0;
|
|
13112
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
|
|
12534
13113
|
return new Promise((resolve7, reject) => {
|
|
12535
13114
|
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
12536
13115
|
const pending = {
|
|
12537
|
-
|
|
13116
|
+
handle,
|
|
12538
13117
|
resolve: resolve7,
|
|
12539
13118
|
reject,
|
|
12540
13119
|
onProgress,
|
|
12541
|
-
timer: null
|
|
13120
|
+
timer: null,
|
|
13121
|
+
acceptFrame,
|
|
13122
|
+
onLateResponse
|
|
12542
13123
|
};
|
|
12543
|
-
pending.timer = setTimeout(() =>
|
|
12544
|
-
this.arbitrateTimeout(key, pending, channel, corr, ms);
|
|
12545
|
-
}, ms);
|
|
13124
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
12546
13125
|
this.pending.set(key, pending);
|
|
12547
|
-
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((
|
|
12548
|
-
const
|
|
12549
|
-
if (
|
|
12550
|
-
this.rejectPending(key,
|
|
13126
|
+
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((error2) => {
|
|
13127
|
+
const current = this.pending.get(key);
|
|
13128
|
+
if (current)
|
|
13129
|
+
this.rejectPending(key, current, error2 instanceof Error ? error2 : new SubcError(String(error2)));
|
|
12551
13130
|
});
|
|
12552
13131
|
});
|
|
12553
13132
|
}
|
|
12554
13133
|
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
12555
13134
|
const settleAsTimeout = () => {
|
|
13135
|
+
if (pending.onLateResponse)
|
|
13136
|
+
this.lateResponses.set(key, pending.onLateResponse);
|
|
12556
13137
|
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
12557
13138
|
};
|
|
12558
13139
|
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
@@ -12568,59 +13149,67 @@ class SubcClient {
|
|
|
12568
13149
|
};
|
|
12569
13150
|
setImmediate(arbitrate);
|
|
12570
13151
|
}
|
|
12571
|
-
async managedRequest(
|
|
13152
|
+
async managedRequest(handle, body, opts) {
|
|
12572
13153
|
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
12573
13154
|
const priority = opts.priority ?? Priority.Interactive;
|
|
13155
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
12574
13156
|
try {
|
|
12575
|
-
const reply = await this.sendManaged(
|
|
13157
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
12576
13158
|
return this.parseJson(reply);
|
|
12577
|
-
} catch (
|
|
12578
|
-
if (
|
|
12579
|
-
throw
|
|
12580
|
-
throw this.terminalCallError("managed call failed",
|
|
13159
|
+
} catch (error2) {
|
|
13160
|
+
if (error2 instanceof SubcCallError)
|
|
13161
|
+
throw error2;
|
|
13162
|
+
throw this.terminalCallError("managed call failed", error2);
|
|
12581
13163
|
}
|
|
12582
13164
|
}
|
|
12583
|
-
sendManaged(
|
|
13165
|
+
sendManaged(handle, body, priority, admission, timeoutMs, onProgress) {
|
|
13166
|
+
try {
|
|
13167
|
+
this.assertLiveHandle(handle);
|
|
13168
|
+
} catch (error2) {
|
|
13169
|
+
return Promise.reject(this.notSentCallError("request used a stale route handle", error2));
|
|
13170
|
+
}
|
|
12584
13171
|
if (this.closedErr) {
|
|
12585
13172
|
return Promise.reject(this.notSentCallError("request was not sent because the subc connection was already closed", this.closedErr));
|
|
12586
13173
|
}
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
13174
|
+
let corr;
|
|
13175
|
+
try {
|
|
13176
|
+
corr = this.allocateCorr();
|
|
13177
|
+
} catch (error2) {
|
|
13178
|
+
return Promise.reject(this.notSentCallError("request correlation allocator was exhausted", error2));
|
|
13179
|
+
}
|
|
13180
|
+
const key = pendingKey(handle, corr);
|
|
13181
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, body);
|
|
12590
13182
|
let handedToSocket = false;
|
|
12591
|
-
const classifyFailure = (
|
|
12592
|
-
if (!handedToSocket)
|
|
12593
|
-
return this.notSentCallError("request bytes were not queued to the subc socket",
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(err)}`, DEADLINE_NO_DROP_CODE, err);
|
|
13183
|
+
const classifyFailure = (error2) => {
|
|
13184
|
+
if (!handedToSocket)
|
|
13185
|
+
return this.notSentCallError("request bytes were not queued to the subc socket", error2);
|
|
13186
|
+
if (error2 instanceof SubcError && error2.code === REQUEST_DEADLINE_MARKER) {
|
|
13187
|
+
return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(error2)}`, DEADLINE_NO_DROP_CODE, error2);
|
|
12597
13188
|
}
|
|
12598
|
-
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response",
|
|
13189
|
+
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error2);
|
|
12599
13190
|
};
|
|
12600
13191
|
return new Promise((resolve7, reject) => {
|
|
12601
13192
|
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
12602
13193
|
const pending = {
|
|
12603
|
-
|
|
13194
|
+
handle,
|
|
12604
13195
|
resolve: resolve7,
|
|
12605
13196
|
reject,
|
|
12606
13197
|
onProgress,
|
|
12607
13198
|
timer: null,
|
|
12608
13199
|
classifyFailure
|
|
12609
13200
|
};
|
|
12610
|
-
pending.timer = setTimeout(() =>
|
|
12611
|
-
this.arbitrateTimeout(key, pending, channel, corr, ms);
|
|
12612
|
-
}, ms);
|
|
13201
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
12613
13202
|
this.pending.set(key, pending);
|
|
12614
13203
|
const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
|
|
12615
13204
|
handedToSocket = write.queued;
|
|
12616
|
-
write.completed.catch((
|
|
12617
|
-
const
|
|
12618
|
-
if (
|
|
12619
|
-
this.rejectPending(key,
|
|
13205
|
+
write.completed.catch((error2) => {
|
|
13206
|
+
const current = this.pending.get(key);
|
|
13207
|
+
if (current)
|
|
13208
|
+
this.rejectPending(key, current, error2 instanceof Error ? error2 : new SubcError(String(error2)));
|
|
12620
13209
|
});
|
|
12621
13210
|
});
|
|
12622
13211
|
}
|
|
12623
|
-
async
|
|
13212
|
+
async cachedRouteHandle(moduleId, opts) {
|
|
12624
13213
|
const identity = opts.identity ?? this.opts.identity;
|
|
12625
13214
|
if (!identity) {
|
|
12626
13215
|
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
@@ -12636,15 +13225,13 @@ class SubcClient {
|
|
|
12636
13225
|
target,
|
|
12637
13226
|
identity,
|
|
12638
13227
|
consumerIdentity,
|
|
12639
|
-
|
|
12640
|
-
generation: 0,
|
|
13228
|
+
handle: null,
|
|
12641
13229
|
opening: null
|
|
12642
13230
|
};
|
|
12643
13231
|
this.routes.set(key, cached);
|
|
12644
13232
|
}
|
|
12645
|
-
if (cached.
|
|
12646
|
-
return cached.
|
|
12647
|
-
}
|
|
13233
|
+
if (cached.handle && this.isLiveHandle(cached.handle))
|
|
13234
|
+
return cached.handle;
|
|
12648
13235
|
if (!cached.opening) {
|
|
12649
13236
|
cached.opening = this.openCachedRoute(cached).finally(() => {
|
|
12650
13237
|
cached.opening = null;
|
|
@@ -12661,44 +13248,43 @@ class SubcClient {
|
|
|
12661
13248
|
throw this.routeClosedDuringOpen();
|
|
12662
13249
|
try {
|
|
12663
13250
|
await this.ensureConnectedForManaged();
|
|
12664
|
-
} catch (
|
|
12665
|
-
throw this.notSentRecoveryError("route.open could not run because reconnect failed",
|
|
12666
|
-
}
|
|
12667
|
-
if (cached.channel !== null && cached.generation === this.generation && !this.closedErr) {
|
|
12668
|
-
return cached.channel;
|
|
13251
|
+
} catch (error2) {
|
|
13252
|
+
throw this.notSentRecoveryError("route.open could not run because reconnect failed", error2);
|
|
12669
13253
|
}
|
|
13254
|
+
if (cached.handle && this.isLiveHandle(cached.handle))
|
|
13255
|
+
return cached.handle;
|
|
12670
13256
|
try {
|
|
12671
|
-
const
|
|
13257
|
+
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
12672
13258
|
consumerIdentity: cached.consumerIdentity ?? null
|
|
12673
13259
|
});
|
|
12674
13260
|
if (cached.closed) {
|
|
12675
|
-
this.
|
|
13261
|
+
this.liveRoutes.delete(handle.channel);
|
|
13262
|
+
this.sendRouteGoodbye(handle);
|
|
12676
13263
|
throw this.routeClosedDuringOpen();
|
|
12677
13264
|
}
|
|
12678
|
-
cached.
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
if (!this.closeStarted && isConsumerReconnectTransient(err)) {
|
|
13265
|
+
cached.handle = handle;
|
|
13266
|
+
return handle;
|
|
13267
|
+
} catch (error2) {
|
|
13268
|
+
if (error2 instanceof SubcCallError && error2.code === "route_closed")
|
|
13269
|
+
throw error2;
|
|
13270
|
+
if (!this.closeStarted && isConsumerReconnectTransient(error2)) {
|
|
12685
13271
|
try {
|
|
12686
|
-
await this.reconnectAfterDrop(
|
|
12687
|
-
} catch (
|
|
12688
|
-
throw this.notSentRecoveryError("route.open was not sent and reconnect failed",
|
|
13272
|
+
await this.reconnectAfterDrop(error2);
|
|
13273
|
+
} catch (reconnectError) {
|
|
13274
|
+
throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectError);
|
|
12689
13275
|
}
|
|
12690
13276
|
continue;
|
|
12691
13277
|
}
|
|
12692
|
-
if (!this.closeStarted &&
|
|
13278
|
+
if (!this.closeStarted && error2 instanceof SubcError && isRetryableRouteOpenCode(error2.code)) {
|
|
12693
13279
|
routeRetryAttempt += 1;
|
|
12694
13280
|
if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
|
|
12695
13281
|
await this.opts.sleep(routeRetryDelay);
|
|
12696
13282
|
routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
|
|
12697
13283
|
continue;
|
|
12698
13284
|
}
|
|
12699
|
-
throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${
|
|
13285
|
+
throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${error2.code} (retry budget exhausted)`, error2);
|
|
12700
13286
|
}
|
|
12701
|
-
throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`,
|
|
13287
|
+
throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, error2);
|
|
12702
13288
|
}
|
|
12703
13289
|
}
|
|
12704
13290
|
}
|
|
@@ -12761,25 +13347,27 @@ class SubcClient {
|
|
|
12761
13347
|
this.currentConn = opened.conn;
|
|
12762
13348
|
this.closedErr = null;
|
|
12763
13349
|
this.generation += 1;
|
|
13350
|
+
this.connectionToken = newConnectionToken();
|
|
13351
|
+
this.liveRoutes.clear();
|
|
13352
|
+
this.lateResponses.clear();
|
|
13353
|
+
this.nextCorr = 1n;
|
|
12764
13354
|
this.readLoop(opened.sock, this.generation);
|
|
12765
13355
|
}
|
|
12766
13356
|
async reopenCachedRoutes() {
|
|
12767
|
-
for (const cached of this.routes.values())
|
|
12768
|
-
cached.
|
|
12769
|
-
cached.generation = 0;
|
|
12770
|
-
}
|
|
13357
|
+
for (const cached of this.routes.values())
|
|
13358
|
+
cached.handle = null;
|
|
12771
13359
|
for (const cached of this.routes.values()) {
|
|
12772
13360
|
if (cached.closed)
|
|
12773
13361
|
continue;
|
|
12774
|
-
const
|
|
13362
|
+
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
12775
13363
|
consumerIdentity: cached.consumerIdentity ?? null
|
|
12776
13364
|
});
|
|
12777
13365
|
if (cached.closed) {
|
|
12778
|
-
this.
|
|
13366
|
+
this.liveRoutes.delete(handle.channel);
|
|
13367
|
+
this.sendRouteGoodbye(handle);
|
|
12779
13368
|
continue;
|
|
12780
13369
|
}
|
|
12781
|
-
cached.
|
|
12782
|
-
cached.generation = this.generation;
|
|
13370
|
+
cached.handle = handle;
|
|
12783
13371
|
}
|
|
12784
13372
|
}
|
|
12785
13373
|
timeoutMessage(channel, corr, ms) {
|
|
@@ -12793,28 +13381,37 @@ class SubcClient {
|
|
|
12793
13381
|
async readLoop(sock, generation) {
|
|
12794
13382
|
try {
|
|
12795
13383
|
for (;; ) {
|
|
12796
|
-
|
|
12797
|
-
|
|
13384
|
+
this.readerActive = false;
|
|
13385
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
|
|
13386
|
+
this.readerActive = true;
|
|
13387
|
+
});
|
|
12798
13388
|
try {
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
if (this.sock === sock && this.generation === generation) {
|
|
12802
|
-
this.dispatch({ header, body });
|
|
12803
|
-
}
|
|
13389
|
+
if (this.sock === sock && this.generation === generation)
|
|
13390
|
+
this.dispatch(frame);
|
|
12804
13391
|
} finally {
|
|
12805
13392
|
this.readerActive = false;
|
|
12806
13393
|
}
|
|
12807
13394
|
}
|
|
12808
|
-
} catch (
|
|
13395
|
+
} catch (error2) {
|
|
12809
13396
|
if (this.sock === sock && this.generation === generation) {
|
|
12810
|
-
this.fail(
|
|
13397
|
+
this.fail(error2 instanceof Error ? error2 : new SubcError(String(error2)));
|
|
12811
13398
|
}
|
|
12812
13399
|
}
|
|
12813
13400
|
}
|
|
12814
13401
|
dispatch(frame) {
|
|
12815
|
-
|
|
13402
|
+
let handle = null;
|
|
13403
|
+
if (frame.header.channel !== 0) {
|
|
13404
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
13405
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
13406
|
+
this.ingressEpochDropCount += 1;
|
|
13407
|
+
return;
|
|
13408
|
+
}
|
|
13409
|
+
}
|
|
13410
|
+
const key = pendingKey(handle, frame.header.corr);
|
|
12816
13411
|
const pending = this.pending.get(key);
|
|
12817
13412
|
if (pending) {
|
|
13413
|
+
if (pending.acceptFrame && !pending.acceptFrame(frame))
|
|
13414
|
+
return;
|
|
12818
13415
|
switch (frame.header.ty) {
|
|
12819
13416
|
case FrameType.Push:
|
|
12820
13417
|
case FrameType.StreamData:
|
|
@@ -12831,15 +13428,22 @@ class SubcClient {
|
|
|
12831
13428
|
return;
|
|
12832
13429
|
}
|
|
12833
13430
|
}
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
this.
|
|
13431
|
+
const late = this.lateResponses.get(key);
|
|
13432
|
+
if (late && (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error)) {
|
|
13433
|
+
this.lateResponses.delete(key);
|
|
13434
|
+
late(frame);
|
|
12837
13435
|
return;
|
|
12838
13436
|
}
|
|
12839
|
-
if (frame.header.ty === FrameType.
|
|
12840
|
-
|
|
13437
|
+
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
13438
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)"));
|
|
13439
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
13440
|
+
this.liveRoutes.delete(handle.channel);
|
|
13441
|
+
this.evictRouteHandle(handle);
|
|
12841
13442
|
return;
|
|
12842
13443
|
}
|
|
13444
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
|
|
13445
|
+
debug("dropped terminal frame with no waiter: type=%d channel=%d epoch=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.epoch, frame.header.corr, this.sock.localPort() ?? "?");
|
|
13446
|
+
}
|
|
12843
13447
|
}
|
|
12844
13448
|
settle(key, pending, run) {
|
|
12845
13449
|
if (this.pending.get(key) !== pending)
|
|
@@ -12862,18 +13466,16 @@ class SubcClient {
|
|
|
12862
13466
|
return new SubcError(Buffer.from(frame.body).toString("utf8") || "subc error");
|
|
12863
13467
|
}
|
|
12864
13468
|
}
|
|
12865
|
-
|
|
13469
|
+
evictRouteHandle(handle) {
|
|
12866
13470
|
for (const cached of this.routes.values()) {
|
|
12867
|
-
if (cached.
|
|
12868
|
-
cached.
|
|
12869
|
-
}
|
|
13471
|
+
if (cached.handle && sameRouteHandle(cached.handle, handle))
|
|
13472
|
+
cached.handle = null;
|
|
12870
13473
|
}
|
|
12871
13474
|
}
|
|
12872
|
-
|
|
13475
|
+
failHandle(handle, error2) {
|
|
12873
13476
|
for (const [key, pending] of this.pending) {
|
|
12874
|
-
if (pending.
|
|
12875
|
-
this.rejectPending(key, pending,
|
|
12876
|
-
}
|
|
13477
|
+
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
13478
|
+
this.rejectPending(key, pending, error2);
|
|
12877
13479
|
}
|
|
12878
13480
|
}
|
|
12879
13481
|
fail(err) {
|
|
@@ -12901,6 +13503,44 @@ class SubcClient {
|
|
|
12901
13503
|
return this.notSentCallError(message, cause);
|
|
12902
13504
|
return this.terminalCallError(message, cause);
|
|
12903
13505
|
}
|
|
13506
|
+
get droppedIngressFrames() {
|
|
13507
|
+
return this.ingressEpochDropCount;
|
|
13508
|
+
}
|
|
13509
|
+
installRoute(channel, epoch) {
|
|
13510
|
+
const handle = createRouteHandle(channel, epoch, this.connectionToken);
|
|
13511
|
+
this.liveRoutes.set(channel, handle);
|
|
13512
|
+
return handle;
|
|
13513
|
+
}
|
|
13514
|
+
isLiveHandle(handle) {
|
|
13515
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
13516
|
+
}
|
|
13517
|
+
assertLiveConnection(handle) {
|
|
13518
|
+
if (!belongsToConnection(handle, this.connectionToken))
|
|
13519
|
+
throw new StaleRouteHandleError(handle);
|
|
13520
|
+
}
|
|
13521
|
+
assertLiveHandle(handle) {
|
|
13522
|
+
if (!this.isLiveHandle(handle))
|
|
13523
|
+
throw new StaleRouteHandleError(handle);
|
|
13524
|
+
}
|
|
13525
|
+
allocateCorr() {
|
|
13526
|
+
const maximum = 0xffffffffffffffffn;
|
|
13527
|
+
if (this.nextCorr > maximum) {
|
|
13528
|
+
const error2 = new SubcError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
13529
|
+
this.fail(error2);
|
|
13530
|
+
this.sock.close();
|
|
13531
|
+
this.scheduleReconnectAfterDrop(error2);
|
|
13532
|
+
throw error2;
|
|
13533
|
+
}
|
|
13534
|
+
const corr = this.nextCorr;
|
|
13535
|
+
this.nextCorr += 1n;
|
|
13536
|
+
return corr;
|
|
13537
|
+
}
|
|
13538
|
+
closeConnectionAfterCleanupFailure() {
|
|
13539
|
+
const error2 = new SubcError("late route cleanup could not be queued", "late_route_cleanup_failed");
|
|
13540
|
+
this.fail(error2);
|
|
13541
|
+
this.sock.close();
|
|
13542
|
+
this.scheduleReconnectAfterDrop(error2);
|
|
13543
|
+
}
|
|
12904
13544
|
encode(value) {
|
|
12905
13545
|
return new Uint8Array(Buffer.from(JSON.stringify(value), "utf8"));
|
|
12906
13546
|
}
|
|
@@ -12970,7 +13610,10 @@ function causeMessage(cause) {
|
|
|
12970
13610
|
return "";
|
|
12971
13611
|
return `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
12972
13612
|
}
|
|
12973
|
-
|
|
13613
|
+
function pendingKey(handle, corr) {
|
|
13614
|
+
return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
|
|
13615
|
+
}
|
|
13616
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
12974
13617
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
12975
13618
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
|
|
12976
13619
|
var BODY_READ_TIMEOUT_MS2 = 30000;
|
|
@@ -13033,6 +13676,11 @@ class SubcProvider {
|
|
|
13033
13676
|
closeStarted = false;
|
|
13034
13677
|
closedErr = null;
|
|
13035
13678
|
inflight = new Map;
|
|
13679
|
+
pending = new Map;
|
|
13680
|
+
liveRoutes = new Map;
|
|
13681
|
+
connectionToken = newConnectionToken();
|
|
13682
|
+
nextCorr = 1n;
|
|
13683
|
+
ingressEpochDropCount = 0;
|
|
13036
13684
|
requestGate = new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY);
|
|
13037
13685
|
reconnecting = null;
|
|
13038
13686
|
generation = 1;
|
|
@@ -13052,12 +13700,55 @@ class SubcProvider {
|
|
|
13052
13700
|
this.readLoop(sock, this.generation);
|
|
13053
13701
|
this.enqueueConnectionState({ state: "connected", epoch: this.connectionEpoch });
|
|
13054
13702
|
}
|
|
13703
|
+
get droppedIngressFrames() {
|
|
13704
|
+
return this.ingressEpochDropCount;
|
|
13705
|
+
}
|
|
13055
13706
|
get conn() {
|
|
13056
13707
|
return this.currentConn;
|
|
13057
13708
|
}
|
|
13058
13709
|
currentEpoch() {
|
|
13059
13710
|
return this.connectionEpoch;
|
|
13060
13711
|
}
|
|
13712
|
+
async request(handle, body, opts = {}) {
|
|
13713
|
+
this.assertLiveHandle(handle);
|
|
13714
|
+
const corr = this.allocateCorr();
|
|
13715
|
+
const key = routeKey(handle, corr);
|
|
13716
|
+
const timeoutMs = opts.timeoutMs ?? WRITE_TIMEOUT_MS;
|
|
13717
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, body);
|
|
13718
|
+
return await new Promise((resolve7, reject) => {
|
|
13719
|
+
const timer = setTimeout(() => {
|
|
13720
|
+
if (this.pending.delete(key))
|
|
13721
|
+
reject(new SubcProviderError("reverse request timed out", "request_timeout"));
|
|
13722
|
+
}, timeoutMs);
|
|
13723
|
+
this.pending.set(key, {
|
|
13724
|
+
resolve: (response) => resolve7(response.body),
|
|
13725
|
+
reject,
|
|
13726
|
+
timer
|
|
13727
|
+
});
|
|
13728
|
+
this.sendOn(this.sock, this.generation, frame).catch((error2) => {
|
|
13729
|
+
const pending = this.pending.get(key);
|
|
13730
|
+
if (!pending)
|
|
13731
|
+
return;
|
|
13732
|
+
this.pending.delete(key);
|
|
13733
|
+
clearTimeout(pending.timer);
|
|
13734
|
+
reject(error2 instanceof Error ? error2 : new SubcProviderError(String(error2)));
|
|
13735
|
+
});
|
|
13736
|
+
});
|
|
13737
|
+
}
|
|
13738
|
+
async push(handle, body, opts = {}) {
|
|
13739
|
+
this.assertLiveHandle(handle);
|
|
13740
|
+
await this.sendOn(this.sock, this.generation, buildFrame(FrameType.Push, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, 0n, body));
|
|
13741
|
+
}
|
|
13742
|
+
cancel(handle, corr) {
|
|
13743
|
+
this.assertLiveHandle(handle);
|
|
13744
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Cancel, controlFlags(), handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
13745
|
+
}
|
|
13746
|
+
closeRoute(handle) {
|
|
13747
|
+
this.assertLiveHandle(handle);
|
|
13748
|
+
this.liveRoutes.delete(handle.channel);
|
|
13749
|
+
this.abortHandle(handle);
|
|
13750
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Goodbye, controlFlags(), handle.channel, handle.epoch, 0n, new Uint8Array(0)));
|
|
13751
|
+
}
|
|
13061
13752
|
static async connect(opts) {
|
|
13062
13753
|
if (opts.manifest.protocol_ver !== PROTOCOL_VERSION) {
|
|
13063
13754
|
throw new SubcProviderError(`manifest protocol_ver ${opts.manifest.protocol_ver} does not match client protocol ${PROTOCOL_VERSION}`, "invalid_manifest");
|
|
@@ -13072,7 +13763,7 @@ class SubcProvider {
|
|
|
13072
13763
|
this.cancelRestoredDebounce();
|
|
13073
13764
|
const sock = this.sock;
|
|
13074
13765
|
try {
|
|
13075
|
-
await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0n, new Uint8Array(0)));
|
|
13766
|
+
await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0, 0n, new Uint8Array(0)));
|
|
13076
13767
|
} catch {} finally {
|
|
13077
13768
|
sock.close();
|
|
13078
13769
|
this.finishClosed();
|
|
@@ -13080,12 +13771,13 @@ class SubcProvider {
|
|
|
13080
13771
|
}
|
|
13081
13772
|
await this.closed;
|
|
13082
13773
|
}
|
|
13083
|
-
static async openConnection(opts) {
|
|
13774
|
+
static async openConnection(opts, onSocket) {
|
|
13084
13775
|
const conn = await readConnectionFile(opts.connectionFile);
|
|
13085
13776
|
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS2);
|
|
13086
13777
|
const endpoint = conn.endpoints[0];
|
|
13087
13778
|
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
13088
13779
|
try {
|
|
13780
|
+
onSocket?.(sock);
|
|
13089
13781
|
await authenticateClient(sock, conn, deadline);
|
|
13090
13782
|
await sendFrame(sock, buildHelloFrame(opts));
|
|
13091
13783
|
const ack = await expectHelloAck(sock, deadline);
|
|
@@ -13098,19 +13790,17 @@ class SubcProvider {
|
|
|
13098
13790
|
async readLoop(sock, generation) {
|
|
13099
13791
|
try {
|
|
13100
13792
|
for (;; ) {
|
|
13101
|
-
const
|
|
13102
|
-
const
|
|
13103
|
-
const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS2);
|
|
13104
|
-
const keepGoing = await this.dispatch({ header, body }, sock, generation);
|
|
13793
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS2 });
|
|
13794
|
+
const keepGoing = await this.dispatch(frame, sock, generation);
|
|
13105
13795
|
if (!keepGoing) {
|
|
13106
13796
|
if (this.sock === sock && this.generation === generation)
|
|
13107
13797
|
this.closeStarted = true;
|
|
13108
13798
|
break;
|
|
13109
13799
|
}
|
|
13110
13800
|
}
|
|
13111
|
-
} catch (
|
|
13801
|
+
} catch (error2) {
|
|
13112
13802
|
if (this.sock === sock && this.generation === generation && !this.closeStarted) {
|
|
13113
|
-
this.handleUnexpectedDrop(sock, generation,
|
|
13803
|
+
this.handleUnexpectedDrop(sock, generation, error2 instanceof Error ? error2 : new SubcProviderError(String(error2)));
|
|
13114
13804
|
return;
|
|
13115
13805
|
}
|
|
13116
13806
|
} finally {
|
|
@@ -13122,28 +13812,58 @@ class SubcProvider {
|
|
|
13122
13812
|
}
|
|
13123
13813
|
}
|
|
13124
13814
|
async dispatch(frame, sock, generation) {
|
|
13815
|
+
let handle = null;
|
|
13816
|
+
if (frame.header.channel !== 0) {
|
|
13817
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
13818
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
13819
|
+
this.ingressEpochDropCount += 1;
|
|
13820
|
+
return true;
|
|
13821
|
+
}
|
|
13822
|
+
}
|
|
13823
|
+
if (handle) {
|
|
13824
|
+
const pendingKey2 = routeKey(handle, frame.header.corr);
|
|
13825
|
+
const pending = this.pending.get(pendingKey2);
|
|
13826
|
+
if (pending) {
|
|
13827
|
+
if (frame.header.ty === FrameType.Push || frame.header.ty === FrameType.StreamData)
|
|
13828
|
+
return true;
|
|
13829
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.StreamEnd) {
|
|
13830
|
+
this.pending.delete(pendingKey2);
|
|
13831
|
+
clearTimeout(pending.timer);
|
|
13832
|
+
pending.resolve(frame);
|
|
13833
|
+
return true;
|
|
13834
|
+
}
|
|
13835
|
+
if (frame.header.ty === FrameType.Error) {
|
|
13836
|
+
this.pending.delete(pendingKey2);
|
|
13837
|
+
clearTimeout(pending.timer);
|
|
13838
|
+
pending.reject(providerErrorFromFrame(frame));
|
|
13839
|
+
return true;
|
|
13840
|
+
}
|
|
13841
|
+
}
|
|
13842
|
+
}
|
|
13125
13843
|
switch (frame.header.ty) {
|
|
13126
13844
|
case FrameType.Ping:
|
|
13127
13845
|
if (frame.header.channel === 0) {
|
|
13128
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, frame.header.corr, new Uint8Array(0)));
|
|
13846
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, 0, frame.header.corr, new Uint8Array(0)));
|
|
13129
13847
|
}
|
|
13130
13848
|
return true;
|
|
13131
13849
|
case FrameType.Goodbye:
|
|
13132
|
-
if (
|
|
13850
|
+
if (!handle)
|
|
13133
13851
|
return false;
|
|
13134
|
-
this.
|
|
13135
|
-
|
|
13852
|
+
this.liveRoutes.delete(handle.channel);
|
|
13853
|
+
this.abortHandle(handle);
|
|
13854
|
+
await this.opts.onRouteGone?.(handle);
|
|
13136
13855
|
return true;
|
|
13137
13856
|
case FrameType.Cancel:
|
|
13138
|
-
|
|
13857
|
+
if (handle)
|
|
13858
|
+
this.inflight.get(routeKey(handle, frame.header.corr))?.abort();
|
|
13139
13859
|
return true;
|
|
13140
13860
|
case FrameType.Request:
|
|
13141
13861
|
if (frame.header.channel === 0) {
|
|
13142
13862
|
await this.handleControlRequest(frame, sock, generation);
|
|
13143
|
-
} else {
|
|
13144
|
-
this.handleDataRequest(frame, sock, generation).catch((
|
|
13863
|
+
} else if (handle) {
|
|
13864
|
+
this.handleDataRequest(frame, handle, sock, generation).catch((error2) => {
|
|
13145
13865
|
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
13146
|
-
console.warn("SubcProvider handler failed after its request was dispatched",
|
|
13866
|
+
console.warn("SubcProvider handler failed after its request was dispatched", error2);
|
|
13147
13867
|
}
|
|
13148
13868
|
});
|
|
13149
13869
|
}
|
|
@@ -13152,19 +13872,28 @@ class SubcProvider {
|
|
|
13152
13872
|
return true;
|
|
13153
13873
|
}
|
|
13154
13874
|
}
|
|
13155
|
-
|
|
13156
|
-
const prefix = `${
|
|
13875
|
+
abortHandle(handle) {
|
|
13876
|
+
const prefix = `${handle.channel}:${handle.epoch}:`;
|
|
13157
13877
|
for (const [key, controller] of this.inflight) {
|
|
13158
13878
|
if (key.startsWith(prefix))
|
|
13159
13879
|
controller.abort();
|
|
13160
13880
|
}
|
|
13881
|
+
for (const [key, pending] of this.pending) {
|
|
13882
|
+
if (!key.startsWith(prefix))
|
|
13883
|
+
continue;
|
|
13884
|
+
this.pending.delete(key);
|
|
13885
|
+
clearTimeout(pending.timer);
|
|
13886
|
+
pending.reject(new StaleRouteHandleError(handle));
|
|
13887
|
+
}
|
|
13161
13888
|
}
|
|
13162
|
-
abortGeneration(
|
|
13163
|
-
|
|
13164
|
-
for (const [key,
|
|
13165
|
-
|
|
13166
|
-
|
|
13889
|
+
abortGeneration(_generation) {
|
|
13890
|
+
this.abortAllInflight();
|
|
13891
|
+
for (const [key, pending] of this.pending) {
|
|
13892
|
+
this.pending.delete(key);
|
|
13893
|
+
clearTimeout(pending.timer);
|
|
13894
|
+
pending.reject(new SubcProviderError("provider connection dropped", "connection_dropped"));
|
|
13167
13895
|
}
|
|
13896
|
+
this.liveRoutes.clear();
|
|
13168
13897
|
}
|
|
13169
13898
|
abortAllInflight() {
|
|
13170
13899
|
for (const controller of this.inflight.values())
|
|
@@ -13173,9 +13902,9 @@ class SubcProvider {
|
|
|
13173
13902
|
async handleControlRequest(frame, sock, generation) {
|
|
13174
13903
|
const request = parseJson(frame.body);
|
|
13175
13904
|
if (request.op === HEALTH_CHECK_OP) {
|
|
13176
|
-
this.handleHealthRequest(frame, sock, generation).catch((
|
|
13905
|
+
this.handleHealthRequest(frame, sock, generation).catch((error2) => {
|
|
13177
13906
|
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
13178
|
-
console.warn("SubcProvider health handler failed after its request was dispatched",
|
|
13907
|
+
console.warn("SubcProvider health handler failed after its request was dispatched", error2);
|
|
13179
13908
|
}
|
|
13180
13909
|
});
|
|
13181
13910
|
return;
|
|
@@ -13183,47 +13912,93 @@ class SubcProvider {
|
|
|
13183
13912
|
if (request.op !== "route.bind") {
|
|
13184
13913
|
throw new SubcProviderError(`unsupported module control request ${request.op ?? "<missing op>"}`);
|
|
13185
13914
|
}
|
|
13915
|
+
const boundChannel = numberField(request.route_channel, "route_channel");
|
|
13916
|
+
const boundEpoch = numberField(request.epoch, "epoch");
|
|
13917
|
+
const stale = this.liveRoutes.get(boundChannel);
|
|
13918
|
+
if (stale) {
|
|
13919
|
+
if (boundEpoch <= stale.epoch) {
|
|
13920
|
+
await this.sendError(frame, "route_rejected", `route.bind epoch ${boundEpoch} does not supersede installed epoch ${stale.epoch} on channel ${boundChannel}`, controlFlags(), sock, generation);
|
|
13921
|
+
return;
|
|
13922
|
+
}
|
|
13923
|
+
this.liveRoutes.delete(stale.channel);
|
|
13924
|
+
this.abortHandle(stale);
|
|
13925
|
+
await this.opts.onRouteGone?.(stale);
|
|
13926
|
+
}
|
|
13927
|
+
const tentative = createRouteHandle(boundChannel, boundEpoch, this.connectionToken);
|
|
13186
13928
|
const bindRequest = {
|
|
13187
|
-
|
|
13929
|
+
handle: tentative,
|
|
13188
13930
|
target: request.target,
|
|
13189
13931
|
identity: request.identity,
|
|
13190
|
-
principal: request.principal
|
|
13932
|
+
principal: request.principal,
|
|
13933
|
+
consumer_capabilities: request.consumer_capabilities
|
|
13191
13934
|
};
|
|
13192
|
-
|
|
13935
|
+
let decision;
|
|
13936
|
+
try {
|
|
13937
|
+
decision = await this.opts.onBind?.(bindRequest);
|
|
13938
|
+
} catch (error2) {
|
|
13939
|
+
try {
|
|
13940
|
+
await this.sendError(frame, "route_rejected", error2 instanceof Error ? error2.message : String(error2), controlFlags(), sock, generation);
|
|
13941
|
+
} finally {
|
|
13942
|
+
await this.opts.onRouteGone?.(tentative);
|
|
13943
|
+
}
|
|
13944
|
+
return;
|
|
13945
|
+
}
|
|
13193
13946
|
const rejection = bindRejection(decision);
|
|
13194
13947
|
if (rejection) {
|
|
13195
|
-
|
|
13948
|
+
try {
|
|
13949
|
+
await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
|
|
13950
|
+
} finally {
|
|
13951
|
+
await this.opts.onRouteGone?.(tentative);
|
|
13952
|
+
}
|
|
13953
|
+
return;
|
|
13954
|
+
}
|
|
13955
|
+
try {
|
|
13956
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Response, controlFlags(), 0, 0, frame.header.corr, encodeJson({ op: "route.bind" })));
|
|
13957
|
+
} catch (error2) {
|
|
13958
|
+
await this.opts.onRouteGone?.(tentative);
|
|
13959
|
+
throw error2;
|
|
13960
|
+
}
|
|
13961
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr) {
|
|
13962
|
+
await this.opts.onRouteGone?.(tentative);
|
|
13196
13963
|
return;
|
|
13197
13964
|
}
|
|
13198
|
-
|
|
13965
|
+
this.liveRoutes.set(tentative.channel, tentative);
|
|
13966
|
+
await this.opts.onBound?.(tentative);
|
|
13199
13967
|
}
|
|
13200
|
-
async handleDataRequest(frame, sock, generation) {
|
|
13201
|
-
const {
|
|
13202
|
-
const key = routeKey(
|
|
13968
|
+
async handleDataRequest(frame, handle, sock, generation) {
|
|
13969
|
+
const { corr, ver } = frame.header;
|
|
13970
|
+
const key = routeKey(handle, corr);
|
|
13203
13971
|
const controller = new AbortController;
|
|
13204
13972
|
this.inflight.set(key, controller);
|
|
13205
|
-
const
|
|
13206
|
-
|
|
13973
|
+
const context = {
|
|
13974
|
+
handle,
|
|
13207
13975
|
signal: controller.signal,
|
|
13208
13976
|
currentEpoch: () => this.connectionEpoch,
|
|
13209
|
-
emit: async (eventBody) => {
|
|
13977
|
+
emit: async (eventBody, options = {}) => {
|
|
13978
|
+
this.assertLiveHandle(handle);
|
|
13210
13979
|
if (controller.signal.aborted)
|
|
13211
13980
|
return;
|
|
13212
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamData,
|
|
13981
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamData, buildFlags(false, options.priority ?? Priority.Interactive, false, options.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, eventBody));
|
|
13213
13982
|
}
|
|
13214
13983
|
};
|
|
13215
13984
|
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
13985
|
+
const dataFlags = buildFlags(false, Priority.Interactive, false);
|
|
13216
13986
|
try {
|
|
13217
|
-
const body = await this.opts.handler(
|
|
13987
|
+
const body = await this.opts.handler(handle, frame.body, context);
|
|
13988
|
+
if (controller.signal.aborted)
|
|
13989
|
+
return;
|
|
13990
|
+
this.assertLiveHandle(handle);
|
|
13218
13991
|
if (body === undefined) {
|
|
13219
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, channel, corr, new Uint8Array(0)));
|
|
13992
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
13220
13993
|
} else if (body instanceof Uint8Array) {
|
|
13221
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, channel, corr, body));
|
|
13994
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, handle.channel, handle.epoch, corr, body));
|
|
13222
13995
|
} else {
|
|
13223
13996
|
throw new SubcProviderError("provider handler must return a Uint8Array or void", "invalid_handler_response");
|
|
13224
13997
|
}
|
|
13225
|
-
} catch (
|
|
13226
|
-
|
|
13998
|
+
} catch (error2) {
|
|
13999
|
+
if (error2 instanceof StaleRouteHandleError || controller.signal.aborted)
|
|
14000
|
+
return;
|
|
14001
|
+
await this.sendError(frame, error2 instanceof SubcProviderError && error2.code ? error2.code : "handler_error", error2 instanceof Error ? error2.message : String(error2), dataFlags, sock, generation);
|
|
13227
14002
|
} finally {
|
|
13228
14003
|
releasePermit();
|
|
13229
14004
|
if (this.inflight.get(key) === controller)
|
|
@@ -13231,8 +14006,8 @@ class SubcProvider {
|
|
|
13231
14006
|
}
|
|
13232
14007
|
}
|
|
13233
14008
|
async handleHealthRequest(frame, sock, generation) {
|
|
13234
|
-
const {
|
|
13235
|
-
const key =
|
|
14009
|
+
const { corr, ver } = frame.header;
|
|
14010
|
+
const key = `control:${generation}:${corr}`;
|
|
13236
14011
|
const controller = new AbortController;
|
|
13237
14012
|
this.inflight.set(key, controller);
|
|
13238
14013
|
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
@@ -13240,14 +14015,14 @@ class SubcProvider {
|
|
|
13240
14015
|
if (controller.signal.aborted)
|
|
13241
14016
|
return;
|
|
13242
14017
|
const report = await this.opts.health();
|
|
13243
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(),
|
|
14018
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(), 0, 0, corr, encodeJson({
|
|
13244
14019
|
op: HEALTH_CHECK_OP,
|
|
13245
14020
|
status: report.status,
|
|
13246
14021
|
...report.detail === undefined ? {} : { detail: report.detail },
|
|
13247
14022
|
...report.metrics === undefined ? {} : { metrics: report.metrics }
|
|
13248
14023
|
})));
|
|
13249
|
-
} catch (
|
|
13250
|
-
await this.sendError(frame,
|
|
14024
|
+
} catch (error2) {
|
|
14025
|
+
await this.sendError(frame, error2 instanceof SubcProviderError && error2.code ? error2.code : "health_error", error2 instanceof Error ? error2.message : String(error2), controlFlags(), sock, generation);
|
|
13251
14026
|
} finally {
|
|
13252
14027
|
releasePermit();
|
|
13253
14028
|
if (this.inflight.get(key) === controller)
|
|
@@ -13255,7 +14030,7 @@ class SubcProvider {
|
|
|
13255
14030
|
}
|
|
13256
14031
|
}
|
|
13257
14032
|
async sendError(frame, code, message, flags, sock, generation) {
|
|
13258
|
-
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.corr, encodeJson({ code, message })));
|
|
14033
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.epoch, frame.header.corr, encodeJson({ code, message })));
|
|
13259
14034
|
}
|
|
13260
14035
|
async sendOn(sock, generation, frame) {
|
|
13261
14036
|
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr)
|
|
@@ -13263,42 +14038,79 @@ class SubcProvider {
|
|
|
13263
14038
|
await sendFrame(sock, frame);
|
|
13264
14039
|
}
|
|
13265
14040
|
handleUnexpectedDrop(sock, generation, cause) {
|
|
14041
|
+
if (this.closeStarted || this.sock !== sock || this.generation !== generation)
|
|
14042
|
+
return;
|
|
13266
14043
|
this.cancelRestoredDebounce();
|
|
13267
14044
|
this.abortGeneration(generation);
|
|
13268
14045
|
this.generation += 1;
|
|
13269
14046
|
sock.close();
|
|
13270
|
-
this.
|
|
13271
|
-
this.scheduleReconnectAfterDrop(cause);
|
|
14047
|
+
this.scheduleReconnectAfterDrop(cause, this.generation, sock);
|
|
13272
14048
|
}
|
|
13273
|
-
scheduleReconnectAfterDrop(
|
|
13274
|
-
if (this.closeStarted
|
|
14049
|
+
scheduleReconnectAfterDrop(cause, generation, droppedSocket) {
|
|
14050
|
+
if (this.closeStarted)
|
|
13275
14051
|
return;
|
|
13276
|
-
const
|
|
13277
|
-
|
|
14052
|
+
const previous = this.reconnecting;
|
|
14053
|
+
if (previous) {
|
|
14054
|
+
if (!this.shouldSupersedeReconnect(previous, generation, droppedSocket))
|
|
14055
|
+
return;
|
|
14056
|
+
previous.superseded = true;
|
|
14057
|
+
previous.socket?.close();
|
|
14058
|
+
}
|
|
14059
|
+
const cycle = {
|
|
14060
|
+
generation,
|
|
14061
|
+
socket: null,
|
|
14062
|
+
socketDied: false,
|
|
14063
|
+
superseded: false
|
|
14064
|
+
};
|
|
14065
|
+
this.reconnecting = cycle;
|
|
14066
|
+
this.enqueueConnectionState({ state: "down", cause });
|
|
14067
|
+
this.reconnectWithRetry(cycle).catch((err) => {
|
|
14068
|
+
if (this.isCurrentReconnect(cycle) && !this.closeStarted) {
|
|
13278
14069
|
this.failFatal(err instanceof Error ? err : new SubcProviderError(String(err)));
|
|
14070
|
+
}
|
|
13279
14071
|
}).finally(() => {
|
|
13280
|
-
if (this.
|
|
14072
|
+
if (this.isCurrentReconnect(cycle))
|
|
13281
14073
|
this.reconnecting = null;
|
|
13282
14074
|
});
|
|
13283
|
-
this.reconnecting = promise;
|
|
13284
14075
|
}
|
|
13285
|
-
async reconnectWithRetry(
|
|
14076
|
+
async reconnectWithRetry(cycle) {
|
|
13286
14077
|
let attempt = 0;
|
|
13287
14078
|
let delay = this.opts.reconnectBackoff.baseMs;
|
|
13288
14079
|
for (;; ) {
|
|
14080
|
+
if (!this.isCurrentReconnect(cycle))
|
|
14081
|
+
return;
|
|
13289
14082
|
if (this.closeStarted)
|
|
13290
14083
|
throw new SubcProviderError("provider closed");
|
|
14084
|
+
cycle.socket = null;
|
|
14085
|
+
cycle.socketDied = false;
|
|
13291
14086
|
attempt += 1;
|
|
13292
|
-
this.
|
|
14087
|
+
this.enqueueReconnectState(cycle, attempt);
|
|
13293
14088
|
try {
|
|
13294
|
-
const opened = await SubcProvider.openConnection(this.opts)
|
|
13295
|
-
|
|
14089
|
+
const opened = await SubcProvider.openConnection(this.opts, (sock) => {
|
|
14090
|
+
if (!this.isCurrentReconnect(cycle)) {
|
|
14091
|
+
sock.close();
|
|
14092
|
+
return;
|
|
14093
|
+
}
|
|
14094
|
+
cycle.socket = sock;
|
|
14095
|
+
});
|
|
14096
|
+
if (!this.isCurrentReconnect(cycle) || this.closeStarted) {
|
|
13296
14097
|
opened.sock.close();
|
|
13297
|
-
|
|
14098
|
+
if (this.closeStarted)
|
|
14099
|
+
throw new SubcProviderError("provider closed");
|
|
14100
|
+
return;
|
|
13298
14101
|
}
|
|
13299
|
-
this.replaceConnection(opened);
|
|
14102
|
+
const epoch = this.replaceConnection(opened, cycle.generation);
|
|
14103
|
+
this.reconnecting = null;
|
|
14104
|
+
if (this.reconnecting !== null) {
|
|
14105
|
+
throw new SubcProviderError("reconnect state must clear before restored", "reconnect_state");
|
|
14106
|
+
}
|
|
14107
|
+
this.scheduleRestored(cycle.generation, epoch);
|
|
13300
14108
|
return;
|
|
13301
14109
|
} catch (err) {
|
|
14110
|
+
if (!this.isCurrentReconnect(cycle))
|
|
14111
|
+
return;
|
|
14112
|
+
if (cycle.socket)
|
|
14113
|
+
cycle.socketDied = true;
|
|
13302
14114
|
if (this.closeStarted)
|
|
13303
14115
|
throw err;
|
|
13304
14116
|
if (!isProviderReconnectTransient(err))
|
|
@@ -13308,16 +14120,29 @@ class SubcProvider {
|
|
|
13308
14120
|
}
|
|
13309
14121
|
}
|
|
13310
14122
|
}
|
|
13311
|
-
|
|
14123
|
+
isCurrentReconnect(cycle) {
|
|
14124
|
+
return !cycle.superseded && this.reconnecting === cycle && this.generation === cycle.generation;
|
|
14125
|
+
}
|
|
14126
|
+
shouldSupersedeReconnect(cycle, generation, droppedSocket) {
|
|
14127
|
+
return cycle.generation < generation || cycle.socketDied || cycle.socket === droppedSocket;
|
|
14128
|
+
}
|
|
14129
|
+
enqueueReconnectState(cycle, attempt) {
|
|
14130
|
+
if (!this.isCurrentReconnect(cycle))
|
|
14131
|
+
return;
|
|
14132
|
+
this.enqueueConnectionState({ state: "reconnecting", attempt }, cycle.generation, cycle);
|
|
14133
|
+
}
|
|
14134
|
+
replaceConnection(opened, generation) {
|
|
13312
14135
|
this.sock.close();
|
|
13313
14136
|
this.sock = opened.sock;
|
|
13314
14137
|
this.currentConn = opened.conn;
|
|
13315
14138
|
this.storage = opened.ack.storage;
|
|
13316
14139
|
this.closedErr = null;
|
|
13317
14140
|
this.connectionEpoch += 1;
|
|
13318
|
-
|
|
14141
|
+
this.connectionToken = newConnectionToken();
|
|
14142
|
+
this.liveRoutes.clear();
|
|
14143
|
+
this.nextCorr = 1n;
|
|
13319
14144
|
this.readLoop(opened.sock, generation);
|
|
13320
|
-
|
|
14145
|
+
return this.connectionEpoch;
|
|
13321
14146
|
}
|
|
13322
14147
|
scheduleRestored(generation, epoch) {
|
|
13323
14148
|
if (!this.opts.onConnectionState)
|
|
@@ -13325,7 +14150,7 @@ class SubcProvider {
|
|
|
13325
14150
|
const token = ++this.restoredDebounceToken;
|
|
13326
14151
|
this.opts.sleep(this.opts.restoredDebounceMs).then(() => {
|
|
13327
14152
|
if (token === this.restoredDebounceToken && !this.closeStarted && this.sock && this.generation === generation && this.connectionEpoch === epoch) {
|
|
13328
|
-
this.enqueueConnectionState({ state: "restored", epoch });
|
|
14153
|
+
this.enqueueConnectionState({ state: "restored", epoch }, generation);
|
|
13329
14154
|
}
|
|
13330
14155
|
}).catch((err) => {
|
|
13331
14156
|
if (token === this.restoredDebounceToken && !this.closeStarted) {
|
|
@@ -13336,10 +14161,10 @@ class SubcProvider {
|
|
|
13336
14161
|
cancelRestoredDebounce() {
|
|
13337
14162
|
this.restoredDebounceToken += 1;
|
|
13338
14163
|
}
|
|
13339
|
-
enqueueConnectionState(event) {
|
|
14164
|
+
enqueueConnectionState(event, generation, reconnect) {
|
|
13340
14165
|
if (!this.opts.onConnectionState)
|
|
13341
14166
|
return;
|
|
13342
|
-
this.stateQueue.push(event);
|
|
14167
|
+
this.stateQueue.push({ event, generation, reconnect });
|
|
13343
14168
|
if (!this.drainingStateQueue)
|
|
13344
14169
|
this.drainConnectionStateQueue();
|
|
13345
14170
|
}
|
|
@@ -13349,7 +14174,12 @@ class SubcProvider {
|
|
|
13349
14174
|
this.drainingStateQueue = true;
|
|
13350
14175
|
try {
|
|
13351
14176
|
while (this.stateQueue.length > 0) {
|
|
13352
|
-
const
|
|
14177
|
+
const queued = this.stateQueue[0];
|
|
14178
|
+
if (this.closeStarted || queued.generation !== undefined && queued.generation !== this.generation || queued.reconnect?.superseded) {
|
|
14179
|
+
this.stateQueue.shift();
|
|
14180
|
+
continue;
|
|
14181
|
+
}
|
|
14182
|
+
const { event } = queued;
|
|
13353
14183
|
try {
|
|
13354
14184
|
await this.opts.onConnectionState?.(event);
|
|
13355
14185
|
this.stateQueue.shift();
|
|
@@ -13369,6 +14199,24 @@ class SubcProvider {
|
|
|
13369
14199
|
this.drainConnectionStateQueue();
|
|
13370
14200
|
}
|
|
13371
14201
|
}
|
|
14202
|
+
isLiveHandle(handle) {
|
|
14203
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
14204
|
+
}
|
|
14205
|
+
assertLiveHandle(handle) {
|
|
14206
|
+
if (!this.isLiveHandle(handle))
|
|
14207
|
+
throw new StaleRouteHandleError(handle);
|
|
14208
|
+
}
|
|
14209
|
+
allocateCorr() {
|
|
14210
|
+
const maximum = 0xffffffffffffffffn;
|
|
14211
|
+
if (this.nextCorr > maximum) {
|
|
14212
|
+
const error2 = new SubcProviderError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
14213
|
+
this.handleUnexpectedDrop(this.sock, this.generation, error2);
|
|
14214
|
+
throw error2;
|
|
14215
|
+
}
|
|
14216
|
+
const corr = this.nextCorr;
|
|
14217
|
+
this.nextCorr += 1n;
|
|
14218
|
+
return corr;
|
|
14219
|
+
}
|
|
13372
14220
|
failFatal(err) {
|
|
13373
14221
|
if (!this.closedErr)
|
|
13374
14222
|
this.closedErr = err;
|
|
@@ -13382,8 +14230,16 @@ class SubcProvider {
|
|
|
13382
14230
|
this.resolveClosed();
|
|
13383
14231
|
}
|
|
13384
14232
|
}
|
|
13385
|
-
function routeKey(
|
|
13386
|
-
return `${
|
|
14233
|
+
function routeKey(handle, corr) {
|
|
14234
|
+
return `${handle.channel}:${handle.epoch}:${corr}`;
|
|
14235
|
+
}
|
|
14236
|
+
function providerErrorFromFrame(frame) {
|
|
14237
|
+
try {
|
|
14238
|
+
const body = parseJson(frame.body);
|
|
14239
|
+
return new SubcProviderError(body.message ?? "subc error", body.code);
|
|
14240
|
+
} catch {
|
|
14241
|
+
return new SubcProviderError(Buffer2.from(frame.body).toString("utf8") || "subc error");
|
|
14242
|
+
}
|
|
13387
14243
|
}
|
|
13388
14244
|
function launchNonce(opts) {
|
|
13389
14245
|
const nonce = opts.launchNonce ?? process.env[SUBC_LAUNCH_NONCE_ENV2];
|
|
@@ -13399,6 +14255,7 @@ function normalizeProviderConnectOptions(opts) {
|
|
|
13399
14255
|
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
13400
14256
|
controlOps: opts.controlOps,
|
|
13401
14257
|
onBind: opts.onBind,
|
|
14258
|
+
onBound: opts.onBound,
|
|
13402
14259
|
onRouteGone: opts.onRouteGone,
|
|
13403
14260
|
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
13404
14261
|
sleep: opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))),
|
|
@@ -13416,7 +14273,7 @@ function normalizedControlOps(controlOps) {
|
|
|
13416
14273
|
}
|
|
13417
14274
|
function buildHelloFrame(opts) {
|
|
13418
14275
|
const nonce = launchNonce(opts);
|
|
13419
|
-
return buildFrame(FrameType.Hello, controlFlags(), 0, HELLO_CORR, encodeJson({
|
|
14276
|
+
return buildFrame(FrameType.Hello, controlFlags(), 0, 0, HELLO_CORR, encodeJson({
|
|
13420
14277
|
manifest: normalizeManifest(opts.manifest),
|
|
13421
14278
|
protocol_ver: PROTOCOL_VERSION,
|
|
13422
14279
|
control_ops: normalizedControlOps(opts.controlOps),
|
|
@@ -13455,14 +14312,17 @@ async function sendFrame(sock, frame) {
|
|
|
13455
14312
|
await sock.write(encodeFrame(frame), Date.now() + WRITE_TIMEOUT_MS);
|
|
13456
14313
|
}
|
|
13457
14314
|
async function expectHelloAck(sock, deadline) {
|
|
13458
|
-
const
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
14315
|
+
const frame = await sock.readFrame(deadline, deadline);
|
|
14316
|
+
switch (frame.header.ty) {
|
|
14317
|
+
case FrameType.HelloAck: {
|
|
14318
|
+
const ack = parseJson(frame.body);
|
|
14319
|
+
if (ack.negotiated_ver !== PROTOCOL_VERSION) {
|
|
14320
|
+
throw new SubcProviderError(`subc negotiated protocol ${ack.negotiated_ver}; expected exactly ${PROTOCOL_VERSION}`, "unsupported_version");
|
|
14321
|
+
}
|
|
14322
|
+
return ack;
|
|
14323
|
+
}
|
|
13464
14324
|
case FrameType.Error: {
|
|
13465
|
-
const error2 = parseJson(body);
|
|
14325
|
+
const error2 = parseJson(frame.body);
|
|
13466
14326
|
throw new SubcProviderError(`subc rejected HELLO: ${error2.code ?? "unknown"} — ${error2.message ?? "subc error"}`, error2.code);
|
|
13467
14327
|
}
|
|
13468
14328
|
default:
|
|
@@ -13617,15 +14477,17 @@ class BgSubscription {
|
|
|
13617
14477
|
identity;
|
|
13618
14478
|
acquireClient;
|
|
13619
14479
|
dropClient;
|
|
14480
|
+
consumerIdentity;
|
|
13620
14481
|
onNudge;
|
|
13621
14482
|
sleep;
|
|
13622
14483
|
stopped = false;
|
|
13623
14484
|
current = null;
|
|
13624
14485
|
loop;
|
|
13625
|
-
constructor(identity, acquireClient, dropClient, onNudge, sleep2) {
|
|
14486
|
+
constructor(identity, acquireClient, dropClient, consumerIdentity, onNudge, sleep2) {
|
|
13626
14487
|
this.identity = identity;
|
|
13627
14488
|
this.acquireClient = acquireClient;
|
|
13628
14489
|
this.dropClient = dropClient;
|
|
14490
|
+
this.consumerIdentity = consumerIdentity;
|
|
13629
14491
|
this.onNudge = onNudge;
|
|
13630
14492
|
this.sleep = sleep2;
|
|
13631
14493
|
this.loop = this.run();
|
|
@@ -13654,9 +14516,9 @@ class BgSubscription {
|
|
|
13654
14516
|
}
|
|
13655
14517
|
if (this.stopped)
|
|
13656
14518
|
return;
|
|
13657
|
-
let
|
|
14519
|
+
let route;
|
|
13658
14520
|
try {
|
|
13659
|
-
|
|
14521
|
+
route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, this.identity, { consumerIdentity: this.consumerIdentity });
|
|
13660
14522
|
} catch (err) {
|
|
13661
14523
|
if (isConsumerReconnectTransient(err))
|
|
13662
14524
|
this.dropClient(client);
|
|
@@ -13664,12 +14526,12 @@ class BgSubscription {
|
|
|
13664
14526
|
continue;
|
|
13665
14527
|
}
|
|
13666
14528
|
if (this.stopped) {
|
|
13667
|
-
safeCloseRoute(client,
|
|
14529
|
+
safeCloseRoute(client, route);
|
|
13668
14530
|
return;
|
|
13669
14531
|
}
|
|
13670
14532
|
const subscribedAt = Date.now();
|
|
13671
14533
|
try {
|
|
13672
|
-
const sub = client.subscribe(
|
|
14534
|
+
const sub = client.subscribe(route, { op: "bg_events" }, () => {
|
|
13673
14535
|
if (!this.stopped)
|
|
13674
14536
|
this.onNudge();
|
|
13675
14537
|
});
|
|
@@ -13689,7 +14551,7 @@ class BgSubscription {
|
|
|
13689
14551
|
attempt = 0;
|
|
13690
14552
|
} finally {
|
|
13691
14553
|
this.current = null;
|
|
13692
|
-
safeCloseRoute(client,
|
|
14554
|
+
safeCloseRoute(client, route);
|
|
13693
14555
|
}
|
|
13694
14556
|
await this.backoff(attempt++);
|
|
13695
14557
|
}
|
|
@@ -13702,12 +14564,12 @@ class BgSubscription {
|
|
|
13702
14564
|
function isRecord(value) {
|
|
13703
14565
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13704
14566
|
}
|
|
13705
|
-
function
|
|
13706
|
-
return err instanceof SubcError && err.code === "unknown_channel";
|
|
14567
|
+
function isRouteProvenAbsentError(err) {
|
|
14568
|
+
return err instanceof SubcError && err.code === "unknown_channel" || err instanceof StaleRouteHandleError;
|
|
13707
14569
|
}
|
|
13708
|
-
function safeCloseRoute(client,
|
|
14570
|
+
function safeCloseRoute(client, route) {
|
|
13709
14571
|
try {
|
|
13710
|
-
client.closeRouteChannel(
|
|
14572
|
+
client.closeRouteChannel(route).catch(() => {
|
|
13711
14573
|
return;
|
|
13712
14574
|
});
|
|
13713
14575
|
} catch {}
|
|
@@ -13794,6 +14656,7 @@ class SubcTransportPool {
|
|
|
13794
14656
|
harness;
|
|
13795
14657
|
connectionFile;
|
|
13796
14658
|
handshakeTimeoutMs;
|
|
14659
|
+
consumerIdentity;
|
|
13797
14660
|
connectFn;
|
|
13798
14661
|
onBgEventsNudge;
|
|
13799
14662
|
bgBackoffSleep;
|
|
@@ -13807,6 +14670,7 @@ class SubcTransportPool {
|
|
|
13807
14670
|
this.connectionFile = options.connectionFile;
|
|
13808
14671
|
this.harness = options.harness;
|
|
13809
14672
|
this.handshakeTimeoutMs = options.handshakeTimeoutMs;
|
|
14673
|
+
this.consumerIdentity = options.consumerIdentity;
|
|
13810
14674
|
this.connectFn = options.connect ?? ((opts) => SubcClient.connect(opts));
|
|
13811
14675
|
this.onBgEventsNudge = options.onBgEventsNudge;
|
|
13812
14676
|
this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
@@ -13829,6 +14693,11 @@ class SubcTransportPool {
|
|
|
13829
14693
|
return null;
|
|
13830
14694
|
return this.transports.get(key) ?? null;
|
|
13831
14695
|
}
|
|
14696
|
+
activeBridges() {
|
|
14697
|
+
if (!this.client || this.shuttingDown)
|
|
14698
|
+
return [];
|
|
14699
|
+
return [...this.transports.values()];
|
|
14700
|
+
}
|
|
13832
14701
|
async toolCall(projectRoot, runtime, name, rawArgs = {}, options) {
|
|
13833
14702
|
return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
|
|
13834
14703
|
}
|
|
@@ -13859,7 +14728,7 @@ class SubcTransportPool {
|
|
|
13859
14728
|
throw new RouteTornDownError("subc session closed");
|
|
13860
14729
|
}
|
|
13861
14730
|
try {
|
|
13862
|
-
const opened = await this.
|
|
14731
|
+
const opened = await this.routeHandle(client, identity, record);
|
|
13863
14732
|
if (!this.isCurrentSession(key, record)) {
|
|
13864
14733
|
throw new RouteTornDownError("subc session closed");
|
|
13865
14734
|
}
|
|
@@ -13885,29 +14754,29 @@ class SubcTransportPool {
|
|
|
13885
14754
|
if (isConsumerReconnectTransient(err)) {
|
|
13886
14755
|
this.transportFailures = 0;
|
|
13887
14756
|
this.dropClient(client);
|
|
13888
|
-
} else if (!
|
|
14757
|
+
} else if (!isRouteProvenAbsentError(err) && ++this.transportFailures >= MAX_CONSECUTIVE_TRANSPORT_FAILURES) {
|
|
13889
14758
|
this.transportFailures = 0;
|
|
13890
14759
|
this.dropClient(client);
|
|
13891
14760
|
}
|
|
13892
14761
|
}
|
|
13893
14762
|
};
|
|
13894
|
-
const requestOnRoute = async (
|
|
13895
|
-
const reply = await client.request(
|
|
14763
|
+
const requestOnRoute = async (route2) => {
|
|
14764
|
+
const reply = await client.request(route2, body, { timeoutMs, onProgress });
|
|
13896
14765
|
if (this.isCurrentSession(key, record) && this.client === client) {
|
|
13897
14766
|
this.transportFailures = 0;
|
|
13898
14767
|
}
|
|
13899
14768
|
this.ensureBgSubscription(identity, record);
|
|
13900
14769
|
return reply;
|
|
13901
14770
|
};
|
|
13902
|
-
let {
|
|
14771
|
+
let { route, entry } = await openRoute();
|
|
13903
14772
|
try {
|
|
13904
|
-
return await requestOnRoute(
|
|
14773
|
+
return await requestOnRoute(route);
|
|
13905
14774
|
} catch (err) {
|
|
13906
|
-
if (
|
|
14775
|
+
if (isRouteProvenAbsentError(err) && this.isCurrentSession(key, record) && this.client === client) {
|
|
13907
14776
|
clearRouteEntry(entry);
|
|
13908
|
-
({
|
|
14777
|
+
({ route, entry } = await openRoute());
|
|
13909
14778
|
try {
|
|
13910
|
-
return await requestOnRoute(
|
|
14779
|
+
return await requestOnRoute(route);
|
|
13911
14780
|
} catch (retryErr) {
|
|
13912
14781
|
handleRequestFailure(retryErr, entry);
|
|
13913
14782
|
throw retryErr;
|
|
@@ -13949,24 +14818,26 @@ class SubcTransportPool {
|
|
|
13949
14818
|
});
|
|
13950
14819
|
return this.connecting;
|
|
13951
14820
|
}
|
|
13952
|
-
async
|
|
14821
|
+
async routeHandle(client, identity, record) {
|
|
13953
14822
|
const key = identityKey(identity);
|
|
13954
14823
|
const existing = record.routeEntry;
|
|
13955
|
-
if (existing?.
|
|
13956
|
-
return {
|
|
14824
|
+
if (existing?.handle != null)
|
|
14825
|
+
return { route: existing.handle, entry: existing };
|
|
13957
14826
|
if (existing?.opening)
|
|
13958
|
-
return {
|
|
13959
|
-
const entry = { client, opening: null,
|
|
13960
|
-
const opening = client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity
|
|
14827
|
+
return { route: await existing.opening, entry: existing };
|
|
14828
|
+
const entry = { client, opening: null, handle: null, closed: false };
|
|
14829
|
+
const opening = client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
|
|
14830
|
+
consumerIdentity: this.consumerIdentity
|
|
14831
|
+
}).then((route) => {
|
|
13961
14832
|
if (!this.isCurrentSession(key, record) || record.routeEntry !== entry || entry.closed || this.client !== client) {
|
|
13962
|
-
safeCloseRoute(client,
|
|
14833
|
+
safeCloseRoute(client, route);
|
|
13963
14834
|
if (record.routeEntry === entry)
|
|
13964
14835
|
record.routeEntry = null;
|
|
13965
14836
|
throw new RouteTornDownError("subc route opened after teardown");
|
|
13966
14837
|
}
|
|
13967
|
-
entry.
|
|
14838
|
+
entry.handle = route;
|
|
13968
14839
|
entry.opening = null;
|
|
13969
|
-
return
|
|
14840
|
+
return route;
|
|
13970
14841
|
}).catch((err) => {
|
|
13971
14842
|
const current = this.isCurrentSession(key, record);
|
|
13972
14843
|
if (record.routeEntry === entry) {
|
|
@@ -13980,7 +14851,7 @@ class SubcTransportPool {
|
|
|
13980
14851
|
});
|
|
13981
14852
|
entry.opening = opening;
|
|
13982
14853
|
record.routeEntry = entry;
|
|
13983
|
-
return {
|
|
14854
|
+
return { route: await opening, entry };
|
|
13984
14855
|
}
|
|
13985
14856
|
ensureBgSubscription(identity, record) {
|
|
13986
14857
|
if (this.shuttingDown || !this.onBgEventsNudge)
|
|
@@ -13991,7 +14862,7 @@ class SubcTransportPool {
|
|
|
13991
14862
|
if (record.bgSub)
|
|
13992
14863
|
return;
|
|
13993
14864
|
const onNudge = () => this.onBgEventsNudge?.(identity.project_root, identity.session);
|
|
13994
|
-
const sub = new BgSubscription(identity, () => this.ensureClient(), (client) => this.dropClient(client), onNudge, this.bgBackoffSleep);
|
|
14865
|
+
const sub = new BgSubscription(identity, () => this.ensureClient(), (client) => this.dropClient(client), this.consumerIdentity, onNudge, this.bgBackoffSleep);
|
|
13995
14866
|
record.bgSub = sub;
|
|
13996
14867
|
}
|
|
13997
14868
|
dropClient(client) {
|
|
@@ -14012,9 +14883,13 @@ class SubcTransportPool {
|
|
|
14012
14883
|
}
|
|
14013
14884
|
}
|
|
14014
14885
|
setConfigureOverride(_key, _value) {}
|
|
14886
|
+
async reconfigure(_projectRoot, _overrides) {}
|
|
14015
14887
|
async replaceBinary(path2) {
|
|
14016
14888
|
return path2;
|
|
14017
14889
|
}
|
|
14890
|
+
isShutdown() {
|
|
14891
|
+
return this.shuttingDown;
|
|
14892
|
+
}
|
|
14018
14893
|
async shutdown() {
|
|
14019
14894
|
this.shuttingDown = true;
|
|
14020
14895
|
const subs = [];
|
|
@@ -14038,10 +14913,10 @@ class SubcTransportPool {
|
|
|
14038
14913
|
this.transports.clear();
|
|
14039
14914
|
await Promise.allSettled(subs.map((sub) => sub.stop()));
|
|
14040
14915
|
await Promise.allSettled(entries.map(async (entry) => {
|
|
14041
|
-
if (entry.
|
|
14916
|
+
if (entry.handle == null)
|
|
14042
14917
|
return;
|
|
14043
14918
|
try {
|
|
14044
|
-
await entry.client.closeRouteChannel(entry.
|
|
14919
|
+
await entry.client.closeRouteChannel(entry.handle);
|
|
14045
14920
|
} catch {}
|
|
14046
14921
|
}));
|
|
14047
14922
|
if (client) {
|
|
@@ -14070,9 +14945,9 @@ class SubcTransportPool {
|
|
|
14070
14945
|
entry.closed = true;
|
|
14071
14946
|
if (sub)
|
|
14072
14947
|
await sub.stop();
|
|
14073
|
-
if (entry?.
|
|
14948
|
+
if (entry?.handle != null) {
|
|
14074
14949
|
try {
|
|
14075
|
-
await entry.client.closeRouteChannel(entry.
|
|
14950
|
+
await entry.client.closeRouteChannel(entry.handle);
|
|
14076
14951
|
} catch {}
|
|
14077
14952
|
}
|
|
14078
14953
|
}
|
|
@@ -14149,18 +15024,25 @@ function collectStructuredExtras(response) {
|
|
|
14149
15024
|
return stringifyData(extras);
|
|
14150
15025
|
}
|
|
14151
15026
|
// ../aft-bridge/dist/transport-factory.js
|
|
14152
|
-
import { homedir as
|
|
14153
|
-
import { isAbsolute as isAbsolute5, join as
|
|
15027
|
+
import { homedir as homedir11 } from "node:os";
|
|
15028
|
+
import { isAbsolute as isAbsolute5, join as join10 } from "node:path";
|
|
14154
15029
|
function resolveConnectionFilePath(raw) {
|
|
14155
15030
|
const trimmed = raw.trim();
|
|
14156
15031
|
if (trimmed.startsWith("~")) {
|
|
14157
|
-
return
|
|
15032
|
+
return join10(homedir11(), trimmed.slice(1).replace(/^[/\\]/, ""));
|
|
14158
15033
|
}
|
|
14159
15034
|
if (isAbsolute5(trimmed))
|
|
14160
15035
|
return trimmed;
|
|
14161
|
-
return
|
|
15036
|
+
return join10(homedir11(), trimmed);
|
|
14162
15037
|
}
|
|
14163
15038
|
async function createAftTransportPool(opts) {
|
|
15039
|
+
let binaryPath = opts.binaryPath;
|
|
15040
|
+
const createPool = () => createConcreteAftTransportPool({ ...opts, binaryPath });
|
|
15041
|
+
return new RevivableTransportPool(await createPool(), createPool, (path2) => {
|
|
15042
|
+
binaryPath = path2;
|
|
15043
|
+
});
|
|
15044
|
+
}
|
|
15045
|
+
async function createConcreteAftTransportPool(opts) {
|
|
14164
15046
|
const raw = opts.subcConnectionFile?.trim();
|
|
14165
15047
|
if (raw && raw.length > 0) {
|
|
14166
15048
|
const connectionFile = resolveConnectionFilePath(raw);
|
|
@@ -14171,18 +15053,17 @@ async function createAftTransportPool(opts) {
|
|
|
14171
15053
|
return new SubcTransportPool({
|
|
14172
15054
|
connectionFile,
|
|
14173
15055
|
harness: opts.harness,
|
|
15056
|
+
consumerIdentity: opts.subcConsumerIdentity,
|
|
14174
15057
|
onBgEventsNudge: opts.onBgEventsNudge
|
|
14175
15058
|
});
|
|
14176
15059
|
}
|
|
14177
15060
|
return new BridgePool(opts.binaryPath, opts.poolOptions, opts.configOverrides);
|
|
14178
15061
|
}
|
|
14179
15062
|
// src/logger.ts
|
|
14180
|
-
import * as fs3 from "node:fs";
|
|
14181
|
-
import * as os2 from "node:os";
|
|
14182
|
-
import * as path2 from "node:path";
|
|
14183
15063
|
var TAG = "[aft-pi]";
|
|
14184
15064
|
var isTestEnv = process.env.BUN_TEST === "1" || false;
|
|
14185
|
-
var logFile =
|
|
15065
|
+
var logFile = resolveAftLogPath(isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
|
|
15066
|
+
var fileSink = new RotatingLogSink(logFile);
|
|
14186
15067
|
var useStderr = process.env.AFT_LOG_STDERR === "1";
|
|
14187
15068
|
var buffer = [];
|
|
14188
15069
|
var flushTimer = null;
|
|
@@ -14197,7 +15078,7 @@ function flush() {
|
|
|
14197
15078
|
if (useStderr) {
|
|
14198
15079
|
process.stderr.write(data);
|
|
14199
15080
|
} else {
|
|
14200
|
-
|
|
15081
|
+
fileSink.append(data);
|
|
14201
15082
|
}
|
|
14202
15083
|
} catch {}
|
|
14203
15084
|
}
|
|
@@ -14910,7 +15791,7 @@ import {
|
|
|
14910
15791
|
// package.json
|
|
14911
15792
|
var package_default = {
|
|
14912
15793
|
name: "@cortexkit/aft-pi",
|
|
14913
|
-
version: "0.
|
|
15794
|
+
version: "0.47.1",
|
|
14914
15795
|
type: "module",
|
|
14915
15796
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
14916
15797
|
main: "dist/index.js",
|
|
@@ -14933,19 +15814,19 @@ var package_default = {
|
|
|
14933
15814
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
14934
15815
|
},
|
|
14935
15816
|
dependencies: {
|
|
14936
|
-
"@cortexkit/aft-bridge": "0.
|
|
15817
|
+
"@cortexkit/aft-bridge": "0.47.1",
|
|
14937
15818
|
"comment-json": "^5.0.0",
|
|
14938
15819
|
diff: "^8.0.4",
|
|
14939
15820
|
typebox: "1.1.38",
|
|
14940
15821
|
zod: "^4.4.3"
|
|
14941
15822
|
},
|
|
14942
15823
|
optionalDependencies: {
|
|
14943
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
14944
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
14945
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
14946
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
14947
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
14948
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
15824
|
+
"@cortexkit/aft-darwin-arm64": "0.47.1",
|
|
15825
|
+
"@cortexkit/aft-darwin-x64": "0.47.1",
|
|
15826
|
+
"@cortexkit/aft-linux-arm64": "0.47.1",
|
|
15827
|
+
"@cortexkit/aft-linux-x64": "0.47.1",
|
|
15828
|
+
"@cortexkit/aft-win32-arm64": "0.47.1",
|
|
15829
|
+
"@cortexkit/aft-win32-x64": "0.47.1"
|
|
14949
15830
|
},
|
|
14950
15831
|
devDependencies: {
|
|
14951
15832
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -15176,9 +16057,15 @@ function formatStatusDialogMessage(status) {
|
|
|
15176
16057
|
}
|
|
15177
16058
|
|
|
15178
16059
|
// src/tools/_shared.ts
|
|
16060
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
15179
16061
|
import { Type } from "typebox";
|
|
15180
|
-
var optionalInt = (
|
|
16062
|
+
var optionalInt = (min, max, description = "(integer)") => Type.Optional(Type.Union([Type.Integer({ minimum: min, maximum: max }), Type.String()], {
|
|
16063
|
+
description
|
|
16064
|
+
}));
|
|
15181
16065
|
function bridgeFor(ctx, cwd) {
|
|
16066
|
+
if (!existsSync7(cwd)) {
|
|
16067
|
+
throw new Error(`project directory no longer exists: ${cwd} (stale restored session?)`);
|
|
16068
|
+
}
|
|
15182
16069
|
return ctx.pool.getBridge(cwd);
|
|
15183
16070
|
}
|
|
15184
16071
|
function resolveSessionId(extCtx) {
|
|
@@ -15534,7 +16421,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
15534
16421
|
}
|
|
15535
16422
|
|
|
15536
16423
|
// src/config.ts
|
|
15537
|
-
import { existsSync as
|
|
16424
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
15538
16425
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
15539
16426
|
|
|
15540
16427
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
@@ -16301,10 +17188,10 @@ function mergeDefs(...defs) {
|
|
|
16301
17188
|
function cloneDef(schema) {
|
|
16302
17189
|
return mergeDefs(schema._zod.def);
|
|
16303
17190
|
}
|
|
16304
|
-
function getElementAtPath(obj,
|
|
16305
|
-
if (!
|
|
17191
|
+
function getElementAtPath(obj, path2) {
|
|
17192
|
+
if (!path2)
|
|
16306
17193
|
return obj;
|
|
16307
|
-
return
|
|
17194
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
16308
17195
|
}
|
|
16309
17196
|
function promiseAllObject(promisesObj) {
|
|
16310
17197
|
const keys = Object.keys(promisesObj);
|
|
@@ -16712,11 +17599,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
16712
17599
|
}
|
|
16713
17600
|
return false;
|
|
16714
17601
|
}
|
|
16715
|
-
function prefixIssues(
|
|
17602
|
+
function prefixIssues(path2, issues) {
|
|
16716
17603
|
return issues.map((iss) => {
|
|
16717
17604
|
var _a2;
|
|
16718
17605
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
16719
|
-
iss.path.unshift(
|
|
17606
|
+
iss.path.unshift(path2);
|
|
16720
17607
|
return iss;
|
|
16721
17608
|
});
|
|
16722
17609
|
}
|
|
@@ -16863,16 +17750,16 @@ function flattenError(error3, mapper = (issue2) => issue2.message) {
|
|
|
16863
17750
|
}
|
|
16864
17751
|
function formatError(error3, mapper = (issue2) => issue2.message) {
|
|
16865
17752
|
const fieldErrors = { _errors: [] };
|
|
16866
|
-
const processError = (error4,
|
|
17753
|
+
const processError = (error4, path2 = []) => {
|
|
16867
17754
|
for (const issue2 of error4.issues) {
|
|
16868
17755
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
16869
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
17756
|
+
issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
|
|
16870
17757
|
} else if (issue2.code === "invalid_key") {
|
|
16871
|
-
processError({ issues: issue2.issues }, [...
|
|
17758
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
16872
17759
|
} else if (issue2.code === "invalid_element") {
|
|
16873
|
-
processError({ issues: issue2.issues }, [...
|
|
17760
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
16874
17761
|
} else {
|
|
16875
|
-
const fullpath = [...
|
|
17762
|
+
const fullpath = [...path2, ...issue2.path];
|
|
16876
17763
|
if (fullpath.length === 0) {
|
|
16877
17764
|
fieldErrors._errors.push(mapper(issue2));
|
|
16878
17765
|
} else {
|
|
@@ -16899,17 +17786,17 @@ function formatError(error3, mapper = (issue2) => issue2.message) {
|
|
|
16899
17786
|
}
|
|
16900
17787
|
function treeifyError(error3, mapper = (issue2) => issue2.message) {
|
|
16901
17788
|
const result = { errors: [] };
|
|
16902
|
-
const processError = (error4,
|
|
17789
|
+
const processError = (error4, path2 = []) => {
|
|
16903
17790
|
var _a2, _b;
|
|
16904
17791
|
for (const issue2 of error4.issues) {
|
|
16905
17792
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
16906
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
17793
|
+
issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
|
|
16907
17794
|
} else if (issue2.code === "invalid_key") {
|
|
16908
|
-
processError({ issues: issue2.issues }, [...
|
|
17795
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
16909
17796
|
} else if (issue2.code === "invalid_element") {
|
|
16910
|
-
processError({ issues: issue2.issues }, [...
|
|
17797
|
+
processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
|
|
16911
17798
|
} else {
|
|
16912
|
-
const fullpath = [...
|
|
17799
|
+
const fullpath = [...path2, ...issue2.path];
|
|
16913
17800
|
if (fullpath.length === 0) {
|
|
16914
17801
|
result.errors.push(mapper(issue2));
|
|
16915
17802
|
continue;
|
|
@@ -16941,8 +17828,8 @@ function treeifyError(error3, mapper = (issue2) => issue2.message) {
|
|
|
16941
17828
|
}
|
|
16942
17829
|
function toDotPath(_path) {
|
|
16943
17830
|
const segs = [];
|
|
16944
|
-
const
|
|
16945
|
-
for (const seg of
|
|
17831
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
17832
|
+
for (const seg of path2) {
|
|
16946
17833
|
if (typeof seg === "number")
|
|
16947
17834
|
segs.push(`[${seg}]`);
|
|
16948
17835
|
else if (typeof seg === "symbol")
|
|
@@ -29401,13 +30288,13 @@ function resolveRef(ref, ctx) {
|
|
|
29401
30288
|
if (!ref.startsWith("#")) {
|
|
29402
30289
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
29403
30290
|
}
|
|
29404
|
-
const
|
|
29405
|
-
if (
|
|
30291
|
+
const path2 = ref.slice(1).split("/").filter(Boolean);
|
|
30292
|
+
if (path2.length === 0) {
|
|
29406
30293
|
return ctx.rootSchema;
|
|
29407
30294
|
}
|
|
29408
30295
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
29409
|
-
if (
|
|
29410
|
-
const key =
|
|
30296
|
+
if (path2[0] === defsKey) {
|
|
30297
|
+
const key = path2[1];
|
|
29411
30298
|
if (!key || !ctx.defs[key]) {
|
|
29412
30299
|
throw new Error(`Reference not found: ${ref}`);
|
|
29413
30300
|
}
|
|
@@ -29893,6 +30780,7 @@ var SemanticConfigSchema = exports_external.object({
|
|
|
29893
30780
|
base_url: exports_external.string().trim().min(1).optional(),
|
|
29894
30781
|
api_key_env: exports_external.string().trim().min(1).optional(),
|
|
29895
30782
|
timeout_ms: exports_external.number().int().positive().optional(),
|
|
30783
|
+
query_timeout_ms: exports_external.number().int().positive().optional(),
|
|
29896
30784
|
max_batch_size: exports_external.number().int().positive().optional(),
|
|
29897
30785
|
max_files: exports_external.number().int().positive().optional()
|
|
29898
30786
|
});
|
|
@@ -29958,7 +30846,7 @@ var BackupConfigSchema = exports_external.object({
|
|
|
29958
30846
|
max_depth: exports_external.number().int().positive().optional(),
|
|
29959
30847
|
max_file_size: exports_external.number().int().positive().optional()
|
|
29960
30848
|
});
|
|
29961
|
-
var AftConfigSchema = exports_external.object({
|
|
30849
|
+
var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecificConfigKeys(value, OPENCODE_ONLY_KEYS), exports_external.object({
|
|
29962
30850
|
$schema: exports_external.string().optional(),
|
|
29963
30851
|
enabled: exports_external.boolean().optional(),
|
|
29964
30852
|
format_on_edit: exports_external.boolean().optional(),
|
|
@@ -29983,7 +30871,7 @@ var AftConfigSchema = exports_external.object({
|
|
|
29983
30871
|
semantic: SemanticConfigSchema.optional(),
|
|
29984
30872
|
bridge: BridgeConfigSchema.optional(),
|
|
29985
30873
|
subc: SubcConfigSchema.optional()
|
|
29986
|
-
}).strict();
|
|
30874
|
+
}).strict());
|
|
29987
30875
|
var CONFIG_MIGRATIONS = [
|
|
29988
30876
|
{ oldKey: "experimental_search_index", newPath: ["search_index"] },
|
|
29989
30877
|
{ oldKey: "experimental_semantic_search", newPath: ["semantic_search"] },
|
|
@@ -30008,9 +30896,9 @@ function extractCommentsForPreservation(content) {
|
|
|
30008
30896
|
}
|
|
30009
30897
|
return comments;
|
|
30010
30898
|
}
|
|
30011
|
-
function ensureRecordAtPath(root,
|
|
30899
|
+
function ensureRecordAtPath(root, path2) {
|
|
30012
30900
|
let current = root;
|
|
30013
|
-
for (const segment of
|
|
30901
|
+
for (const segment of path2) {
|
|
30014
30902
|
const existing = current[segment];
|
|
30015
30903
|
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
30016
30904
|
current[segment] = {};
|
|
@@ -30019,9 +30907,9 @@ function ensureRecordAtPath(root, path3) {
|
|
|
30019
30907
|
}
|
|
30020
30908
|
return current;
|
|
30021
30909
|
}
|
|
30022
|
-
function hasPath(root,
|
|
30910
|
+
function hasPath(root, path2) {
|
|
30023
30911
|
let current = root;
|
|
30024
|
-
for (const segment of
|
|
30912
|
+
for (const segment of path2) {
|
|
30025
30913
|
if (!current || typeof current !== "object" || Array.isArray(current))
|
|
30026
30914
|
return false;
|
|
30027
30915
|
const record2 = current;
|
|
@@ -30031,9 +30919,9 @@ function hasPath(root, path3) {
|
|
|
30031
30919
|
}
|
|
30032
30920
|
return true;
|
|
30033
30921
|
}
|
|
30034
|
-
function setPath(root,
|
|
30035
|
-
const parent = ensureRecordAtPath(root,
|
|
30036
|
-
parent[
|
|
30922
|
+
function setPath(root, path2, value) {
|
|
30923
|
+
const parent = ensureRecordAtPath(root, path2.slice(0, -1));
|
|
30924
|
+
parent[path2[path2.length - 1]] = value;
|
|
30037
30925
|
}
|
|
30038
30926
|
function migrateRawConfig(rawConfig, configPath, logger) {
|
|
30039
30927
|
const oldKeys = [];
|
|
@@ -30094,7 +30982,7 @@ function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
|
|
|
30094
30982
|
return movedKeys;
|
|
30095
30983
|
}
|
|
30096
30984
|
function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 }) {
|
|
30097
|
-
if (!
|
|
30985
|
+
if (!existsSync8(configPath)) {
|
|
30098
30986
|
return { migrated: false, oldKeys: [] };
|
|
30099
30987
|
}
|
|
30100
30988
|
let tmpPath = null;
|
|
@@ -30147,7 +31035,7 @@ function recordConfigParseFailure(configPath, errorMessage) {
|
|
|
30147
31035
|
}
|
|
30148
31036
|
function loadConfigFromPath(configPath) {
|
|
30149
31037
|
try {
|
|
30150
|
-
if (!
|
|
31038
|
+
if (!existsSync8(configPath))
|
|
30151
31039
|
return null;
|
|
30152
31040
|
const content = readFileSync7(configPath, "utf-8");
|
|
30153
31041
|
const rawConfig = import_comment_json.parse(content);
|
|
@@ -30428,7 +31316,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
30428
31316
|
import { createHash as createHash4 } from "node:crypto";
|
|
30429
31317
|
import {
|
|
30430
31318
|
createReadStream,
|
|
30431
|
-
existsSync as
|
|
31319
|
+
existsSync as existsSync10,
|
|
30432
31320
|
mkdirSync as mkdirSync7,
|
|
30433
31321
|
readFileSync as readFileSync10,
|
|
30434
31322
|
renameSync as renameSync6,
|
|
@@ -30448,7 +31336,7 @@ import {
|
|
|
30448
31336
|
unlinkSync as unlinkSync6,
|
|
30449
31337
|
writeFileSync as writeFileSync5
|
|
30450
31338
|
} from "node:fs";
|
|
30451
|
-
import { homedir as
|
|
31339
|
+
import { homedir as homedir12 } from "node:os";
|
|
30452
31340
|
import { join as join11 } from "node:path";
|
|
30453
31341
|
function aftCacheBase() {
|
|
30454
31342
|
const override = process.env.AFT_CACHE_DIR;
|
|
@@ -30456,10 +31344,10 @@ function aftCacheBase() {
|
|
|
30456
31344
|
return override;
|
|
30457
31345
|
if (process.platform === "win32") {
|
|
30458
31346
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
30459
|
-
const base2 = localAppData || join11(
|
|
31347
|
+
const base2 = localAppData || join11(homedir12(), "AppData", "Local");
|
|
30460
31348
|
return join11(base2, "aft");
|
|
30461
31349
|
}
|
|
30462
|
-
const base = process.env.XDG_CACHE_HOME || join11(
|
|
31350
|
+
const base = process.env.XDG_CACHE_HOME || join11(homedir12(), ".cache");
|
|
30463
31351
|
return join11(base, "aft");
|
|
30464
31352
|
}
|
|
30465
31353
|
function lspCacheRoot() {
|
|
@@ -30503,11 +31391,11 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
30503
31391
|
}
|
|
30504
31392
|
}
|
|
30505
31393
|
function readInstalledMetaIn(installDir) {
|
|
30506
|
-
const
|
|
31394
|
+
const path2 = join11(installDir, INSTALLED_META_FILE);
|
|
30507
31395
|
try {
|
|
30508
|
-
if (!statSync5(
|
|
31396
|
+
if (!statSync5(path2).isFile())
|
|
30509
31397
|
return null;
|
|
30510
|
-
const raw = readFileSync8(
|
|
31398
|
+
const raw = readFileSync8(path2, "utf8");
|
|
30511
31399
|
const parsed = JSON.parse(raw);
|
|
30512
31400
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
30513
31401
|
return null;
|
|
@@ -30813,7 +31701,7 @@ var NPM_LSP_TABLE = [
|
|
|
30813
31701
|
];
|
|
30814
31702
|
|
|
30815
31703
|
// src/lsp-project-relevance.ts
|
|
30816
|
-
import { existsSync as
|
|
31704
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
|
|
30817
31705
|
import { join as join12 } from "node:path";
|
|
30818
31706
|
var MAX_WALK_DIRS = 200;
|
|
30819
31707
|
var MAX_WALK_DEPTH = 4;
|
|
@@ -30831,7 +31719,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
|
|
|
30831
31719
|
if (!rootMarkers)
|
|
30832
31720
|
return false;
|
|
30833
31721
|
for (const marker of rootMarkers) {
|
|
30834
|
-
if (
|
|
31722
|
+
if (existsSync9(join12(projectRoot, marker)))
|
|
30835
31723
|
return true;
|
|
30836
31724
|
}
|
|
30837
31725
|
return false;
|
|
@@ -31013,7 +31901,7 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
|
|
|
31013
31901
|
function ensureInstallAnchor(cwd) {
|
|
31014
31902
|
try {
|
|
31015
31903
|
const stub = join13(cwd, "package.json");
|
|
31016
|
-
if (!
|
|
31904
|
+
if (!existsSync10(stub)) {
|
|
31017
31905
|
writeFileSync6(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
|
|
31018
31906
|
`);
|
|
31019
31907
|
}
|
|
@@ -31199,8 +32087,8 @@ function installedBinaryPath(spec) {
|
|
|
31199
32087
|
}
|
|
31200
32088
|
return null;
|
|
31201
32089
|
}
|
|
31202
|
-
function sha256OfFileSync(
|
|
31203
|
-
return createHash4("sha256").update(readFileSync10(
|
|
32090
|
+
function sha256OfFileSync(path2) {
|
|
32091
|
+
return createHash4("sha256").update(readFileSync10(path2)).digest("hex");
|
|
31204
32092
|
}
|
|
31205
32093
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
31206
32094
|
const packageDir = lspPackageDir(spec.npm);
|
|
@@ -31278,9 +32166,15 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
|
|
|
31278
32166
|
return installsStarted;
|
|
31279
32167
|
},
|
|
31280
32168
|
skipped,
|
|
31281
|
-
installsComplete: Promise.all(installPromises).then(() => {})
|
|
32169
|
+
installsComplete: Promise.all(installPromises).then(() => {}),
|
|
32170
|
+
getCachedBinDirs: () => NPM_LSP_TABLE.filter((spec) => isInstalled(spec.npm, spec.binary) && validateCachedNpmInstall(spec)).map((spec) => lspBinDir(spec.npm))
|
|
31282
32171
|
};
|
|
31283
32172
|
}
|
|
32173
|
+
async function pushLspPathsAfterAutoInstall(pool, projectRoot, cachedBinDirs) {
|
|
32174
|
+
const paths = [...new Set(cachedBinDirs)];
|
|
32175
|
+
pool.setConfigureOverride("lsp_paths_extra", paths);
|
|
32176
|
+
await pool.reconfigure(projectRoot, { lsp_paths_extra: paths });
|
|
32177
|
+
}
|
|
31284
32178
|
|
|
31285
32179
|
// src/lsp-github-install.ts
|
|
31286
32180
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -31289,7 +32183,7 @@ import {
|
|
|
31289
32183
|
copyFileSync as copyFileSync4,
|
|
31290
32184
|
createReadStream as createReadStream2,
|
|
31291
32185
|
createWriteStream as createWriteStream3,
|
|
31292
|
-
existsSync as
|
|
32186
|
+
existsSync as existsSync11,
|
|
31293
32187
|
lstatSync as lstatSync2,
|
|
31294
32188
|
mkdirSync as mkdirSync8,
|
|
31295
32189
|
readdirSync as readdirSync4,
|
|
@@ -31302,7 +32196,7 @@ import {
|
|
|
31302
32196
|
unlinkSync as unlinkSync7,
|
|
31303
32197
|
writeFileSync as writeFileSync7
|
|
31304
32198
|
} from "node:fs";
|
|
31305
|
-
import { dirname as
|
|
32199
|
+
import { dirname as dirname6, join as join14, relative as relative3, resolve as resolve7 } from "node:path";
|
|
31306
32200
|
import { Readable as Readable3 } from "node:stream";
|
|
31307
32201
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
31308
32202
|
|
|
@@ -31424,10 +32318,10 @@ function ghBinaryCandidates(spec, platform) {
|
|
|
31424
32318
|
}
|
|
31425
32319
|
function readGithubInstalledMetaIn(installDir) {
|
|
31426
32320
|
try {
|
|
31427
|
-
const
|
|
31428
|
-
if (!statSync7(
|
|
32321
|
+
const path2 = join14(installDir, INSTALLED_META_FILE2);
|
|
32322
|
+
if (!statSync7(path2).isFile())
|
|
31429
32323
|
return null;
|
|
31430
|
-
const parsed = JSON.parse(readFileSync11(
|
|
32324
|
+
const parsed = JSON.parse(readFileSync11(path2, "utf8"));
|
|
31431
32325
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
31432
32326
|
return null;
|
|
31433
32327
|
return {
|
|
@@ -31458,17 +32352,17 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
|
|
|
31458
32352
|
}
|
|
31459
32353
|
var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
|
|
31460
32354
|
var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
|
|
31461
|
-
function sha256OfFile(
|
|
32355
|
+
function sha256OfFile(path2) {
|
|
31462
32356
|
return new Promise((resolve8, reject) => {
|
|
31463
32357
|
const hash2 = createHash5("sha256");
|
|
31464
|
-
const stream = createReadStream2(
|
|
32358
|
+
const stream = createReadStream2(path2);
|
|
31465
32359
|
stream.on("error", reject);
|
|
31466
32360
|
stream.on("data", (chunk) => hash2.update(chunk));
|
|
31467
32361
|
stream.on("end", () => resolve8(hash2.digest("hex")));
|
|
31468
32362
|
});
|
|
31469
32363
|
}
|
|
31470
|
-
function sha256OfFileSync2(
|
|
31471
|
-
return createHash5("sha256").update(readFileSync11(
|
|
32364
|
+
function sha256OfFileSync2(path2) {
|
|
32365
|
+
return createHash5("sha256").update(readFileSync11(path2)).digest("hex");
|
|
31472
32366
|
}
|
|
31473
32367
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
|
|
31474
32368
|
const candidates = [];
|
|
@@ -31644,7 +32538,7 @@ async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
|
31644
32538
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
|
|
31645
32539
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
|
|
31646
32540
|
}
|
|
31647
|
-
mkdirSync8(
|
|
32541
|
+
mkdirSync8(dirname6(destPath), { recursive: true });
|
|
31648
32542
|
let bytesWritten = 0;
|
|
31649
32543
|
const guard = new TransformStream({
|
|
31650
32544
|
transform(chunk, controller) {
|
|
@@ -31791,7 +32685,7 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
31791
32685
|
const dest = join14(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
31792
32686
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
31793
32687
|
try {
|
|
31794
|
-
mkdirSync8(
|
|
32688
|
+
mkdirSync8(dirname6(dest), { recursive: true });
|
|
31795
32689
|
rmSync5(dest, { recursive: true, force: true });
|
|
31796
32690
|
renameSync7(packageDir, dest);
|
|
31797
32691
|
} catch (err) {
|
|
@@ -31920,12 +32814,12 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
31920
32814
|
} catch {}
|
|
31921
32815
|
}
|
|
31922
32816
|
const innerBinaryPath = join14(extractDir, spec.binaryPathInArchive(platform, arch, version2));
|
|
31923
|
-
if (!
|
|
32817
|
+
if (!existsSync11(innerBinaryPath)) {
|
|
31924
32818
|
error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
|
|
31925
32819
|
return null;
|
|
31926
32820
|
}
|
|
31927
32821
|
const targetBinary = ghBinaryPath(spec, platform);
|
|
31928
|
-
mkdirSync8(
|
|
32822
|
+
mkdirSync8(dirname6(targetBinary), { recursive: true });
|
|
31929
32823
|
try {
|
|
31930
32824
|
copyFileSync4(innerBinaryPath, targetBinary);
|
|
31931
32825
|
if (platform !== "win32") {
|
|
@@ -32004,7 +32898,7 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
|
|
|
32004
32898
|
if (!host) {
|
|
32005
32899
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
32006
32900
|
try {
|
|
32007
|
-
if (
|
|
32901
|
+
if (existsSync11(ghBinDir(spec))) {
|
|
32008
32902
|
cachedBinDirs.push(ghBinDir(spec));
|
|
32009
32903
|
}
|
|
32010
32904
|
} catch {}
|
|
@@ -32014,7 +32908,8 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
|
|
|
32014
32908
|
installsStarted: 0,
|
|
32015
32909
|
installingBinaries: [],
|
|
32016
32910
|
skipped,
|
|
32017
|
-
installsComplete: Promise.resolve()
|
|
32911
|
+
installsComplete: Promise.resolve(),
|
|
32912
|
+
getCachedBinDirs: () => [...cachedBinDirs]
|
|
32018
32913
|
};
|
|
32019
32914
|
}
|
|
32020
32915
|
for (const spec of GITHUB_LSP_TABLE) {
|
|
@@ -32056,7 +32951,13 @@ function runGithubAutoInstall(relevantServers, config2, fetchImpl = fetch) {
|
|
|
32056
32951
|
return installsStarted;
|
|
32057
32952
|
},
|
|
32058
32953
|
skipped,
|
|
32059
|
-
installsComplete: Promise.all(installPromises).then(() => {})
|
|
32954
|
+
installsComplete: Promise.all(installPromises).then(() => {}),
|
|
32955
|
+
getCachedBinDirs: () => {
|
|
32956
|
+
const currentHost = detectHostPlatform();
|
|
32957
|
+
if (!currentHost)
|
|
32958
|
+
return [];
|
|
32959
|
+
return GITHUB_LSP_TABLE.filter((spec) => isGithubInstalled(spec, currentHost.platform) && validateCachedGithubInstall(spec, currentHost.platform)).map((spec) => ghBinDir(spec));
|
|
32960
|
+
}
|
|
32060
32961
|
};
|
|
32061
32962
|
}
|
|
32062
32963
|
function discoverRelevantGithubServers(projectRoot) {
|
|
@@ -32242,6 +33143,27 @@ function statusBarBlockForSession(sessionID, counts, force = false) {
|
|
|
32242
33143
|
return shouldEmitStatusBar(state, counts) ? formatStatusBar(counts) : undefined;
|
|
32243
33144
|
}
|
|
32244
33145
|
|
|
33146
|
+
// src/bash-wait-detach.ts
|
|
33147
|
+
var BASH_TRANSPORT_TIMEOUT_MS = 30000;
|
|
33148
|
+
async function sendBashWaitDetach(bridge, sessionID) {
|
|
33149
|
+
const response = await bridge.send("bash_wait_detach", { session_id: sessionID }, { keepBridgeOnTimeout: true, transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS });
|
|
33150
|
+
if (response.success === false) {
|
|
33151
|
+
throw new Error(String(response.message ?? "bash_wait_detach failed"));
|
|
33152
|
+
}
|
|
33153
|
+
}
|
|
33154
|
+
async function signalBashWaitDetachForProject(pool, projectRoot, sessionID) {
|
|
33155
|
+
if (!sessionID)
|
|
33156
|
+
return;
|
|
33157
|
+
const bridge = pool.getActiveBridgeForRoot(projectRoot);
|
|
33158
|
+
if (!bridge)
|
|
33159
|
+
return;
|
|
33160
|
+
try {
|
|
33161
|
+
await sendBashWaitDetach(bridge, sessionID);
|
|
33162
|
+
} catch (err) {
|
|
33163
|
+
warn2(`[bash_wait_detach] failed for session ${sessionID}: ${err instanceof Error ? err.message : String(err)}`);
|
|
33164
|
+
}
|
|
33165
|
+
}
|
|
33166
|
+
|
|
32245
33167
|
// src/shutdown-hooks.ts
|
|
32246
33168
|
var GLOBAL_KEY = "__aftPiShutdownHooks__";
|
|
32247
33169
|
function getState() {
|
|
@@ -32338,8 +33260,8 @@ import { StringEnum } from "@earendil-works/pi-ai";
|
|
|
32338
33260
|
import { Type as Type3 } from "typebox";
|
|
32339
33261
|
|
|
32340
33262
|
// src/tools/hoisted.ts
|
|
32341
|
-
import { stat } from "node:fs/promises";
|
|
32342
|
-
import { homedir as
|
|
33263
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
33264
|
+
import { homedir as homedir13 } from "node:os";
|
|
32343
33265
|
import { isAbsolute as isAbsolute6, relative as relative4, resolve as resolve8, sep } from "node:path";
|
|
32344
33266
|
import {
|
|
32345
33267
|
renderDiff
|
|
@@ -32483,23 +33405,24 @@ function containsPath(parent, child) {
|
|
|
32483
33405
|
const rel = relative4(parent, child);
|
|
32484
33406
|
return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
|
|
32485
33407
|
}
|
|
32486
|
-
function
|
|
32487
|
-
|
|
32488
|
-
|
|
32489
|
-
|
|
32490
|
-
|
|
32491
|
-
|
|
32492
|
-
|
|
33408
|
+
function normalizePathInput(path2) {
|
|
33409
|
+
const decoded = decodeFileUrl(path2);
|
|
33410
|
+
if (!decoded || !decoded.startsWith("~"))
|
|
33411
|
+
return decoded;
|
|
33412
|
+
if (decoded === "~")
|
|
33413
|
+
return homedir13();
|
|
33414
|
+
if (decoded.startsWith(`~${sep}`) || decoded.startsWith("~/")) {
|
|
33415
|
+
return resolve8(homedir13(), decoded.slice(2));
|
|
32493
33416
|
}
|
|
32494
|
-
return
|
|
33417
|
+
return decoded;
|
|
32495
33418
|
}
|
|
32496
33419
|
function absoluteSearchPath(cwd, target) {
|
|
32497
|
-
const expanded =
|
|
33420
|
+
const expanded = normalizePathInput(target);
|
|
32498
33421
|
return isAbsolute6(expanded) ? expanded : resolve8(cwd, expanded);
|
|
32499
33422
|
}
|
|
32500
33423
|
async function searchPathExists(cwd, target) {
|
|
32501
33424
|
try {
|
|
32502
|
-
await
|
|
33425
|
+
await stat2(absoluteSearchPath(cwd, target));
|
|
32503
33426
|
return true;
|
|
32504
33427
|
} catch {
|
|
32505
33428
|
return false;
|
|
@@ -32550,12 +33473,14 @@ ${note}` : note;
|
|
|
32550
33473
|
async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
32551
33474
|
if (!target)
|
|
32552
33475
|
return;
|
|
32553
|
-
const expanded =
|
|
33476
|
+
const expanded = normalizePathInput(target);
|
|
32554
33477
|
const absoluteTarget = isAbsolute6(expanded) ? expanded : resolve8(extCtx.cwd, expanded);
|
|
32555
33478
|
if (containsPath(extCtx.cwd, absoluteTarget))
|
|
32556
33479
|
return;
|
|
32557
33480
|
if (options.restrictToProjectRoot === false)
|
|
32558
33481
|
return;
|
|
33482
|
+
if (options.serverValidatedRead === true)
|
|
33483
|
+
return;
|
|
32559
33484
|
throw new Error(`Blocked: '${absoluteTarget}' is outside the project root and restrict_to_project_root is ` + "enabled (AFT full isolation). Not overridable per-call; set restrict_to_project_root: false " + "in aft.jsonc to allow external paths.");
|
|
32560
33485
|
}
|
|
32561
33486
|
var ReadParams = Type2.Object({
|
|
@@ -32565,8 +33490,8 @@ var ReadParams = Type2.Object({
|
|
|
32565
33490
|
filePath: Type2.Optional(Type2.String({
|
|
32566
33491
|
description: "Alias for `path` — provide one of the two."
|
|
32567
33492
|
})),
|
|
32568
|
-
offset: optionalInt(1, Number.MAX_SAFE_INTEGER),
|
|
32569
|
-
limit: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
33493
|
+
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line number to start reading from (use with limit)"),
|
|
33494
|
+
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum number of lines to return")
|
|
32570
33495
|
});
|
|
32571
33496
|
var WriteParams = Type2.Object({
|
|
32572
33497
|
filePath: Type2.Optional(Type2.String({
|
|
@@ -32580,8 +33505,10 @@ var WriteParams = Type2.Object({
|
|
|
32580
33505
|
var BatchEditParams = Type2.Object({
|
|
32581
33506
|
oldString: Type2.Optional(Type2.String({ description: "Text to find for a batch find/replace edit" })),
|
|
32582
33507
|
newString: Type2.Optional(Type2.String({ description: "Replacement text for a batch find/replace edit" })),
|
|
32583
|
-
|
|
32584
|
-
|
|
33508
|
+
replaceAll: Type2.Optional(Type2.Boolean({ description: "Replace every occurrence for this batch item" })),
|
|
33509
|
+
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER, "0-based occurrence for this batch item"),
|
|
33510
|
+
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based start line for a batch line-range edit"),
|
|
33511
|
+
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based end line for a batch line-range edit"),
|
|
32585
33512
|
content: Type2.Optional(Type2.String({
|
|
32586
33513
|
description: "Replacement text for a batch line-range edit (empty string deletes the lines)"
|
|
32587
33514
|
}))
|
|
@@ -32596,12 +33523,12 @@ var EditParams = Type2.Object({
|
|
|
32596
33523
|
oldString: Type2.Optional(Type2.String({ description: "Text to find (exact match, fuzzy fallback)" })),
|
|
32597
33524
|
newString: Type2.Optional(Type2.String({ description: "Replacement text (omit to delete match)" })),
|
|
32598
33525
|
replaceAll: Type2.Optional(Type2.Boolean({ description: "Replace every occurrence" })),
|
|
32599
|
-
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER),
|
|
33526
|
+
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER, "0-based occurrence to replace when multiple matches exist"),
|
|
32600
33527
|
appendContent: Type2.Optional(Type2.String({
|
|
32601
33528
|
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, edits/oldString/newString are ignored."
|
|
32602
33529
|
})),
|
|
32603
33530
|
edits: Type2.Optional(Type2.Array(BatchEditParams, {
|
|
32604
|
-
description: "Batch edits — array of { oldString, newString } or { startLine, endLine, content } objects applied atomically
|
|
33531
|
+
description: "Batch edits — array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically."
|
|
32605
33532
|
}))
|
|
32606
33533
|
});
|
|
32607
33534
|
var GrepParams = Type2.Object({
|
|
@@ -32685,7 +33612,8 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32685
33612
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
32686
33613
|
const filePath = await resolvePathArg(extCtx.cwd, pathArg);
|
|
32687
33614
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
32688
|
-
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33615
|
+
restrictToProjectRoot: surface.restrictToProjectRoot,
|
|
33616
|
+
serverValidatedRead: true
|
|
32689
33617
|
});
|
|
32690
33618
|
const rawArgs = { filePath: pathArg };
|
|
32691
33619
|
if (offset !== undefined)
|
|
@@ -32766,7 +33694,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
32766
33694
|
pi.registerTool({
|
|
32767
33695
|
name: "edit",
|
|
32768
33696
|
label: "edit",
|
|
32769
|
-
description: "Edit part of a file via `appendContent`, batch `edits[]`, or `oldString`/`newString` find-and-replace.
|
|
33697
|
+
description: "Edit part of a file via `appendContent`, batch `edits[]`, or `oldString`/`newString` find-and-replace. Batch `{ oldString, newString, replaceAll: true }` replaces every match. Mode priority: appendContent > edits > oldString.",
|
|
32770
33698
|
promptSnippet: "Partial file edits via appendContent, edits[], or oldString/newString (mode priority: appendContent > edits > oldString).",
|
|
32771
33699
|
promptGuidelines: [
|
|
32772
33700
|
"Prefer edit over write when changing part of an existing file.",
|
|
@@ -32792,10 +33720,20 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
32792
33720
|
});
|
|
32793
33721
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32794
33722
|
const rawArgs = { filePath: filePathArg };
|
|
32795
|
-
for (const key of ["appendContent", "
|
|
33723
|
+
for (const key of ["appendContent", "oldString", "newString"]) {
|
|
32796
33724
|
if (argsRecord[key] !== undefined)
|
|
32797
33725
|
rawArgs[key] = argsRecord[key];
|
|
32798
33726
|
}
|
|
33727
|
+
if (Array.isArray(argsRecord.edits)) {
|
|
33728
|
+
rawArgs.edits = argsRecord.edits.map((item) => {
|
|
33729
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
33730
|
+
return item;
|
|
33731
|
+
const batchItem = item;
|
|
33732
|
+
return batchItem.replaceAll === undefined ? batchItem : { ...batchItem, replaceAll: coerceBoolean(batchItem.replaceAll) };
|
|
33733
|
+
});
|
|
33734
|
+
} else if (argsRecord.edits !== undefined) {
|
|
33735
|
+
rawArgs.edits = argsRecord.edits;
|
|
33736
|
+
}
|
|
32799
33737
|
if (params.replaceAll !== undefined)
|
|
32800
33738
|
rawArgs.replaceAll = coerceBoolean(params.replaceAll);
|
|
32801
33739
|
const occurrence = coerceOptionalInt(params.occurrence, "occurrence", 0, Number.MAX_SAFE_INTEGER);
|
|
@@ -32998,17 +33936,17 @@ ${summary}${suffix}`);
|
|
|
32998
33936
|
container.addChild(new Text(renderDiff(diff), 1, 0));
|
|
32999
33937
|
return container;
|
|
33000
33938
|
}
|
|
33001
|
-
function shortenPath2(
|
|
33002
|
-
const home =
|
|
33003
|
-
if (
|
|
33004
|
-
return `~${
|
|
33005
|
-
return
|
|
33939
|
+
function shortenPath2(path2) {
|
|
33940
|
+
const home = homedir13();
|
|
33941
|
+
if (path2.startsWith(home))
|
|
33942
|
+
return `~${path2.slice(home.length)}`;
|
|
33943
|
+
return path2;
|
|
33006
33944
|
}
|
|
33007
|
-
async function resolvePathArg(cwd,
|
|
33008
|
-
const expanded =
|
|
33009
|
-
const abs = absoluteSearchPath(cwd,
|
|
33945
|
+
async function resolvePathArg(cwd, path2) {
|
|
33946
|
+
const expanded = normalizePathInput(path2);
|
|
33947
|
+
const abs = absoluteSearchPath(cwd, path2);
|
|
33010
33948
|
try {
|
|
33011
|
-
await
|
|
33949
|
+
await stat2(abs);
|
|
33012
33950
|
return abs;
|
|
33013
33951
|
} catch {
|
|
33014
33952
|
return expanded;
|
|
@@ -33016,7 +33954,7 @@ async function resolvePathArg(cwd, path3) {
|
|
|
33016
33954
|
}
|
|
33017
33955
|
|
|
33018
33956
|
// src/tools/render-helpers.ts
|
|
33019
|
-
import { homedir as
|
|
33957
|
+
import { homedir as homedir14 } from "node:os";
|
|
33020
33958
|
import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
|
|
33021
33959
|
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
|
|
33022
33960
|
function reuseText2(last) {
|
|
@@ -33025,11 +33963,11 @@ function reuseText2(last) {
|
|
|
33025
33963
|
function reuseContainer2(last) {
|
|
33026
33964
|
return last instanceof Container2 ? last : new Container2;
|
|
33027
33965
|
}
|
|
33028
|
-
function shortenPath3(
|
|
33029
|
-
const home =
|
|
33030
|
-
if (
|
|
33031
|
-
return `~${
|
|
33032
|
-
return
|
|
33966
|
+
function shortenPath3(path2) {
|
|
33967
|
+
const home = homedir14();
|
|
33968
|
+
if (path2.startsWith(home))
|
|
33969
|
+
return `~${path2.slice(home.length)}`;
|
|
33970
|
+
return path2;
|
|
33033
33971
|
}
|
|
33034
33972
|
function renderToolCall(toolName, summary, theme, context) {
|
|
33035
33973
|
const text = reuseText2(context.lastComponent);
|
|
@@ -33037,10 +33975,10 @@ function renderToolCall(toolName, summary, theme, context) {
|
|
|
33037
33975
|
text.setText(`${theme.fg("toolTitle", theme.bold(toolName))}${suffix}`);
|
|
33038
33976
|
return text;
|
|
33039
33977
|
}
|
|
33040
|
-
function accentPath(theme,
|
|
33041
|
-
if (!
|
|
33978
|
+
function accentPath(theme, path2) {
|
|
33979
|
+
if (!path2)
|
|
33042
33980
|
return theme.fg("toolOutput", "...");
|
|
33043
|
-
return theme.fg("accent", shortenPath3(
|
|
33981
|
+
return theme.fg("accent", shortenPath3(path2));
|
|
33044
33982
|
}
|
|
33045
33983
|
function collectTextContent(result) {
|
|
33046
33984
|
return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
|
|
@@ -33225,17 +34163,17 @@ ${warningStrings.map((w) => ` ${w}`).join(`
|
|
|
33225
34163
|
async function resolveAstPaths(extCtx, paths) {
|
|
33226
34164
|
if (isEmptyParam(paths) || !Array.isArray(paths))
|
|
33227
34165
|
return;
|
|
33228
|
-
return Promise.all(paths.filter((
|
|
34166
|
+
return Promise.all(paths.filter((path2) => typeof path2 === "string").map((path2) => resolvePathArg(extCtx.cwd, path2)));
|
|
33229
34167
|
}
|
|
33230
34168
|
async function assertAstPathsPermission(extCtx, paths, restrictToProjectRoot) {
|
|
33231
34169
|
if (paths === undefined || paths.length === 0)
|
|
33232
34170
|
return;
|
|
33233
34171
|
const checked = new Set;
|
|
33234
|
-
for (const
|
|
33235
|
-
if (checked.has(
|
|
34172
|
+
for (const path2 of paths) {
|
|
34173
|
+
if (checked.has(path2))
|
|
33236
34174
|
continue;
|
|
33237
|
-
checked.add(
|
|
33238
|
-
await assertExternalDirectoryPermission(extCtx,
|
|
34175
|
+
checked.add(path2);
|
|
34176
|
+
await assertExternalDirectoryPermission(extCtx, path2, { restrictToProjectRoot });
|
|
33239
34177
|
}
|
|
33240
34178
|
}
|
|
33241
34179
|
function buildAstSearchSections(payload, theme) {
|
|
@@ -33417,7 +34355,7 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
33417
34355
|
}
|
|
33418
34356
|
|
|
33419
34357
|
// src/tools/bash.ts
|
|
33420
|
-
import * as
|
|
34358
|
+
import * as fs3 from "node:fs/promises";
|
|
33421
34359
|
import { Container as Container3, Spacer as Spacer3, Text as Text3 } from "@earendil-works/pi-tui";
|
|
33422
34360
|
import { Type as Type4 } from "typebox";
|
|
33423
34361
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
@@ -33433,7 +34371,7 @@ function resolveForegroundWaitMs(configured) {
|
|
|
33433
34371
|
}
|
|
33434
34372
|
return configured;
|
|
33435
34373
|
}
|
|
33436
|
-
var
|
|
34374
|
+
var BASH_TRANSPORT_TIMEOUT_MS2 = 30000;
|
|
33437
34375
|
var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
|
33438
34376
|
var BASH_TRANSPORT_MARGIN_MS = 1e4;
|
|
33439
34377
|
function orchestratedTransportTimeoutMs(blockToCompletion, wait, effectiveTimeout, foregroundWaitMs) {
|
|
@@ -33444,7 +34382,7 @@ var BashBaseParams = {
|
|
|
33444
34382
|
command: Type4.String({
|
|
33445
34383
|
description: "Shell command to execute. Supports pipes, redirections, and shell syntax."
|
|
33446
34384
|
}),
|
|
33447
|
-
timeout: optionalInt(1, Number.MAX_SAFE_INTEGER),
|
|
34385
|
+
timeout: optionalInt(1, Number.MAX_SAFE_INTEGER, "Hard kill timeout in milliseconds"),
|
|
33448
34386
|
workdir: Type4.Optional(Type4.String({
|
|
33449
34387
|
description: "Working directory for command execution. Relative paths resolve against the project root. Defaults to the current session's working directory."
|
|
33450
34388
|
})),
|
|
@@ -33452,7 +34390,7 @@ var BashBaseParams = {
|
|
|
33452
34390
|
description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
|
|
33453
34391
|
})),
|
|
33454
34392
|
wait: Type4.Optional(Type4.Boolean({
|
|
33455
|
-
description: "When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout. Use only when you know the result is required before doing anything else."
|
|
34393
|
+
description: "When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout; if you send a new message, the wait detaches to background. Use only when you know the result is required before doing anything else."
|
|
33456
34394
|
}))
|
|
33457
34395
|
};
|
|
33458
34396
|
var BashBackgroundFlagParam = {
|
|
@@ -33469,8 +34407,8 @@ var BashPtyParams = {
|
|
|
33469
34407
|
pty: Type4.Optional(Type4.Boolean({
|
|
33470
34408
|
description: 'Spawn the command in a real PTY for interactive programs. Implies background: true automatically. Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.'
|
|
33471
34409
|
})),
|
|
33472
|
-
ptyRows: optionalInt(1, 60),
|
|
33473
|
-
ptyCols: optionalInt(1, 140)
|
|
34410
|
+
ptyRows: optionalInt(1, 60, "PTY terminal height in rows (minimum 1, maximum 60)"),
|
|
34411
|
+
ptyCols: optionalInt(1, 140, "PTY terminal width in columns (minimum 1, maximum 140)")
|
|
33474
34412
|
};
|
|
33475
34413
|
var BashParams = Type4.Object({
|
|
33476
34414
|
...BashBaseParams,
|
|
@@ -33504,7 +34442,7 @@ var BashWatchParams = Type4.Object({
|
|
|
33504
34442
|
}),
|
|
33505
34443
|
pattern: Type4.Optional(Type4.Union([Type4.String(), Type4.Object({ regex: Type4.String() })])),
|
|
33506
34444
|
background: Type4.Optional(Type4.Boolean()),
|
|
33507
|
-
timeout_ms: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS),
|
|
34445
|
+
timeout_ms: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS, "Maximum time to wait in milliseconds"),
|
|
33508
34446
|
once: Type4.Optional(Type4.Boolean())
|
|
33509
34447
|
});
|
|
33510
34448
|
var BashWriteParams = Type4.Object({
|
|
@@ -33530,7 +34468,7 @@ function unavailableSnapshot() {
|
|
|
33530
34468
|
}
|
|
33531
34469
|
async function callBashBridge(bridge, command, params = {}, extCtx, options) {
|
|
33532
34470
|
return await callBridge(bridge, command, params, extCtx, {
|
|
33533
|
-
transportTimeoutMs:
|
|
34471
|
+
transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS2,
|
|
33534
34472
|
...options,
|
|
33535
34473
|
keepBridgeOnTimeout: true
|
|
33536
34474
|
});
|
|
@@ -33561,7 +34499,7 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false) {
|
|
|
33561
34499
|
const searchSteer = aftSearchRegistered ? "use `aft_search` (concepts, identifiers, regex, literals), `read`, `aft_outline`, or `aft_zoom` instead" : "use the `grep` tool, `read`, `aft_outline`, or `aft_zoom` instead";
|
|
33562
34500
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
33563
34501
|
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output. Piped commands run verbatim and show the pipeline's output; for AFT's test/build summary, run the runner without `| head`, `| tail`, or `| grep`." : "";
|
|
33564
|
-
const tasksSentence = bashCfg.background ? ' Commands run in the foreground and return inline; `wait: true` blocks until a long command finishes instead of auto-promoting — 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 useful work to do while it runs; then `bash_watch` waits on the task (sync blocks until exit/pattern, async notifies) and `bash_status` peeks at it — never background a command and immediately `bash_watch` it (that wastes a turn for what foreground returns in one), and never loop `bash_status` to wait. `pty: true` runs interactive programs (REPLs, TUIs), implies background, and is driven with `bash_status({ output_mode: "screen" })` plus `bash_write`.' : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
|
|
34502
|
+
const tasksSentence = bashCfg.background ? ' Commands run in the foreground and return inline; `wait: true` blocks until a long command finishes instead of auto-promoting, but detaches to background if you send a new message — 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 useful work to do while it runs; then `bash_watch` waits on the task (sync blocks until exit/pattern, async notifies) and `bash_status` peeks at it — never background a command and immediately `bash_watch` it (that wastes a turn for what foreground returns in one), and never loop `bash_status` to wait. `pty: true` runs interactive programs (REPLs, TUIs), implies background, and is driven with `bash_status({ output_mode: "screen" })` plus `bash_write`.' : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
|
|
33565
34503
|
pi.registerTool({
|
|
33566
34504
|
name: "bash",
|
|
33567
34505
|
label: "bash",
|
|
@@ -33835,7 +34773,7 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
33835
34773
|
let scanBaseOffset = 0;
|
|
33836
34774
|
const bridgeOptions = {
|
|
33837
34775
|
keepBridgeOnTimeout: true,
|
|
33838
|
-
transportTimeoutMs:
|
|
34776
|
+
transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS2
|
|
33839
34777
|
};
|
|
33840
34778
|
if (waitFor?.kind === "regex") {
|
|
33841
34779
|
await validateWaitRegex(bridge, extCtx, waitFor);
|
|
@@ -33946,7 +34884,7 @@ async function readNewTaskOutput(data, cursor) {
|
|
|
33946
34884
|
};
|
|
33947
34885
|
}
|
|
33948
34886
|
async function readFileBytesFrom(outputPath, cursor) {
|
|
33949
|
-
const handle = await
|
|
34887
|
+
const handle = await fs3.open(outputPath, "r");
|
|
33950
34888
|
try {
|
|
33951
34889
|
const chunks = [];
|
|
33952
34890
|
let offset = cursor;
|
|
@@ -34075,7 +35013,7 @@ async function formatPtyStatus(_extCtx, taskId, details, requestedOutputMode) {
|
|
|
34075
35013
|
return `
|
|
34076
35014
|
[PTY output path unavailable]`;
|
|
34077
35015
|
const outputMode = requestedOutputMode ?? "screen";
|
|
34078
|
-
const raw = outputMode === "raw" || outputMode === "both" ? await
|
|
35016
|
+
const raw = outputMode === "raw" || outputMode === "both" ? await fs3.readFile(details.output_path) : undefined;
|
|
34079
35017
|
let suffix = "";
|
|
34080
35018
|
if (outputMode === "raw") {
|
|
34081
35019
|
suffix = raw && raw.length > 0 ? `
|
|
@@ -34196,12 +35134,12 @@ function registerConflictsTool(pi, ctx) {
|
|
|
34196
35134
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
34197
35135
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
34198
35136
|
const reqParams = {};
|
|
34199
|
-
const
|
|
34200
|
-
if (typeof
|
|
34201
|
-
await assertExternalDirectoryPermission(extCtx,
|
|
35137
|
+
const path2 = params?.path;
|
|
35138
|
+
if (typeof path2 === "string" && path2.trim() !== "") {
|
|
35139
|
+
await assertExternalDirectoryPermission(extCtx, path2, {
|
|
34202
35140
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34203
35141
|
});
|
|
34204
|
-
reqParams.path = await resolvePathArg(extCtx.cwd,
|
|
35142
|
+
reqParams.path = await resolvePathArg(extCtx.cwd, path2);
|
|
34205
35143
|
}
|
|
34206
35144
|
const response = await callToolCall(bridge, "conflicts", reqParams, extCtx);
|
|
34207
35145
|
if (response.success === false) {
|
|
@@ -34613,8 +35551,8 @@ function tier2SummaryPart(summary, key, label) {
|
|
|
34613
35551
|
return `${label} ${status ?? "unavailable"}`;
|
|
34614
35552
|
}
|
|
34615
35553
|
function shortDupOccurrence(entry) {
|
|
34616
|
-
const [
|
|
34617
|
-
return
|
|
35554
|
+
const [path2] = entry.split(":");
|
|
35555
|
+
return path2?.split("/").pop() ?? entry;
|
|
34618
35556
|
}
|
|
34619
35557
|
function tier2TopPreview(summary, theme) {
|
|
34620
35558
|
const lines = [];
|
|
@@ -34798,7 +35736,7 @@ function navigateParamsSchema() {
|
|
|
34798
35736
|
description: "Source file containing the symbol (absolute or relative to project root)"
|
|
34799
35737
|
}),
|
|
34800
35738
|
symbol: Type9.String({ description: "Name of the symbol to analyze" }),
|
|
34801
|
-
depth: optionalInt(1, Number.MAX_SAFE_INTEGER),
|
|
35739
|
+
depth: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum call-graph depth to traverse"),
|
|
34802
35740
|
expression: Type9.Optional(Type9.String({ description: "Expression to track (required for trace_data)" })),
|
|
34803
35741
|
toSymbol: Type9.Optional(Type9.String({
|
|
34804
35742
|
description: "Target symbol for trace_to_symbol; the returned path ends here"
|
|
@@ -35235,9 +36173,9 @@ var RefactorParams = Type11.Object({
|
|
|
35235
36173
|
destination: Type11.Optional(Type11.String({ description: "Target file (for move)" })),
|
|
35236
36174
|
scope: Type11.Optional(Type11.String({ description: "Disambiguation scope for move op" })),
|
|
35237
36175
|
name: Type11.Optional(Type11.String({ description: "New function name (for extract)" })),
|
|
35238
|
-
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER),
|
|
35239
|
-
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER),
|
|
35240
|
-
callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
36176
|
+
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based start line for extract"),
|
|
36177
|
+
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based end line (inclusive) for extract"),
|
|
36178
|
+
callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based call site line for inline")
|
|
35241
36179
|
});
|
|
35242
36180
|
function buildRefactorSections(args, payload, theme) {
|
|
35243
36181
|
const response = asRecord3(payload);
|
|
@@ -35356,7 +36294,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
35356
36294
|
import { StringEnum as StringEnum5 } from "@earendil-works/pi-ai";
|
|
35357
36295
|
import { Type as Type12 } from "typebox";
|
|
35358
36296
|
function responsePaths(response) {
|
|
35359
|
-
return Array.isArray(response.paths) ? response.paths.filter((
|
|
36297
|
+
return Array.isArray(response.paths) ? response.paths.filter((path2) => typeof path2 === "string" && path2.length > 0) : [];
|
|
35360
36298
|
}
|
|
35361
36299
|
var SafetyParams = Type12.Object({
|
|
35362
36300
|
op: StringEnum5(["undo", "history", "checkpoint", "restore", "list"], {
|
|
@@ -35738,7 +36676,7 @@ function buildWorkflowHints(opts) {
|
|
|
35738
36676
|
}
|
|
35739
36677
|
if (hasBgBash) {
|
|
35740
36678
|
sections.push([
|
|
35741
|
-
`**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — use \`${bashName}({ command, wait: true })\` when you know it is long and need the result before anything else; otherwise omit \`wait\` so auto-promote can hand you a reminder while you work.`,
|
|
36679
|
+
`**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — use \`${bashName}({ command, wait: true })\` when you know it is long and need the result before anything else; if you send a new message, the wait detaches to background; otherwise omit \`wait\` so auto-promote can hand you a reminder while you work.`,
|
|
35742
36680
|
"- `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.",
|
|
35743
36681
|
"- `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."
|
|
35744
36682
|
].join(`
|
|
@@ -35809,18 +36747,18 @@ function createVersionMismatchHandler(getPool, ensureCompatibleBinary = ensureBi
|
|
|
35809
36747
|
const upgradePromise = (async () => {
|
|
35810
36748
|
warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
|
|
35811
36749
|
try {
|
|
35812
|
-
const
|
|
35813
|
-
if (!
|
|
36750
|
+
const path2 = await ensureCompatibleBinary(`v${minVersion}`);
|
|
36751
|
+
if (!path2) {
|
|
35814
36752
|
warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
|
|
35815
36753
|
return null;
|
|
35816
36754
|
}
|
|
35817
36755
|
const pool = getPool();
|
|
35818
36756
|
if (!pool) {
|
|
35819
|
-
warn2(`Found/downloaded compatible binary at ${
|
|
36757
|
+
warn2(`Found/downloaded compatible binary at ${path2}, but bridge pool is not ready.`);
|
|
35820
36758
|
return null;
|
|
35821
36759
|
}
|
|
35822
|
-
log2(`Found/downloaded compatible binary at ${
|
|
35823
|
-
const replaced = await pool.replaceBinary(
|
|
36760
|
+
log2(`Found/downloaded compatible binary at ${path2}. Replacing running bridges...`);
|
|
36761
|
+
const replaced = await pool.replaceBinary(path2);
|
|
35824
36762
|
log2("Binary replaced successfully. New bridges will use the updated binary.");
|
|
35825
36763
|
return replaced;
|
|
35826
36764
|
} catch (err) {
|
|
@@ -35842,12 +36780,14 @@ var PLUGIN_VERSION = (() => {
|
|
|
35842
36780
|
return "0.0.0";
|
|
35843
36781
|
}
|
|
35844
36782
|
})();
|
|
35845
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
36783
|
+
var ANNOUNCEMENT_VERSION = "0.47.1";
|
|
35846
36784
|
var ANNOUNCEMENT_FEATURES = [
|
|
35847
|
-
"
|
|
35848
|
-
"
|
|
35849
|
-
"
|
|
35850
|
-
"
|
|
36785
|
+
"Fixed: apply_patch could delete a file when a patch moved it to its own path — same-path moves are now plain in-place updates.",
|
|
36786
|
+
"Fixed: moving a symlink across filesystems turned it into a regular file; the link itself is now preserved.",
|
|
36787
|
+
"Fixed: a failed delete could leave a phantom undo entry, so the next undo reverted the wrong operation.",
|
|
36788
|
+
"Fixed: a crashed process could leave a stale lock that permanently disabled update checks.",
|
|
36789
|
+
"Paths can be given as file: URLs (#162), and reading your own background task's output files no longer prompts for permission (#161).",
|
|
36790
|
+
"Faster hot paths: warm symbol lookups skip re-hashing unchanged files, search reads each posting list once per query, snippets stream only the needed lines, and edits no longer copy the whole undo history per mutation."
|
|
35851
36791
|
];
|
|
35852
36792
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
35853
36793
|
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
@@ -36044,6 +36984,7 @@ async function src_default(pi) {
|
|
|
36044
36984
|
const configOverrides = buildConfigTierConfigureParams(projectRoot, {
|
|
36045
36985
|
storage_dir: storageDir
|
|
36046
36986
|
});
|
|
36987
|
+
let lspInstallCompletion = null;
|
|
36047
36988
|
try {
|
|
36048
36989
|
const lspAutoInstall = config2.lsp?.auto_install ?? true;
|
|
36049
36990
|
const lspGraceDays = config2.lsp?.grace_days ?? 7;
|
|
@@ -36073,10 +37014,24 @@ async function src_default(pi) {
|
|
|
36073
37014
|
if (lspInflightInstalls.length > 0) {
|
|
36074
37015
|
configOverrides.lsp_inflight_installs = lspInflightInstalls;
|
|
36075
37016
|
}
|
|
36076
|
-
|
|
37017
|
+
const installsWereStarted = npmResult.installsStarted > 0 || ghResult.installsStarted > 0;
|
|
37018
|
+
if (installsWereStarted) {
|
|
36077
37019
|
log2(`[lsp] auto-install: ${npmResult.installsStarted} npm + ${ghResult.installsStarted} github install(s) running in background`);
|
|
36078
37020
|
}
|
|
36079
|
-
Promise.all([npmResult.installsComplete, ghResult.installsComplete]).then(() => {
|
|
37021
|
+
const installCompletion = Promise.all([npmResult.installsComplete, ghResult.installsComplete]).then(() => {
|
|
37022
|
+
if (installsWereStarted) {
|
|
37023
|
+
const updatedPaths = [
|
|
37024
|
+
...new Set([...npmResult.getCachedBinDirs(), ...ghResult.getCachedBinDirs()])
|
|
37025
|
+
];
|
|
37026
|
+
if (updatedPaths.length > 0) {
|
|
37027
|
+
configOverrides.lsp_paths_extra = updatedPaths;
|
|
37028
|
+
} else {
|
|
37029
|
+
delete configOverrides.lsp_paths_extra;
|
|
37030
|
+
}
|
|
37031
|
+
return updatedPaths;
|
|
37032
|
+
}
|
|
37033
|
+
return null;
|
|
37034
|
+
}).then((updatedPaths) => {
|
|
36080
37035
|
const actionable = [...npmResult.skipped, ...ghResult.skipped].filter((s) => {
|
|
36081
37036
|
const r = s.reason.toLowerCase();
|
|
36082
37037
|
if (r === "auto_install: false")
|
|
@@ -36091,16 +37046,20 @@ async function src_default(pi) {
|
|
|
36091
37046
|
return false;
|
|
36092
37047
|
return true;
|
|
36093
37048
|
});
|
|
36094
|
-
if (actionable.length
|
|
36095
|
-
|
|
36096
|
-
const lines = actionable.map((s) => ` • ${s.id}: ${s.reason}`).join(`
|
|
37049
|
+
if (actionable.length > 0) {
|
|
37050
|
+
const lines = actionable.map((s) => ` • ${s.id}: ${s.reason}`).join(`
|
|
36097
37051
|
`);
|
|
36098
|
-
|
|
37052
|
+
warn2(`[lsp] skipped or failed to install ${actionable.length} server(s):
|
|
36099
37053
|
${lines}
|
|
36100
37054
|
` + 'Pin a working version with `lsp.versions: { "<package>": "<version>" }` if grace is blocking, ' + "or set `lsp.auto_install: false` to suppress.");
|
|
37055
|
+
}
|
|
37056
|
+
return updatedPaths;
|
|
36101
37057
|
}).catch((err) => {
|
|
36102
37058
|
warn2(`[lsp] install-summary aggregation failed: ${err}`);
|
|
37059
|
+
return null;
|
|
36103
37060
|
});
|
|
37061
|
+
if (installsWereStarted)
|
|
37062
|
+
lspInstallCompletion = installCompletion;
|
|
36104
37063
|
} catch (err) {
|
|
36105
37064
|
warn2(`[lsp] auto-install setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36106
37065
|
}
|
|
@@ -36171,6 +37130,17 @@ ${lines}
|
|
|
36171
37130
|
});
|
|
36172
37131
|
}
|
|
36173
37132
|
});
|
|
37133
|
+
if (lspInstallCompletion) {
|
|
37134
|
+
lspInstallCompletion.then((updatedPaths) => {
|
|
37135
|
+
if (!updatedPaths)
|
|
37136
|
+
return;
|
|
37137
|
+
pushLspPathsAfterAutoInstall(pool, projectRoot, updatedPaths).then(() => {
|
|
37138
|
+
log2(`[lsp] lsp_paths_extra updated after auto-install: ${updatedPaths.length} dirs pushed to live bridges`);
|
|
37139
|
+
}).catch((err) => {
|
|
37140
|
+
warn2(`[lsp] live bridge lsp_paths_extra update failed: ${err}`);
|
|
37141
|
+
});
|
|
37142
|
+
});
|
|
37143
|
+
}
|
|
36174
37144
|
pool.setConfigureOverride("harness", "pi");
|
|
36175
37145
|
pool.setConfigureOverride("aft_search_registered", resolveToolSurface(config2).semantic);
|
|
36176
37146
|
const ctx = { pool, config: config2, storageDir };
|
|
@@ -36283,7 +37253,9 @@ ${lines}
|
|
|
36283
37253
|
}
|
|
36284
37254
|
});
|
|
36285
37255
|
pi.on("input", (_event, extCtx) => {
|
|
36286
|
-
|
|
37256
|
+
const sessionId = resolveSessionId(extCtx);
|
|
37257
|
+
signalSyncWatchAbort(sessionId);
|
|
37258
|
+
signalBashWaitDetachForProject(pool, extCtx.cwd, sessionId);
|
|
36287
37259
|
});
|
|
36288
37260
|
const unregisterShutdownCleanup = registerShutdownCleanup(async () => {
|
|
36289
37261
|
try {
|