@cortexkit/aft-opencode 0.39.4 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +12 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/configure-warnings.d.ts +3 -1
- package/dist/configure-warnings.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1285 -1218
- package/dist/lsp-cache.d.ts +3 -3
- package/dist/lsp-github-install.d.ts +5 -5
- package/dist/lsp-github-probe.d.ts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/patch-parser.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +45 -0
- package/dist/shared/rpc-notifications.d.ts.map +1 -0
- package/dist/shared/rpc-utils.d.ts +10 -3
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/subc-tool-schemas.d.ts +20 -0
- package/dist/subc-tool-schemas.d.ts.map +1 -0
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactoring.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tui.js +1351 -81
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +14 -10
- package/src/shared/rpc-notifications.ts +121 -0
- package/src/shared/rpc-utils.ts +12 -7
- package/src/tui/index.tsx +118 -38
- package/src/tui/sidebar.tsx +12 -6
package/dist/tui.js
CHANGED
|
@@ -4568,45 +4568,45 @@ var require_esprima = __commonJS((exports, module) => {
|
|
|
4568
4568
|
this.errors = [];
|
|
4569
4569
|
this.tolerant = false;
|
|
4570
4570
|
}
|
|
4571
|
-
ErrorHandler2.prototype.recordError = function(
|
|
4572
|
-
this.errors.push(
|
|
4571
|
+
ErrorHandler2.prototype.recordError = function(error2) {
|
|
4572
|
+
this.errors.push(error2);
|
|
4573
4573
|
};
|
|
4574
|
-
ErrorHandler2.prototype.tolerate = function(
|
|
4574
|
+
ErrorHandler2.prototype.tolerate = function(error2) {
|
|
4575
4575
|
if (this.tolerant) {
|
|
4576
|
-
this.recordError(
|
|
4576
|
+
this.recordError(error2);
|
|
4577
4577
|
} else {
|
|
4578
|
-
throw
|
|
4578
|
+
throw error2;
|
|
4579
4579
|
}
|
|
4580
4580
|
};
|
|
4581
4581
|
ErrorHandler2.prototype.constructError = function(msg, column) {
|
|
4582
|
-
var
|
|
4582
|
+
var error2 = new Error(msg);
|
|
4583
4583
|
try {
|
|
4584
|
-
throw
|
|
4584
|
+
throw error2;
|
|
4585
4585
|
} catch (base) {
|
|
4586
4586
|
if (Object.create && Object.defineProperty) {
|
|
4587
|
-
|
|
4588
|
-
Object.defineProperty(
|
|
4587
|
+
error2 = Object.create(base);
|
|
4588
|
+
Object.defineProperty(error2, "column", { value: column });
|
|
4589
4589
|
}
|
|
4590
4590
|
}
|
|
4591
|
-
return
|
|
4591
|
+
return error2;
|
|
4592
4592
|
};
|
|
4593
4593
|
ErrorHandler2.prototype.createError = function(index, line, col, description) {
|
|
4594
4594
|
var msg = "Line " + line + ": " + description;
|
|
4595
|
-
var
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
return
|
|
4595
|
+
var error2 = this.constructError(msg, col);
|
|
4596
|
+
error2.index = index;
|
|
4597
|
+
error2.lineNumber = line;
|
|
4598
|
+
error2.description = description;
|
|
4599
|
+
return error2;
|
|
4600
4600
|
};
|
|
4601
4601
|
ErrorHandler2.prototype.throwError = function(index, line, col, description) {
|
|
4602
4602
|
throw this.createError(index, line, col, description);
|
|
4603
4603
|
};
|
|
4604
4604
|
ErrorHandler2.prototype.tolerateError = function(index, line, col, description) {
|
|
4605
|
-
var
|
|
4605
|
+
var error2 = this.createError(index, line, col, description);
|
|
4606
4606
|
if (this.tolerant) {
|
|
4607
|
-
this.recordError(
|
|
4607
|
+
this.recordError(error2);
|
|
4608
4608
|
} else {
|
|
4609
|
-
throw
|
|
4609
|
+
throw error2;
|
|
4610
4610
|
}
|
|
4611
4611
|
};
|
|
4612
4612
|
return ErrorHandler2;
|
|
@@ -7276,19 +7276,19 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
7276
7276
|
var symbolFor = (prefix) => Symbol.for(last_prop !== UNDEFINED ? prefix + COLON + last_prop : prefix);
|
|
7277
7277
|
var transform = (k, { value, context = {} }) => reviver ? reviver(k, value, context) : value;
|
|
7278
7278
|
var unexpected = () => {
|
|
7279
|
-
const
|
|
7280
|
-
Object.assign(
|
|
7279
|
+
const error2 = new SyntaxError(`Unexpected token '${current.value.slice(0, 1)}', "${current_code}" is not valid JSON`);
|
|
7280
|
+
Object.assign(error2, current.loc.start);
|
|
7281
7281
|
free();
|
|
7282
|
-
throw
|
|
7282
|
+
throw error2;
|
|
7283
7283
|
};
|
|
7284
7284
|
var unexpected_end = () => {
|
|
7285
|
-
const
|
|
7286
|
-
Object.assign(
|
|
7285
|
+
const error2 = new SyntaxError("Unexpected end of JSON input");
|
|
7286
|
+
Object.assign(error2, last ? last.loc.end : {
|
|
7287
7287
|
line: 1,
|
|
7288
7288
|
column: 0
|
|
7289
7289
|
});
|
|
7290
7290
|
free();
|
|
7291
|
-
throw
|
|
7291
|
+
throw error2;
|
|
7292
7292
|
};
|
|
7293
7293
|
var next = () => {
|
|
7294
7294
|
const new_token = tokens[++index];
|
|
@@ -7637,10 +7637,10 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
7637
7637
|
replacer = null;
|
|
7638
7638
|
indent = EMPTY;
|
|
7639
7639
|
};
|
|
7640
|
-
var
|
|
7640
|
+
var join5 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
|
|
7641
7641
|
var join_content = (inside, value, gap) => {
|
|
7642
7642
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
7643
|
-
return
|
|
7643
|
+
return join5(comment, inside, gap);
|
|
7644
7644
|
};
|
|
7645
7645
|
var stringify_string = (holder, key, value) => {
|
|
7646
7646
|
const raw = get_raw_string_literal(holder, key);
|
|
@@ -7662,13 +7662,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
7662
7662
|
if (i !== 0) {
|
|
7663
7663
|
inside += COMMA;
|
|
7664
7664
|
}
|
|
7665
|
-
const before =
|
|
7665
|
+
const before = join5(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
|
|
7666
7666
|
inside += before || LF + deeper_gap;
|
|
7667
7667
|
inside += stringify(i, value, deeper_gap) || STR_NULL;
|
|
7668
7668
|
inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
|
|
7669
7669
|
after_comma = process_comments(value, AFTER(i), deeper_gap);
|
|
7670
7670
|
}
|
|
7671
|
-
inside +=
|
|
7671
|
+
inside += join5(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
7672
7672
|
return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
|
|
7673
7673
|
};
|
|
7674
7674
|
var object_stringify = (value, gap) => {
|
|
@@ -7689,13 +7689,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
7689
7689
|
inside += COMMA;
|
|
7690
7690
|
}
|
|
7691
7691
|
first = false;
|
|
7692
|
-
const before =
|
|
7692
|
+
const before = join5(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
|
|
7693
7693
|
inside += before || LF + deeper_gap;
|
|
7694
7694
|
inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
|
|
7695
7695
|
after_comma = process_comments(value, AFTER(key), deeper_gap);
|
|
7696
7696
|
};
|
|
7697
7697
|
keys.forEach(iteratee);
|
|
7698
|
-
inside +=
|
|
7698
|
+
inside += join5(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
7699
7699
|
return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
|
|
7700
7700
|
};
|
|
7701
7701
|
function stringify(key, holder, gap) {
|
|
@@ -7790,26 +7790,1244 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
7790
7790
|
// src/tui/index.tsx
|
|
7791
7791
|
import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
|
|
7792
7792
|
// package.json
|
|
7793
|
-
var version = "0.
|
|
7793
|
+
var version = "0.40.1";
|
|
7794
7794
|
|
|
7795
7795
|
// src/shared/rpc-client.ts
|
|
7796
7796
|
import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
7797
|
-
import { join as
|
|
7797
|
+
import { join as join3 } from "node:path";
|
|
7798
7798
|
|
|
7799
7799
|
// src/shared/rpc-utils.ts
|
|
7800
|
-
import {
|
|
7800
|
+
import { join as join2 } from "node:path";
|
|
7801
|
+
|
|
7802
|
+
// ../aft-bridge/dist/active-logger.js
|
|
7803
|
+
var ACTIVE_LOGGER_SYMBOL = Symbol.for("aft-bridge-active-logger");
|
|
7804
|
+
function loggerGlobal() {
|
|
7805
|
+
return globalThis;
|
|
7806
|
+
}
|
|
7807
|
+
function getActiveLogger() {
|
|
7808
|
+
return loggerGlobal()[ACTIVE_LOGGER_SYMBOL];
|
|
7809
|
+
}
|
|
7810
|
+
function getLogFilePath() {
|
|
7811
|
+
try {
|
|
7812
|
+
return getActiveLogger()?.getLogFilePath?.();
|
|
7813
|
+
} catch (err) {
|
|
7814
|
+
console.error(`[aft-bridge] ERROR: active logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7815
|
+
return;
|
|
7816
|
+
}
|
|
7817
|
+
}
|
|
7818
|
+
function log(message, meta) {
|
|
7819
|
+
const active = getActiveLogger();
|
|
7820
|
+
if (active) {
|
|
7821
|
+
try {
|
|
7822
|
+
active.log(message, meta);
|
|
7823
|
+
} catch (err) {
|
|
7824
|
+
console.error(`[aft-bridge] ERROR: active logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7825
|
+
console.error(`[aft-bridge] ${message}`);
|
|
7826
|
+
}
|
|
7827
|
+
} else {
|
|
7828
|
+
console.error(`[aft-bridge] ${message}`);
|
|
7829
|
+
}
|
|
7830
|
+
}
|
|
7831
|
+
function warn(message, meta) {
|
|
7832
|
+
const active = getActiveLogger();
|
|
7833
|
+
if (active) {
|
|
7834
|
+
try {
|
|
7835
|
+
active.warn(message, meta);
|
|
7836
|
+
} catch (err) {
|
|
7837
|
+
console.error(`[aft-bridge] ERROR: active logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7838
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
7839
|
+
}
|
|
7840
|
+
} else {
|
|
7841
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
7842
|
+
}
|
|
7843
|
+
}
|
|
7844
|
+
function error(message, meta) {
|
|
7845
|
+
const active = getActiveLogger();
|
|
7846
|
+
if (active) {
|
|
7847
|
+
try {
|
|
7848
|
+
active.error(message, meta);
|
|
7849
|
+
} catch (err) {
|
|
7850
|
+
console.error(`[aft-bridge] ERROR: active logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7851
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
7852
|
+
}
|
|
7853
|
+
} else {
|
|
7854
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
// ../aft-bridge/dist/bridge.js
|
|
7858
|
+
import { spawn } from "node:child_process";
|
|
7859
|
+
import { homedir } from "node:os";
|
|
7801
7860
|
import { join } from "node:path";
|
|
7861
|
+
import { StringDecoder } from "node:string_decoder";
|
|
7862
|
+
|
|
7863
|
+
// ../aft-bridge/dist/command-timeouts.js
|
|
7864
|
+
var PASSIVE_COMMANDS = new Set(["status"]);
|
|
7865
|
+
var PASSIVE_COMMAND_TIMEOUT_MS = 5000;
|
|
7866
|
+
function isPassiveCommand(command) {
|
|
7867
|
+
return PASSIVE_COMMANDS.has(command);
|
|
7868
|
+
}
|
|
7869
|
+
|
|
7870
|
+
// ../aft-bridge/dist/status-bar.js
|
|
7871
|
+
function parseStatusBarCounts(value) {
|
|
7872
|
+
if (!value || typeof value !== "object")
|
|
7873
|
+
return;
|
|
7874
|
+
const record = value;
|
|
7875
|
+
const num = (key) => {
|
|
7876
|
+
const raw = record[key];
|
|
7877
|
+
return typeof raw === "number" && Number.isFinite(raw) ? raw : 0;
|
|
7878
|
+
};
|
|
7879
|
+
return {
|
|
7880
|
+
errors: num("errors"),
|
|
7881
|
+
warnings: num("warnings"),
|
|
7882
|
+
dead_code: num("dead_code"),
|
|
7883
|
+
unused_exports: num("unused_exports"),
|
|
7884
|
+
duplicates: num("duplicates"),
|
|
7885
|
+
todos: num("todos"),
|
|
7886
|
+
tier2_stale: record.tier2_stale === true
|
|
7887
|
+
};
|
|
7888
|
+
}
|
|
7889
|
+
|
|
7890
|
+
// ../aft-bridge/dist/bridge.js
|
|
7891
|
+
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
7892
|
+
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
7893
|
+
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
7894
|
+
var STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
7895
|
+
var TERMINAL_BASH_STATUSES = new Set([
|
|
7896
|
+
"completed",
|
|
7897
|
+
"failed",
|
|
7898
|
+
"killed",
|
|
7899
|
+
"timed_out",
|
|
7900
|
+
"cancelled",
|
|
7901
|
+
"timeout"
|
|
7902
|
+
]);
|
|
7903
|
+
function isTerminalBashStatus(status) {
|
|
7904
|
+
return typeof status === "string" && TERMINAL_BASH_STATUSES.has(status);
|
|
7905
|
+
}
|
|
7906
|
+
function bashTaskIdFrom(response) {
|
|
7907
|
+
const snakeCase = response.task_id;
|
|
7908
|
+
if (typeof snakeCase === "string" && snakeCase.length > 0)
|
|
7909
|
+
return snakeCase;
|
|
7910
|
+
const camelCase = response.taskId;
|
|
7911
|
+
if (typeof camelCase === "string" && camelCase.length > 0)
|
|
7912
|
+
return camelCase;
|
|
7913
|
+
return;
|
|
7914
|
+
}
|
|
7915
|
+
function tagStderrLine(line) {
|
|
7916
|
+
return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
|
|
7917
|
+
}
|
|
7918
|
+
var BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo";
|
|
7919
|
+
function shouldSurfaceStderrLine(line) {
|
|
7920
|
+
const normalized = line.trim();
|
|
7921
|
+
return !(normalized === `Error in cpuinfo: ${BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE}` || normalized === BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE);
|
|
7922
|
+
}
|
|
7923
|
+
function compareSemver(a, b) {
|
|
7924
|
+
const [aMain, aPre] = a.split("-", 2);
|
|
7925
|
+
const [bMain, bPre] = b.split("-", 2);
|
|
7926
|
+
const aParts = aMain.split(".").map(Number);
|
|
7927
|
+
const bParts = bMain.split(".").map(Number);
|
|
7928
|
+
for (let i = 0;i < 3; i++) {
|
|
7929
|
+
if (aParts[i] !== bParts[i])
|
|
7930
|
+
return (aParts[i] ?? 0) - (bParts[i] ?? 0);
|
|
7931
|
+
}
|
|
7932
|
+
if (!aPre && !bPre)
|
|
7933
|
+
return 0;
|
|
7934
|
+
if (!aPre)
|
|
7935
|
+
return 1;
|
|
7936
|
+
if (!bPre)
|
|
7937
|
+
return -1;
|
|
7938
|
+
const aIds = aPre.split(".");
|
|
7939
|
+
const bIds = bPre.split(".");
|
|
7940
|
+
for (let i = 0;i < Math.max(aIds.length, bIds.length); i++) {
|
|
7941
|
+
const ai = aIds[i];
|
|
7942
|
+
const bi = bIds[i];
|
|
7943
|
+
if (ai === undefined)
|
|
7944
|
+
return -1;
|
|
7945
|
+
if (bi === undefined)
|
|
7946
|
+
return 1;
|
|
7947
|
+
const aNum = /^\d+$/.test(ai);
|
|
7948
|
+
const bNum = /^\d+$/.test(bi);
|
|
7949
|
+
if (aNum && bNum) {
|
|
7950
|
+
const diff = Number.parseInt(ai, 10) - Number.parseInt(bi, 10);
|
|
7951
|
+
if (diff !== 0)
|
|
7952
|
+
return diff;
|
|
7953
|
+
} else if (aNum) {
|
|
7954
|
+
return -1;
|
|
7955
|
+
} else if (bNum) {
|
|
7956
|
+
return 1;
|
|
7957
|
+
} else {
|
|
7958
|
+
const cmp = ai.localeCompare(bi);
|
|
7959
|
+
if (cmp !== 0)
|
|
7960
|
+
return cmp;
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
return 0;
|
|
7964
|
+
}
|
|
7965
|
+
function coerceConfigureDroppedKeys(value) {
|
|
7966
|
+
if (!Array.isArray(value))
|
|
7967
|
+
return [];
|
|
7968
|
+
const dropped = [];
|
|
7969
|
+
for (const item of value) {
|
|
7970
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
7971
|
+
continue;
|
|
7972
|
+
const record = item;
|
|
7973
|
+
if (typeof record.key === "string" && typeof record.tier === "string" && typeof record.reason === "string") {
|
|
7974
|
+
dropped.push({ key: record.key, tier: record.tier, reason: record.reason });
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
return dropped;
|
|
7978
|
+
}
|
|
7979
|
+
|
|
7980
|
+
class BridgeReplacedDuringVersionCheck extends Error {
|
|
7981
|
+
newBinaryPath;
|
|
7982
|
+
constructor(newBinaryPath) {
|
|
7983
|
+
super(`Bridge binary replaced during version check: ${newBinaryPath}`);
|
|
7984
|
+
this.newBinaryPath = newBinaryPath;
|
|
7985
|
+
this.name = "BridgeReplacedDuringVersionCheck";
|
|
7986
|
+
}
|
|
7987
|
+
}
|
|
7988
|
+
|
|
7989
|
+
class BridgeTransportTimeoutError extends Error {
|
|
7990
|
+
command;
|
|
7991
|
+
timeoutMs;
|
|
7992
|
+
code = "transport_timeout";
|
|
7993
|
+
constructor(command, timeoutMs, message) {
|
|
7994
|
+
super(message);
|
|
7995
|
+
this.command = command;
|
|
7996
|
+
this.timeoutMs = timeoutMs;
|
|
7997
|
+
this.name = "BridgeTransportTimeoutError";
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
class BinaryBridge {
|
|
8001
|
+
static RESTART_RESET_MS = 5 * 60 * 1000;
|
|
8002
|
+
static STDERR_TAIL_MAX = 20;
|
|
8003
|
+
binaryPath;
|
|
8004
|
+
cwd;
|
|
8005
|
+
process = null;
|
|
8006
|
+
pending = new Map;
|
|
8007
|
+
outstandingBackgroundTaskIds = new Set;
|
|
8008
|
+
nextId = 1;
|
|
8009
|
+
stdoutBuffer = "";
|
|
8010
|
+
stdoutReadOffset = 0;
|
|
8011
|
+
stderrBuffer = "";
|
|
8012
|
+
stderrTail = [];
|
|
8013
|
+
_restartCount = 0;
|
|
8014
|
+
_shuttingDown = false;
|
|
8015
|
+
timeoutMs;
|
|
8016
|
+
hangThreshold;
|
|
8017
|
+
maxRestarts;
|
|
8018
|
+
configured = false;
|
|
8019
|
+
_configurePromise = null;
|
|
8020
|
+
configOverrides;
|
|
8021
|
+
minVersion;
|
|
8022
|
+
onVersionMismatch;
|
|
8023
|
+
onConfigureWarnings;
|
|
8024
|
+
onBashCompletion;
|
|
8025
|
+
onBashLongRunning;
|
|
8026
|
+
onBashPatternMatch;
|
|
8027
|
+
cachedStatus = null;
|
|
8028
|
+
statusListeners = new Set;
|
|
8029
|
+
configureWarningClients = new Map;
|
|
8030
|
+
restartResetTimer = null;
|
|
8031
|
+
lastChildActivityAt = 0;
|
|
8032
|
+
lastStatusBar;
|
|
8033
|
+
consecutiveRequestTimeouts = 0;
|
|
8034
|
+
errorPrefix;
|
|
8035
|
+
logger;
|
|
8036
|
+
childEnv;
|
|
8037
|
+
constructor(binaryPath, cwd, options, configOverrides) {
|
|
8038
|
+
this.binaryPath = binaryPath;
|
|
8039
|
+
this.cwd = cwd;
|
|
8040
|
+
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
8041
|
+
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
8042
|
+
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
8043
|
+
this.configOverrides = configOverrides ?? {};
|
|
8044
|
+
this.minVersion = options?.minVersion;
|
|
8045
|
+
this.onVersionMismatch = options?.onVersionMismatch;
|
|
8046
|
+
this.onConfigureWarnings = options?.onConfigureWarnings;
|
|
8047
|
+
this.onBashCompletion = options?.onBashCompletion;
|
|
8048
|
+
this.onBashLongRunning = options?.onBashLongRunning;
|
|
8049
|
+
this.onBashPatternMatch = options?.onBashPatternMatch;
|
|
8050
|
+
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
8051
|
+
this.logger = options?.logger;
|
|
8052
|
+
this.childEnv = options?.childEnv;
|
|
8053
|
+
}
|
|
8054
|
+
logVia(message, meta) {
|
|
8055
|
+
const logger = this.logger ?? getActiveLogger();
|
|
8056
|
+
if (logger) {
|
|
8057
|
+
try {
|
|
8058
|
+
logger.log(message, meta);
|
|
8059
|
+
} catch (err) {
|
|
8060
|
+
console.error(`[aft-bridge] ERROR: logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8061
|
+
console.error(`[aft-bridge] ${message}`);
|
|
8062
|
+
}
|
|
8063
|
+
} else {
|
|
8064
|
+
log(message, meta);
|
|
8065
|
+
}
|
|
8066
|
+
}
|
|
8067
|
+
warnVia(message, meta) {
|
|
8068
|
+
const logger = this.logger ?? getActiveLogger();
|
|
8069
|
+
if (logger) {
|
|
8070
|
+
try {
|
|
8071
|
+
logger.warn(message, meta);
|
|
8072
|
+
} catch (err) {
|
|
8073
|
+
console.error(`[aft-bridge] ERROR: logger warn threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8074
|
+
console.error(`[aft-bridge] WARN: ${message}`);
|
|
8075
|
+
}
|
|
8076
|
+
} else {
|
|
8077
|
+
warn(message, meta);
|
|
8078
|
+
}
|
|
8079
|
+
}
|
|
8080
|
+
errorVia(message, meta) {
|
|
8081
|
+
const logger = this.logger ?? getActiveLogger();
|
|
8082
|
+
if (logger) {
|
|
8083
|
+
try {
|
|
8084
|
+
logger.error(message, meta);
|
|
8085
|
+
} catch (err) {
|
|
8086
|
+
console.error(`[aft-bridge] ERROR: logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8087
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
8088
|
+
}
|
|
8089
|
+
} else {
|
|
8090
|
+
error(message, meta);
|
|
8091
|
+
}
|
|
8092
|
+
}
|
|
8093
|
+
getLogFilePathVia() {
|
|
8094
|
+
if (this.logger?.getLogFilePath) {
|
|
8095
|
+
try {
|
|
8096
|
+
return this.logger.getLogFilePath();
|
|
8097
|
+
} catch (err) {
|
|
8098
|
+
console.error(`[aft-bridge] ERROR: logger getLogFilePath threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8099
|
+
return;
|
|
8100
|
+
}
|
|
8101
|
+
}
|
|
8102
|
+
return getLogFilePath();
|
|
8103
|
+
}
|
|
8104
|
+
sessionLogVia(sessionId, message) {
|
|
8105
|
+
this.logVia(message, sessionId ? { sessionId } : undefined);
|
|
8106
|
+
}
|
|
8107
|
+
sessionWarnVia(sessionId, message) {
|
|
8108
|
+
this.warnVia(message, sessionId ? { sessionId } : undefined);
|
|
8109
|
+
}
|
|
8110
|
+
sessionErrorVia(sessionId, message) {
|
|
8111
|
+
this.errorVia(message, sessionId ? { sessionId } : undefined);
|
|
8112
|
+
}
|
|
8113
|
+
get restartCount() {
|
|
8114
|
+
return this._restartCount;
|
|
8115
|
+
}
|
|
8116
|
+
isAlive() {
|
|
8117
|
+
return this.process !== null && this.process.exitCode === null && !this.process.killed;
|
|
8118
|
+
}
|
|
8119
|
+
hasPendingRequests() {
|
|
8120
|
+
return this.pending.size > 0;
|
|
8121
|
+
}
|
|
8122
|
+
hasOutstandingBackgroundTasks() {
|
|
8123
|
+
return this.outstandingBackgroundTaskIds.size > 0;
|
|
8124
|
+
}
|
|
8125
|
+
getCwd() {
|
|
8126
|
+
return this.cwd;
|
|
8127
|
+
}
|
|
8128
|
+
getCachedStatus() {
|
|
8129
|
+
return this.cachedStatus;
|
|
8130
|
+
}
|
|
8131
|
+
subscribeStatus(listener) {
|
|
8132
|
+
this.statusListeners.add(listener);
|
|
8133
|
+
if (this.cachedStatus !== null) {
|
|
8134
|
+
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
8135
|
+
}
|
|
8136
|
+
return () => {
|
|
8137
|
+
this.statusListeners.delete(listener);
|
|
8138
|
+
};
|
|
8139
|
+
}
|
|
8140
|
+
cacheStatusSnapshot(snapshot) {
|
|
8141
|
+
this.cachedStatus = snapshot;
|
|
8142
|
+
}
|
|
8143
|
+
async send(command, params = {}, options) {
|
|
8144
|
+
return this.sendWithVersionMismatchRetry(command, params, options, true);
|
|
8145
|
+
}
|
|
8146
|
+
async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
|
|
8147
|
+
try {
|
|
8148
|
+
if (this._shuttingDown) {
|
|
8149
|
+
throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
|
|
8150
|
+
}
|
|
8151
|
+
if (Object.hasOwn(params, "id")) {
|
|
8152
|
+
throw new Error("params cannot contain reserved key 'id'");
|
|
8153
|
+
}
|
|
8154
|
+
const requestSessionId = typeof params.session_id === "string" && params.session_id.length > 0 ? params.session_id : undefined;
|
|
8155
|
+
this.ensureSpawned(requestSessionId);
|
|
8156
|
+
if (requestSessionId && options?.configureWarningClient !== undefined) {
|
|
8157
|
+
this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
|
|
8158
|
+
}
|
|
8159
|
+
const passive = isPassiveCommand(command);
|
|
8160
|
+
const resolvedTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
8161
|
+
const effectiveTimeoutMs = passive ? Math.min(resolvedTimeoutMs, PASSIVE_COMMAND_TIMEOUT_MS) : resolvedTimeoutMs;
|
|
8162
|
+
const implicitTransportOptions = {
|
|
8163
|
+
...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
|
|
8164
|
+
markConfiguredOnSuccess: false
|
|
8165
|
+
};
|
|
8166
|
+
if (!this.configured) {
|
|
8167
|
+
if (command !== "configure" && command !== "version") {
|
|
8168
|
+
if (!this._configurePromise) {
|
|
8169
|
+
const sessionIdForConfigure = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
8170
|
+
this._configurePromise = (async () => {
|
|
8171
|
+
try {
|
|
8172
|
+
const configResult = await this.send("configure", {
|
|
8173
|
+
project_root: this.cwd,
|
|
8174
|
+
...this.configOverrides,
|
|
8175
|
+
...sessionIdForConfigure ? { session_id: sessionIdForConfigure } : {}
|
|
8176
|
+
}, implicitTransportOptions);
|
|
8177
|
+
if (configResult.success === false) {
|
|
8178
|
+
throw new Error(`${this.errorPrefix} Configure failed: ${configResult.message ?? "unknown error"}`);
|
|
8179
|
+
}
|
|
8180
|
+
await this.deliverConfigureWarnings(configResult, params, options);
|
|
8181
|
+
await this.checkVersion(implicitTransportOptions);
|
|
8182
|
+
if (!this.isAlive()) {
|
|
8183
|
+
throw new Error(`${this.errorPrefix} Bridge died during version check. Check logs: ${this.getLogFilePathVia()}`);
|
|
8184
|
+
}
|
|
8185
|
+
this.configured = true;
|
|
8186
|
+
} finally {
|
|
8187
|
+
this._configurePromise = null;
|
|
8188
|
+
}
|
|
8189
|
+
})();
|
|
8190
|
+
}
|
|
8191
|
+
await this._configurePromise;
|
|
8192
|
+
}
|
|
8193
|
+
}
|
|
8194
|
+
const id = String(this.nextId++);
|
|
8195
|
+
let request;
|
|
8196
|
+
if (Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
|
|
8197
|
+
const nested = { ...params };
|
|
8198
|
+
const reserved = {};
|
|
8199
|
+
for (const key of ["session_id", "lsp_hints"]) {
|
|
8200
|
+
if (Object.hasOwn(nested, key)) {
|
|
8201
|
+
reserved[key] = nested[key];
|
|
8202
|
+
delete nested[key];
|
|
8203
|
+
}
|
|
8204
|
+
}
|
|
8205
|
+
request = { id, command, ...reserved, params: nested };
|
|
8206
|
+
} else {
|
|
8207
|
+
request = { id, command, ...params };
|
|
8208
|
+
}
|
|
8209
|
+
const line = `${JSON.stringify(request)}
|
|
8210
|
+
`;
|
|
8211
|
+
const keepBridgeOnTimeout = passive || options?.keepBridgeOnTimeout === true;
|
|
8212
|
+
let requestSentAt = Date.now();
|
|
8213
|
+
const response = await new Promise((resolve, reject) => {
|
|
8214
|
+
const timer = setTimeout(() => {
|
|
8215
|
+
const entry = this.pending.get(id);
|
|
8216
|
+
if (!entry)
|
|
8217
|
+
return;
|
|
8218
|
+
this.pending.delete(id);
|
|
8219
|
+
clearTimeout(entry.timer);
|
|
8220
|
+
if (keepBridgeOnTimeout) {
|
|
8221
|
+
const timeoutMsg2 = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`;
|
|
8222
|
+
if (requestSessionId) {
|
|
8223
|
+
this.sessionWarnVia(requestSessionId, timeoutMsg2);
|
|
8224
|
+
} else {
|
|
8225
|
+
this.warnVia(timeoutMsg2);
|
|
8226
|
+
}
|
|
8227
|
+
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
8228
|
+
return;
|
|
8229
|
+
}
|
|
8230
|
+
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
8231
|
+
const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
|
|
8232
|
+
this.consecutiveRequestTimeouts = consecutiveTimeouts;
|
|
8233
|
+
const keepWarm = childActiveSinceRequest || consecutiveTimeouts < this.hangThreshold;
|
|
8234
|
+
const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
|
|
8235
|
+
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
8236
|
+
if (requestSessionId) {
|
|
8237
|
+
this.sessionWarnVia(requestSessionId, timeoutMsg);
|
|
8238
|
+
} else {
|
|
8239
|
+
this.warnVia(timeoutMsg);
|
|
8240
|
+
}
|
|
8241
|
+
if (keepWarm) {
|
|
8242
|
+
entry.reject(new Error(`${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry`));
|
|
8243
|
+
return;
|
|
8244
|
+
}
|
|
8245
|
+
entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
8246
|
+
this.handleTimeout(requestSessionId);
|
|
8247
|
+
}, effectiveTimeoutMs);
|
|
8248
|
+
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress, command });
|
|
8249
|
+
if (!this.process?.stdin?.writable) {
|
|
8250
|
+
this.pending.delete(id);
|
|
8251
|
+
clearTimeout(timer);
|
|
8252
|
+
reject(new Error(`${this.errorPrefix} stdin not writable for command "${command}"`));
|
|
8253
|
+
return;
|
|
8254
|
+
}
|
|
8255
|
+
requestSentAt = Date.now();
|
|
8256
|
+
this.process.stdin.write(line, (err) => {
|
|
8257
|
+
if (err) {
|
|
8258
|
+
const entry = this.pending.get(id);
|
|
8259
|
+
if (entry) {
|
|
8260
|
+
this.pending.delete(id);
|
|
8261
|
+
clearTimeout(entry.timer);
|
|
8262
|
+
entry.reject(new Error(`${this.errorPrefix} Failed to write to stdin: ${err.message}`));
|
|
8263
|
+
}
|
|
8264
|
+
}
|
|
8265
|
+
});
|
|
8266
|
+
});
|
|
8267
|
+
if (command === "configure" && response.success === true && options?.markConfiguredOnSuccess !== false) {
|
|
8268
|
+
this.configured = true;
|
|
8269
|
+
}
|
|
8270
|
+
return response;
|
|
8271
|
+
} catch (err) {
|
|
8272
|
+
if (err instanceof BridgeReplacedDuringVersionCheck && canRetryAfterVersionSwap && command !== "configure" && command !== "version") {
|
|
8273
|
+
this.logVia(`Retrying request "${command}" once after coordinated binary replacement: ${err.newBinaryPath}`);
|
|
8274
|
+
return this.sendWithVersionMismatchRetry(command, params, options, false);
|
|
8275
|
+
}
|
|
8276
|
+
throw err;
|
|
8277
|
+
}
|
|
8278
|
+
}
|
|
8279
|
+
async deliverConfigureWarnings(configResult, params, options) {
|
|
8280
|
+
if (!this.onConfigureWarnings)
|
|
8281
|
+
return;
|
|
8282
|
+
const warnings = Array.isArray(configResult.warnings) ? configResult.warnings : [];
|
|
8283
|
+
const configDroppedKeys = coerceConfigureDroppedKeys(configResult.config_dropped_keys);
|
|
8284
|
+
if (warnings.length === 0 && configDroppedKeys.length === 0)
|
|
8285
|
+
return;
|
|
8286
|
+
const sessionId = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
8287
|
+
const context = {
|
|
8288
|
+
projectRoot: this.cwd,
|
|
8289
|
+
sessionId,
|
|
8290
|
+
client: options?.configureWarningClient ?? (sessionId ? this.configureWarningClients.get(sessionId) : undefined),
|
|
8291
|
+
warnings
|
|
8292
|
+
};
|
|
8293
|
+
if (configDroppedKeys.length > 0) {
|
|
8294
|
+
context.configDroppedKeys = configDroppedKeys;
|
|
8295
|
+
}
|
|
8296
|
+
try {
|
|
8297
|
+
await this.onConfigureWarnings(context);
|
|
8298
|
+
} catch (err) {
|
|
8299
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8300
|
+
} finally {
|
|
8301
|
+
if (sessionId) {
|
|
8302
|
+
this.configureWarningClients.delete(sessionId);
|
|
8303
|
+
}
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
8306
|
+
async handleConfigureWarningsFrame(frame) {
|
|
8307
|
+
if (!this.onConfigureWarnings)
|
|
8308
|
+
return;
|
|
8309
|
+
const warnings = frame.warnings;
|
|
8310
|
+
if (!Array.isArray(warnings) || warnings.length === 0)
|
|
8311
|
+
return;
|
|
8312
|
+
const projectRoot = typeof frame.project_root === "string" ? frame.project_root : this.cwd;
|
|
8313
|
+
const rawSessionId = frame.session_id;
|
|
8314
|
+
const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : null;
|
|
8315
|
+
try {
|
|
8316
|
+
await this.onConfigureWarnings({
|
|
8317
|
+
projectRoot,
|
|
8318
|
+
sessionId,
|
|
8319
|
+
client: sessionId ? this.configureWarningClients.get(sessionId) : undefined,
|
|
8320
|
+
warnings
|
|
8321
|
+
});
|
|
8322
|
+
} finally {
|
|
8323
|
+
if (sessionId) {
|
|
8324
|
+
this.configureWarningClients.delete(sessionId);
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
handleStatusChangedFrame(frame) {
|
|
8329
|
+
const snapshot = frame.snapshot;
|
|
8330
|
+
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
8331
|
+
return;
|
|
8332
|
+
this.cachedStatus = snapshot;
|
|
8333
|
+
for (const listener of this.statusListeners) {
|
|
8334
|
+
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
deliverStatusSnapshot(listener, snapshot) {
|
|
8338
|
+
try {
|
|
8339
|
+
listener(snapshot);
|
|
8340
|
+
} catch (err) {
|
|
8341
|
+
this.warnVia(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
async shutdown() {
|
|
8345
|
+
this._shuttingDown = true;
|
|
8346
|
+
this.clearRestartResetTimer();
|
|
8347
|
+
this.configureWarningClients.clear();
|
|
8348
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge shutting down`));
|
|
8349
|
+
if (this.process) {
|
|
8350
|
+
const proc = this.process;
|
|
8351
|
+
this.process = null;
|
|
8352
|
+
return new Promise((resolve) => {
|
|
8353
|
+
const forceKillTimer = setTimeout(() => {
|
|
8354
|
+
proc.kill("SIGKILL");
|
|
8355
|
+
resolve();
|
|
8356
|
+
}, 5000);
|
|
8357
|
+
proc.once("exit", () => {
|
|
8358
|
+
clearTimeout(forceKillTimer);
|
|
8359
|
+
this.logVia("Process exited during shutdown");
|
|
8360
|
+
resolve();
|
|
8361
|
+
});
|
|
8362
|
+
proc.kill("SIGTERM");
|
|
8363
|
+
});
|
|
8364
|
+
}
|
|
8365
|
+
}
|
|
8366
|
+
async checkVersion(options) {
|
|
8367
|
+
if (!this.minVersion)
|
|
8368
|
+
return;
|
|
8369
|
+
try {
|
|
8370
|
+
const resp = await this.send("version", {}, options);
|
|
8371
|
+
if (resp.success === false) {
|
|
8372
|
+
throw new Error(`Binary version check failed: ${String(resp.code ?? "unknown")} — likely too old`);
|
|
8373
|
+
}
|
|
8374
|
+
const binaryVersion = resp.version;
|
|
8375
|
+
if (typeof binaryVersion !== "string") {
|
|
8376
|
+
throw new Error(`Binary did not report a version — likely too old (minVersion: ${this.minVersion})`);
|
|
8377
|
+
}
|
|
8378
|
+
this.logVia(`Binary version: ${binaryVersion}`);
|
|
8379
|
+
if (compareSemver(binaryVersion, this.minVersion) < 0) {
|
|
8380
|
+
this.warnVia(`Binary version ${binaryVersion} is older than required ${this.minVersion}`);
|
|
8381
|
+
const replacementPath = await this.onVersionMismatch?.(binaryVersion, this.minVersion);
|
|
8382
|
+
if (replacementPath === undefined) {
|
|
8383
|
+
return;
|
|
8384
|
+
}
|
|
8385
|
+
if (replacementPath === null || replacementPath.length === 0) {
|
|
8386
|
+
throw new Error(`Binary version ${binaryVersion} is older than required ${this.minVersion}; no compatible replacement binary was provided`);
|
|
8387
|
+
}
|
|
8388
|
+
await this.replaceCurrentBinary(replacementPath);
|
|
8389
|
+
throw new BridgeReplacedDuringVersionCheck(replacementPath);
|
|
8390
|
+
}
|
|
8391
|
+
} catch (err) {
|
|
8392
|
+
this.warnVia(`Version check failed: ${err.message}`);
|
|
8393
|
+
throw err;
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
async replaceCurrentBinary(newBinaryPath) {
|
|
8397
|
+
this.binaryPath = newBinaryPath;
|
|
8398
|
+
this.configured = false;
|
|
8399
|
+
this.clearRestartResetTimer();
|
|
8400
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge restarting with updated binary: ${newBinaryPath}`));
|
|
8401
|
+
if (!this.process)
|
|
8402
|
+
return;
|
|
8403
|
+
const proc = this.process;
|
|
8404
|
+
this.process = null;
|
|
8405
|
+
await new Promise((resolve) => {
|
|
8406
|
+
const forceKillTimer = setTimeout(() => {
|
|
8407
|
+
proc.kill("SIGKILL");
|
|
8408
|
+
resolve();
|
|
8409
|
+
}, 5000);
|
|
8410
|
+
proc.once("exit", () => {
|
|
8411
|
+
clearTimeout(forceKillTimer);
|
|
8412
|
+
this.logVia("Process exited during coordinated binary replacement");
|
|
8413
|
+
resolve();
|
|
8414
|
+
});
|
|
8415
|
+
proc.kill("SIGTERM");
|
|
8416
|
+
});
|
|
8417
|
+
}
|
|
8418
|
+
ensureSpawned(triggeringSessionId) {
|
|
8419
|
+
if (this.isAlive())
|
|
8420
|
+
return;
|
|
8421
|
+
this.spawnProcess(triggeringSessionId);
|
|
8422
|
+
}
|
|
8423
|
+
spawnProcess(triggeringSessionId) {
|
|
8424
|
+
this.lastStatusBar = undefined;
|
|
8425
|
+
if (triggeringSessionId) {
|
|
8426
|
+
this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
8427
|
+
} else {
|
|
8428
|
+
this.logVia(`Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
8429
|
+
}
|
|
8430
|
+
const semantic = this.configOverrides.semantic;
|
|
8431
|
+
const semanticBackend = (() => {
|
|
8432
|
+
if (semantic && typeof semantic === "object" && !Array.isArray(semantic)) {
|
|
8433
|
+
const candidate = semantic.backend;
|
|
8434
|
+
return typeof candidate === "string" ? candidate : undefined;
|
|
8435
|
+
}
|
|
8436
|
+
return;
|
|
8437
|
+
})();
|
|
8438
|
+
const useFastembedBackend = semanticBackend === undefined || semanticBackend === "fastembed" || semanticBackend === "";
|
|
8439
|
+
const ortDir = typeof this.configOverrides._ort_dylib_dir === "string" && useFastembedBackend ? this.configOverrides._ort_dylib_dir : null;
|
|
8440
|
+
const ortLibraryPath = ortDir == null ? null : join(ortDir, process.platform === "win32" ? "onnxruntime.dll" : process.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so");
|
|
8441
|
+
const envPath = process.platform === "win32" && ortDir ? `${ortDir};${process.env.PATH ?? ""}` : process.env.PATH;
|
|
8442
|
+
const env = {
|
|
8443
|
+
...process.env,
|
|
8444
|
+
...envPath ? { PATH: envPath } : {}
|
|
8445
|
+
};
|
|
8446
|
+
this.logVia(`bridge.spawnProcess: useFastembedBackend=${useFastembedBackend}, ` + `parentORT=${process.env.ORT_DYLIB_PATH ?? "(unset)"}, ` + `ortLibraryPath=${ortLibraryPath ?? "(none)"}`);
|
|
8447
|
+
if (useFastembedBackend) {
|
|
8448
|
+
env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join(this.configOverrides.storage_dir, "semantic", "models") : join(homedir() || "", ".cache", "fastembed"));
|
|
8449
|
+
if (process.env.ORT_DYLIB_PATH) {
|
|
8450
|
+
this.logVia(`ORT_DYLIB_PATH inherited from parent env: ${process.env.ORT_DYLIB_PATH}`);
|
|
8451
|
+
} else if (ortLibraryPath) {
|
|
8452
|
+
env.ORT_DYLIB_PATH = ortLibraryPath;
|
|
8453
|
+
this.logVia(`ORT_DYLIB_PATH set from managed ONNX Runtime: ${ortLibraryPath}`);
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
if (this.childEnv) {
|
|
8457
|
+
for (const [key, value] of Object.entries(this.childEnv)) {
|
|
8458
|
+
if (value === undefined) {
|
|
8459
|
+
delete env[key];
|
|
8460
|
+
} else {
|
|
8461
|
+
env[key] = value;
|
|
8462
|
+
}
|
|
8463
|
+
}
|
|
8464
|
+
}
|
|
8465
|
+
const child = spawn(this.binaryPath, [], {
|
|
8466
|
+
cwd: this.cwd,
|
|
8467
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
8468
|
+
env
|
|
8469
|
+
});
|
|
8470
|
+
const currentChild = child;
|
|
8471
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
8472
|
+
child.stdout?.on("data", (chunk) => {
|
|
8473
|
+
this.onStdoutData(stdoutDecoder.write(chunk));
|
|
8474
|
+
});
|
|
8475
|
+
child.stdout?.on("end", () => {
|
|
8476
|
+
const remaining = stdoutDecoder.end();
|
|
8477
|
+
if (remaining)
|
|
8478
|
+
this.onStdoutData(remaining);
|
|
8479
|
+
this.flushStdoutBuffer();
|
|
8480
|
+
});
|
|
8481
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
8482
|
+
child.stderr?.on("data", (chunk) => {
|
|
8483
|
+
this.onStderrData(stderrDecoder.write(chunk));
|
|
8484
|
+
});
|
|
8485
|
+
child.stderr?.on("end", () => {
|
|
8486
|
+
const remaining = stderrDecoder.end();
|
|
8487
|
+
if (remaining)
|
|
8488
|
+
this.onStderrData(remaining);
|
|
8489
|
+
this.flushStderrBuffer();
|
|
8490
|
+
});
|
|
8491
|
+
child.on("error", (err) => {
|
|
8492
|
+
if (this.process !== currentChild)
|
|
8493
|
+
return;
|
|
8494
|
+
this.errorVia(`Process error: ${err.message}${this.formatStderrTail()}`);
|
|
8495
|
+
this.handleCrash();
|
|
8496
|
+
});
|
|
8497
|
+
child.on("exit", (code, signal) => {
|
|
8498
|
+
if (this.process !== currentChild)
|
|
8499
|
+
return;
|
|
8500
|
+
if (this._shuttingDown)
|
|
8501
|
+
return;
|
|
8502
|
+
this.flushStdoutBuffer();
|
|
8503
|
+
this.logVia(`Process exited: code=${code}, signal=${signal}`);
|
|
8504
|
+
if (signal === "SIGTERM" || signal === "SIGKILL" || signal === "SIGHUP" || signal === "SIGINT") {
|
|
8505
|
+
this.process = null;
|
|
8506
|
+
this.configured = false;
|
|
8507
|
+
this.clearRestartResetTimer();
|
|
8508
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Binary killed by ${signal}`));
|
|
8509
|
+
return;
|
|
8510
|
+
}
|
|
8511
|
+
this.handleCrash();
|
|
8512
|
+
});
|
|
8513
|
+
this.process = child;
|
|
8514
|
+
this.stdoutBuffer = "";
|
|
8515
|
+
this.stdoutReadOffset = 0;
|
|
8516
|
+
this.stderrBuffer = "";
|
|
8517
|
+
this.lastChildActivityAt = 0;
|
|
8518
|
+
this.consecutiveRequestTimeouts = 0;
|
|
8519
|
+
this.stderrTail = [];
|
|
8520
|
+
}
|
|
8521
|
+
pushStderrLine(line) {
|
|
8522
|
+
this.stderrTail.push(line);
|
|
8523
|
+
if (this.stderrTail.length > BinaryBridge.STDERR_TAIL_MAX) {
|
|
8524
|
+
this.stderrTail.shift();
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
onStderrData(data) {
|
|
8528
|
+
this.stderrBuffer += data;
|
|
8529
|
+
let newlineIdx;
|
|
8530
|
+
while ((newlineIdx = this.stderrBuffer.indexOf(`
|
|
8531
|
+
`)) !== -1) {
|
|
8532
|
+
const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
8533
|
+
this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
|
|
8534
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
8535
|
+
continue;
|
|
8536
|
+
const tagged = tagStderrLine(line);
|
|
8537
|
+
this.logVia(tagged);
|
|
8538
|
+
this.pushStderrLine(tagged);
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8541
|
+
flushStderrBuffer() {
|
|
8542
|
+
const line = this.stderrBuffer.replace(/\r$/, "");
|
|
8543
|
+
this.stderrBuffer = "";
|
|
8544
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
8545
|
+
return;
|
|
8546
|
+
const tagged = tagStderrLine(line);
|
|
8547
|
+
this.logVia(tagged);
|
|
8548
|
+
this.pushStderrLine(tagged);
|
|
8549
|
+
}
|
|
8550
|
+
formatStderrTail() {
|
|
8551
|
+
if (this.stderrTail.length === 0)
|
|
8552
|
+
return "";
|
|
8553
|
+
const tail = this.stderrTail.join(`
|
|
8554
|
+
`);
|
|
8555
|
+
return `
|
|
8556
|
+
--- last ${this.stderrTail.length} stderr lines ---
|
|
8557
|
+
${tail}`;
|
|
8558
|
+
}
|
|
8559
|
+
onStdoutData(data) {
|
|
8560
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD) {
|
|
8561
|
+
this.compactStdoutBuffer();
|
|
8562
|
+
}
|
|
8563
|
+
this.stdoutBuffer += data;
|
|
8564
|
+
if (this.stdoutBuffer.length - this.stdoutReadOffset > MAX_STDOUT_BUFFER) {
|
|
8565
|
+
this.handleCrash(new Error(`aft bridge stdout buffer exceeded ${MAX_STDOUT_BUFFER} bytes — killing bridge`));
|
|
8566
|
+
return;
|
|
8567
|
+
}
|
|
8568
|
+
let newlineIdx;
|
|
8569
|
+
while ((newlineIdx = this.stdoutBuffer.indexOf(`
|
|
8570
|
+
`, this.stdoutReadOffset)) !== -1) {
|
|
8571
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset, newlineIdx).trim();
|
|
8572
|
+
this.stdoutReadOffset = newlineIdx + 1;
|
|
8573
|
+
if (line) {
|
|
8574
|
+
this.processStdoutLine(line);
|
|
8575
|
+
}
|
|
8576
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD && this.stdoutReadOffset > this.stdoutBuffer.length / 2) {
|
|
8577
|
+
this.compactStdoutBuffer();
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8580
|
+
if (this.stdoutReadOffset === this.stdoutBuffer.length) {
|
|
8581
|
+
this.stdoutBuffer = "";
|
|
8582
|
+
this.stdoutReadOffset = 0;
|
|
8583
|
+
}
|
|
8584
|
+
}
|
|
8585
|
+
compactStdoutBuffer() {
|
|
8586
|
+
if (this.stdoutReadOffset === 0)
|
|
8587
|
+
return;
|
|
8588
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(this.stdoutReadOffset);
|
|
8589
|
+
this.stdoutReadOffset = 0;
|
|
8590
|
+
}
|
|
8591
|
+
flushStdoutBuffer() {
|
|
8592
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset).trim();
|
|
8593
|
+
this.stdoutBuffer = "";
|
|
8594
|
+
this.stdoutReadOffset = 0;
|
|
8595
|
+
if (!line)
|
|
8596
|
+
return;
|
|
8597
|
+
this.processStdoutLine(line);
|
|
8598
|
+
}
|
|
8599
|
+
processStdoutLine(line) {
|
|
8600
|
+
try {
|
|
8601
|
+
const response = JSON.parse(line);
|
|
8602
|
+
this.lastChildActivityAt = Date.now();
|
|
8603
|
+
if (response.type === "progress") {
|
|
8604
|
+
const requestId = response.request_id;
|
|
8605
|
+
const entry = requestId ? this.pending.get(requestId) : undefined;
|
|
8606
|
+
const kind = response.kind === "stderr" ? "stderr" : "stdout";
|
|
8607
|
+
const text = typeof response.chunk === "string" ? response.chunk : "";
|
|
8608
|
+
entry?.onProgress?.({ kind, text });
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8611
|
+
if (response.type === "permission_ask") {
|
|
8612
|
+
const requestId = response.request_id;
|
|
8613
|
+
const entry = requestId ? this.pending.get(requestId) : undefined;
|
|
8614
|
+
if (requestId && entry) {
|
|
8615
|
+
this.pending.delete(requestId);
|
|
8616
|
+
clearTimeout(entry.timer);
|
|
8617
|
+
entry.resolve({
|
|
8618
|
+
success: false,
|
|
8619
|
+
code: "permission_required",
|
|
8620
|
+
message: "bash command requires permission",
|
|
8621
|
+
asks: response.asks
|
|
8622
|
+
});
|
|
8623
|
+
}
|
|
8624
|
+
return;
|
|
8625
|
+
}
|
|
8626
|
+
if (response.type === "bash_completed") {
|
|
8627
|
+
const taskId = bashTaskIdFrom(response);
|
|
8628
|
+
if (taskId)
|
|
8629
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
8630
|
+
this.onBashCompletion?.(response, this);
|
|
8631
|
+
return;
|
|
8632
|
+
}
|
|
8633
|
+
if (response.type === "bash_long_running") {
|
|
8634
|
+
this.onBashLongRunning?.(response, this);
|
|
8635
|
+
return;
|
|
8636
|
+
}
|
|
8637
|
+
if (response.type === "bash_pattern_match") {
|
|
8638
|
+
this.onBashPatternMatch?.(response, this);
|
|
8639
|
+
return;
|
|
8640
|
+
}
|
|
8641
|
+
if (response.type === "configure_warnings") {
|
|
8642
|
+
this.handleConfigureWarningsFrame(response).catch((err) => {
|
|
8643
|
+
this.warnVia(`configure warning delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8644
|
+
});
|
|
8645
|
+
return;
|
|
8646
|
+
}
|
|
8647
|
+
if (response.type === "status_changed") {
|
|
8648
|
+
this.handleStatusChangedFrame(response);
|
|
8649
|
+
return;
|
|
8650
|
+
}
|
|
8651
|
+
const id = response.id;
|
|
8652
|
+
if (id && this.pending.has(id)) {
|
|
8653
|
+
const entry = this.pending.get(id);
|
|
8654
|
+
if (!entry)
|
|
8655
|
+
return;
|
|
8656
|
+
this.pending.delete(id);
|
|
8657
|
+
clearTimeout(entry.timer);
|
|
8658
|
+
this.consecutiveRequestTimeouts = 0;
|
|
8659
|
+
this.scheduleRestartCountReset();
|
|
8660
|
+
this.accountForBashTaskResponse(entry.command, response);
|
|
8661
|
+
this.captureStatusBar(response);
|
|
8662
|
+
entry.resolve(response);
|
|
8663
|
+
} else if (typeof response.type === "string") {
|
|
8664
|
+
this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
|
|
8665
|
+
}
|
|
8666
|
+
} catch (_err) {
|
|
8667
|
+
this.warnVia(`Failed to parse stdout line: ${line}`);
|
|
8668
|
+
}
|
|
8669
|
+
}
|
|
8670
|
+
accountForBashTaskResponse(command, response) {
|
|
8671
|
+
const taskId = bashTaskIdFrom(response);
|
|
8672
|
+
if (!taskId)
|
|
8673
|
+
return;
|
|
8674
|
+
if (isTerminalBashStatus(response.status)) {
|
|
8675
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
8676
|
+
return;
|
|
8677
|
+
}
|
|
8678
|
+
if (command === "bash" && response.success !== false) {
|
|
8679
|
+
this.outstandingBackgroundTaskIds.add(taskId);
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
captureStatusBar(response) {
|
|
8683
|
+
const parsed = parseStatusBarCounts(response.status_bar);
|
|
8684
|
+
if (parsed)
|
|
8685
|
+
this.lastStatusBar = parsed;
|
|
8686
|
+
}
|
|
8687
|
+
getStatusBar() {
|
|
8688
|
+
return this.lastStatusBar;
|
|
8689
|
+
}
|
|
8690
|
+
handleTimeout(triggeringSessionId) {
|
|
8691
|
+
this.consecutiveRequestTimeouts = 0;
|
|
8692
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
8693
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
8694
|
+
if (this.process) {
|
|
8695
|
+
this.process.kill("SIGKILL");
|
|
8696
|
+
this.process = null;
|
|
8697
|
+
}
|
|
8698
|
+
this.clearRestartResetTimer();
|
|
8699
|
+
this.configured = false;
|
|
8700
|
+
const tail = this.formatStderrTail();
|
|
8701
|
+
this.stderrTail = [];
|
|
8702
|
+
const killedMsg = tail ? `Bridge killed after timeout.${tail}` : `Bridge killed after timeout (see ${this.getLogFilePathVia()})`;
|
|
8703
|
+
if (tail) {
|
|
8704
|
+
if (triggeringSessionId) {
|
|
8705
|
+
this.sessionErrorVia(triggeringSessionId, killedMsg);
|
|
8706
|
+
} else {
|
|
8707
|
+
this.errorVia(killedMsg);
|
|
8708
|
+
}
|
|
8709
|
+
} else if (triggeringSessionId) {
|
|
8710
|
+
this.sessionWarnVia(triggeringSessionId, killedMsg);
|
|
8711
|
+
} else {
|
|
8712
|
+
this.warnVia(killedMsg);
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
handleCrash(cause) {
|
|
8716
|
+
const proc = this.process;
|
|
8717
|
+
this.process = null;
|
|
8718
|
+
if (proc && proc.exitCode === null && !proc.killed) {
|
|
8719
|
+
proc.kill("SIGKILL");
|
|
8720
|
+
}
|
|
8721
|
+
this.clearRestartResetTimer();
|
|
8722
|
+
this.configured = false;
|
|
8723
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
8724
|
+
const tail = this.formatStderrTail();
|
|
8725
|
+
if (tail) {
|
|
8726
|
+
this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
|
|
8727
|
+
}
|
|
8728
|
+
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
|
|
8729
|
+
if (this._restartCount < this.maxRestarts) {
|
|
8730
|
+
const delay = 100 * 2 ** this._restartCount;
|
|
8731
|
+
this._restartCount++;
|
|
8732
|
+
this.logVia(`Auto-restart #${this._restartCount} in ${delay}ms`);
|
|
8733
|
+
setTimeout(() => {
|
|
8734
|
+
if (!this._shuttingDown && !this.isAlive()) {
|
|
8735
|
+
try {
|
|
8736
|
+
this.spawnProcess();
|
|
8737
|
+
} catch (err) {
|
|
8738
|
+
this.errorVia(`Failed to restart: ${err.message}`);
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8741
|
+
}, delay);
|
|
8742
|
+
this.scheduleRestartCountReset();
|
|
8743
|
+
} else {
|
|
8744
|
+
this.errorVia(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${this.getLogFilePathVia()}${tail}`);
|
|
8745
|
+
this.scheduleRestartCountReset();
|
|
8746
|
+
}
|
|
8747
|
+
}
|
|
8748
|
+
rejectAllPending(error2) {
|
|
8749
|
+
for (const [_id, entry] of this.pending) {
|
|
8750
|
+
clearTimeout(entry.timer);
|
|
8751
|
+
entry.reject(error2);
|
|
8752
|
+
}
|
|
8753
|
+
this.pending.clear();
|
|
8754
|
+
}
|
|
8755
|
+
scheduleRestartCountReset() {
|
|
8756
|
+
this.clearRestartResetTimer();
|
|
8757
|
+
this.restartResetTimer = setTimeout(() => {
|
|
8758
|
+
this._restartCount = 0;
|
|
8759
|
+
this.restartResetTimer = null;
|
|
8760
|
+
}, BinaryBridge.RESTART_RESET_MS);
|
|
8761
|
+
}
|
|
8762
|
+
clearRestartResetTimer() {
|
|
8763
|
+
if (this.restartResetTimer) {
|
|
8764
|
+
clearTimeout(this.restartResetTimer);
|
|
8765
|
+
this.restartResetTimer = null;
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
// ../aft-bridge/dist/downloader.js
|
|
8770
|
+
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
8771
|
+
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
8772
|
+
// ../aft-bridge/dist/migration.js
|
|
8773
|
+
var DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
8774
|
+
// ../aft-bridge/dist/onnx-runtime.js
|
|
8775
|
+
var ORT_VERSION = "1.24.4";
|
|
8776
|
+
var MAX_DOWNLOAD_BYTES2 = 256 * 1024 * 1024;
|
|
8777
|
+
var MAX_EXTRACT_BYTES = 1 * 1024 * 1024 * 1024;
|
|
8778
|
+
var STALE_LOCK_MS = 5 * 60 * 1000;
|
|
8779
|
+
var ORT_PLATFORM_MAP = {
|
|
8780
|
+
darwin: {
|
|
8781
|
+
arm64: {
|
|
8782
|
+
assetName: `onnxruntime-osx-arm64-${ORT_VERSION}`,
|
|
8783
|
+
libName: "libonnxruntime.dylib",
|
|
8784
|
+
archiveType: "tgz"
|
|
8785
|
+
}
|
|
8786
|
+
},
|
|
8787
|
+
linux: {
|
|
8788
|
+
x64: {
|
|
8789
|
+
assetName: `onnxruntime-linux-x64-${ORT_VERSION}`,
|
|
8790
|
+
libName: "libonnxruntime.so",
|
|
8791
|
+
archiveType: "tgz"
|
|
8792
|
+
},
|
|
8793
|
+
arm64: {
|
|
8794
|
+
assetName: `onnxruntime-linux-aarch64-${ORT_VERSION}`,
|
|
8795
|
+
libName: "libonnxruntime.so",
|
|
8796
|
+
archiveType: "tgz"
|
|
8797
|
+
}
|
|
8798
|
+
},
|
|
8799
|
+
win32: {
|
|
8800
|
+
x64: {
|
|
8801
|
+
assetName: `onnxruntime-win-x64-${ORT_VERSION}`,
|
|
8802
|
+
libName: "onnxruntime.dll",
|
|
8803
|
+
archiveType: "zip"
|
|
8804
|
+
},
|
|
8805
|
+
arm64: {
|
|
8806
|
+
assetName: `onnxruntime-win-arm64-${ORT_VERSION}`,
|
|
8807
|
+
libName: "onnxruntime.dll",
|
|
8808
|
+
archiveType: "zip"
|
|
8809
|
+
}
|
|
8810
|
+
}
|
|
8811
|
+
};
|
|
8812
|
+
// ../aft-bridge/dist/project-identity.js
|
|
8813
|
+
import { createHash } from "node:crypto";
|
|
8814
|
+
import { realpathSync } from "node:fs";
|
|
8815
|
+
import { resolve } from "node:path";
|
|
8816
|
+
function canonicalizeProjectRoot(dir) {
|
|
8817
|
+
const trimmed = dir.replace(/[/\\]+$/, "");
|
|
8818
|
+
let canonical;
|
|
8819
|
+
try {
|
|
8820
|
+
canonical = realpathSync(trimmed);
|
|
8821
|
+
} catch {
|
|
8822
|
+
canonical = resolve(trimmed);
|
|
8823
|
+
}
|
|
8824
|
+
return normalizeWindowsRoot(canonical);
|
|
8825
|
+
}
|
|
8826
|
+
function normalizeWindowsRoot(p) {
|
|
8827
|
+
if (process.platform !== "win32")
|
|
8828
|
+
return p;
|
|
8829
|
+
let s = p;
|
|
8830
|
+
if (s.startsWith("\\\\?\\UNC\\")) {
|
|
8831
|
+
s = `\\\\${s.slice("\\\\?\\UNC\\".length)}`;
|
|
8832
|
+
} else if (s.startsWith("\\\\?\\")) {
|
|
8833
|
+
s = s.slice("\\\\?\\".length);
|
|
8834
|
+
}
|
|
8835
|
+
if (s.length >= 2 && s[1] === ":") {
|
|
8836
|
+
const drive = s.charCodeAt(0);
|
|
8837
|
+
if (drive >= 97 && drive <= 122) {
|
|
8838
|
+
s = s[0].toUpperCase() + s.slice(1);
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
8841
|
+
return s;
|
|
8842
|
+
}
|
|
8843
|
+
function projectRootKeyHash(dir) {
|
|
8844
|
+
return createHash("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
|
|
8845
|
+
}
|
|
8846
|
+
|
|
8847
|
+
// ../aft-bridge/dist/pool.js
|
|
8848
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
8849
|
+
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
8850
|
+
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
8851
|
+
class BridgePool {
|
|
8852
|
+
bridges = new Map;
|
|
8853
|
+
staleBridges = new Set;
|
|
8854
|
+
binaryPath;
|
|
8855
|
+
maxPoolSize;
|
|
8856
|
+
idleTimeoutMs;
|
|
8857
|
+
bridgeOptions;
|
|
8858
|
+
configOverrides;
|
|
8859
|
+
projectConfigLoader;
|
|
8860
|
+
logger;
|
|
8861
|
+
cleanupTimer = null;
|
|
8862
|
+
constructor(binaryPath, options = {}, configOverrides = {}) {
|
|
8863
|
+
this.binaryPath = binaryPath;
|
|
8864
|
+
this.maxPoolSize = options.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE;
|
|
8865
|
+
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
8866
|
+
this.logger = options.logger;
|
|
8867
|
+
this.projectConfigLoader = options.projectConfigLoader;
|
|
8868
|
+
this.bridgeOptions = {
|
|
8869
|
+
timeoutMs: options.timeoutMs,
|
|
8870
|
+
hangThreshold: options.hangThreshold,
|
|
8871
|
+
maxRestarts: options.maxRestarts,
|
|
8872
|
+
minVersion: options.minVersion,
|
|
8873
|
+
onVersionMismatch: options.onVersionMismatch,
|
|
8874
|
+
onConfigureWarnings: options.onConfigureWarnings,
|
|
8875
|
+
onBashCompletion: options.onBashCompletion,
|
|
8876
|
+
onBashLongRunning: options.onBashLongRunning,
|
|
8877
|
+
onBashPatternMatch: options.onBashPatternMatch,
|
|
8878
|
+
errorPrefix: options.errorPrefix,
|
|
8879
|
+
logger: options.logger,
|
|
8880
|
+
childEnv: options.childEnv
|
|
8881
|
+
};
|
|
8882
|
+
this.configOverrides = configOverrides;
|
|
8883
|
+
if (Number.isFinite(this.idleTimeoutMs)) {
|
|
8884
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
|
|
8885
|
+
this.cleanupTimer.unref();
|
|
8886
|
+
}
|
|
8887
|
+
}
|
|
8888
|
+
getActiveBridgeForRoot(projectRoot) {
|
|
8889
|
+
const key = normalizeKey(projectRoot);
|
|
8890
|
+
const entry = this.bridges.get(key);
|
|
8891
|
+
if (!entry?.bridge.isAlive())
|
|
8892
|
+
return null;
|
|
8893
|
+
entry.lastUsed = Date.now();
|
|
8894
|
+
return entry.bridge;
|
|
8895
|
+
}
|
|
8896
|
+
getBridge(projectRoot) {
|
|
8897
|
+
const key = normalizeKey(projectRoot);
|
|
8898
|
+
const existing = this.bridges.get(key);
|
|
8899
|
+
if (existing) {
|
|
8900
|
+
existing.lastUsed = Date.now();
|
|
8901
|
+
return existing.bridge;
|
|
8902
|
+
}
|
|
8903
|
+
if (this.bridges.size >= this.maxPoolSize) {
|
|
8904
|
+
this.evictLRU();
|
|
8905
|
+
}
|
|
8906
|
+
let projectOverrides = {};
|
|
8907
|
+
if (this.projectConfigLoader) {
|
|
8908
|
+
try {
|
|
8909
|
+
projectOverrides = this.projectConfigLoader(key) ?? {};
|
|
8910
|
+
} catch (err) {
|
|
8911
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8912
|
+
this.error(`projectConfigLoader failed; using global overrides only: ${message}`);
|
|
8913
|
+
}
|
|
8914
|
+
}
|
|
8915
|
+
const mergedOverrides = { ...this.configOverrides, ...projectOverrides };
|
|
8916
|
+
const bridge = new BinaryBridge(this.binaryPath, key, this.bridgeOptions, mergedOverrides);
|
|
8917
|
+
this.bridges.set(key, { bridge, lastUsed: Date.now() });
|
|
8918
|
+
return bridge;
|
|
8919
|
+
}
|
|
8920
|
+
cleanup() {
|
|
8921
|
+
const now = Date.now();
|
|
8922
|
+
for (const [dir, entry] of this.bridges) {
|
|
8923
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
8924
|
+
continue;
|
|
8925
|
+
if (now - entry.lastUsed > this.idleTimeoutMs) {
|
|
8926
|
+
entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
|
|
8927
|
+
this.bridges.delete(dir);
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8930
|
+
for (const bridge of this.staleBridges) {
|
|
8931
|
+
if (bridge.hasPendingRequests() || bridge.hasOutstandingBackgroundTasks())
|
|
8932
|
+
continue;
|
|
8933
|
+
bridge.shutdown().catch((err) => this.error("stale cleanup shutdown failed:", err));
|
|
8934
|
+
this.staleBridges.delete(bridge);
|
|
8935
|
+
}
|
|
8936
|
+
}
|
|
8937
|
+
evictLRU() {
|
|
8938
|
+
let oldestDir = null;
|
|
8939
|
+
let oldestTime = Infinity;
|
|
8940
|
+
for (const [dir, entry] of this.bridges) {
|
|
8941
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
8942
|
+
continue;
|
|
8943
|
+
if (entry.lastUsed < oldestTime) {
|
|
8944
|
+
oldestTime = entry.lastUsed;
|
|
8945
|
+
oldestDir = dir;
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8948
|
+
if (oldestDir) {
|
|
8949
|
+
const entry = this.bridges.get(oldestDir);
|
|
8950
|
+
entry?.bridge.shutdown().catch((err) => this.error("eviction shutdown failed:", err));
|
|
8951
|
+
this.bridges.delete(oldestDir);
|
|
8952
|
+
}
|
|
8953
|
+
}
|
|
8954
|
+
async shutdown() {
|
|
8955
|
+
if (this.cleanupTimer) {
|
|
8956
|
+
clearInterval(this.cleanupTimer);
|
|
8957
|
+
this.cleanupTimer = null;
|
|
8958
|
+
}
|
|
8959
|
+
const shutdowns = [
|
|
8960
|
+
...Array.from(this.bridges.values(), (e) => e.bridge.shutdown()),
|
|
8961
|
+
...Array.from(this.staleBridges.values(), (bridge) => bridge.shutdown())
|
|
8962
|
+
];
|
|
8963
|
+
this.bridges.clear();
|
|
8964
|
+
this.staleBridges.clear();
|
|
8965
|
+
await Promise.allSettled(shutdowns);
|
|
8966
|
+
}
|
|
8967
|
+
async replaceBinary(newPath) {
|
|
8968
|
+
this.binaryPath = newPath;
|
|
8969
|
+
for (const entry of this.bridges.values()) {
|
|
8970
|
+
this.staleBridges.add(entry.bridge);
|
|
8971
|
+
}
|
|
8972
|
+
this.bridges.clear();
|
|
8973
|
+
this.log(`Binary path updated to ${newPath}. Active bridges marked stale — next calls will use the new binary.`);
|
|
8974
|
+
return newPath;
|
|
8975
|
+
}
|
|
8976
|
+
log(message, meta) {
|
|
8977
|
+
const logger = this.logger ?? getActiveLogger();
|
|
8978
|
+
if (logger) {
|
|
8979
|
+
try {
|
|
8980
|
+
logger.log(message, meta);
|
|
8981
|
+
} catch (err) {
|
|
8982
|
+
console.error(`[aft-bridge] ERROR: pool logger log threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8983
|
+
console.error(`[aft-bridge] ${message}`);
|
|
8984
|
+
}
|
|
8985
|
+
} else
|
|
8986
|
+
log(message, meta);
|
|
8987
|
+
}
|
|
8988
|
+
error(message, meta) {
|
|
8989
|
+
const logger = this.logger ?? getActiveLogger();
|
|
8990
|
+
if (logger) {
|
|
8991
|
+
try {
|
|
8992
|
+
logger.error(message, meta);
|
|
8993
|
+
} catch (err) {
|
|
8994
|
+
console.error(`[aft-bridge] ERROR: pool logger error threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
8995
|
+
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
8996
|
+
}
|
|
8997
|
+
} else
|
|
8998
|
+
error(message, meta);
|
|
8999
|
+
}
|
|
9000
|
+
setConfigureOverride(key, value) {
|
|
9001
|
+
if (value === undefined) {
|
|
9002
|
+
delete this.configOverrides[key];
|
|
9003
|
+
} else {
|
|
9004
|
+
this.configOverrides[key] = value;
|
|
9005
|
+
}
|
|
9006
|
+
}
|
|
9007
|
+
get size() {
|
|
9008
|
+
return this.bridges.size;
|
|
9009
|
+
}
|
|
9010
|
+
_testGetConfigOverrides() {
|
|
9011
|
+
return { ...this.configOverrides };
|
|
9012
|
+
}
|
|
9013
|
+
_testGetBridgeOptions() {
|
|
9014
|
+
return { ...this.bridgeOptions };
|
|
9015
|
+
}
|
|
9016
|
+
}
|
|
9017
|
+
function normalizeKey(projectRoot) {
|
|
9018
|
+
return canonicalizeProjectRoot(projectRoot);
|
|
9019
|
+
}
|
|
9020
|
+
// src/shared/rpc-utils.ts
|
|
7802
9021
|
function projectHash(directory) {
|
|
7803
|
-
|
|
7804
|
-
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
9022
|
+
return projectRootKeyHash(directory);
|
|
7805
9023
|
}
|
|
7806
9024
|
function rpcPortFilePath(storageDir, directory) {
|
|
7807
9025
|
const hash = projectHash(directory);
|
|
7808
|
-
return
|
|
9026
|
+
return join2(storageDir, "rpc", hash, "port");
|
|
7809
9027
|
}
|
|
7810
9028
|
function rpcPortFileDir(storageDir, directory) {
|
|
7811
9029
|
const hash = projectHash(directory);
|
|
7812
|
-
return
|
|
9030
|
+
return join2(storageDir, "rpc", hash, "ports");
|
|
7813
9031
|
}
|
|
7814
9032
|
function isPidAlive(pid) {
|
|
7815
9033
|
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
@@ -7863,7 +9081,7 @@ function throwIfAborted(signal) {
|
|
|
7863
9081
|
}
|
|
7864
9082
|
function delay(ms, signal) {
|
|
7865
9083
|
throwIfAborted(signal);
|
|
7866
|
-
return new Promise((
|
|
9084
|
+
return new Promise((resolve2, reject) => {
|
|
7867
9085
|
let timer;
|
|
7868
9086
|
const onAbort = () => {
|
|
7869
9087
|
clearTimeout(timer);
|
|
@@ -7871,7 +9089,7 @@ function delay(ms, signal) {
|
|
|
7871
9089
|
};
|
|
7872
9090
|
timer = setTimeout(() => {
|
|
7873
9091
|
signal?.removeEventListener("abort", onAbort);
|
|
7874
|
-
|
|
9092
|
+
resolve2();
|
|
7875
9093
|
}, ms);
|
|
7876
9094
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7877
9095
|
});
|
|
@@ -7984,7 +9202,7 @@ class AftRpcClient {
|
|
|
7984
9202
|
for (const entry of readdirSync(this.portsDir)) {
|
|
7985
9203
|
if (!entry.endsWith(".json"))
|
|
7986
9204
|
continue;
|
|
7987
|
-
const filePath =
|
|
9205
|
+
const filePath = join3(this.portsDir, entry);
|
|
7988
9206
|
const info = this.parsePortFile(filePath);
|
|
7989
9207
|
if (!info)
|
|
7990
9208
|
continue;
|
|
@@ -8233,39 +9451,39 @@ function coerceAftStatus(response) {
|
|
|
8233
9451
|
import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js";
|
|
8234
9452
|
|
|
8235
9453
|
// src/shared/storage-paths.ts
|
|
8236
|
-
import { homedir } from "node:os";
|
|
8237
|
-
import { join as
|
|
9454
|
+
import { homedir as homedir2 } from "node:os";
|
|
9455
|
+
import { join as join4 } from "node:path";
|
|
8238
9456
|
function homeDir() {
|
|
8239
9457
|
if (process.platform === "win32")
|
|
8240
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
8241
|
-
return process.env.HOME ||
|
|
9458
|
+
return process.env.USERPROFILE || process.env.HOME || homedir2();
|
|
9459
|
+
return process.env.HOME || homedir2();
|
|
8242
9460
|
}
|
|
8243
9461
|
function dataHome() {
|
|
8244
9462
|
if (process.env.XDG_DATA_HOME)
|
|
8245
9463
|
return process.env.XDG_DATA_HOME;
|
|
8246
9464
|
if (process.platform === "win32") {
|
|
8247
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA ||
|
|
9465
|
+
return process.env.LOCALAPPDATA || process.env.APPDATA || join4(homeDir(), "AppData", "Local");
|
|
8248
9466
|
}
|
|
8249
|
-
return
|
|
9467
|
+
return join4(homeDir(), ".local", "share");
|
|
8250
9468
|
}
|
|
8251
|
-
function
|
|
8252
|
-
return
|
|
9469
|
+
function resolveCortexKitStorageRoot2() {
|
|
9470
|
+
return join4(dataHome(), "cortexkit", "aft");
|
|
8253
9471
|
}
|
|
8254
9472
|
|
|
8255
9473
|
// src/tui/preferences.ts
|
|
8256
9474
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
8257
9475
|
import { watch } from "node:fs";
|
|
8258
9476
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
8259
|
-
import { homedir as
|
|
8260
|
-
import { basename, dirname, join as
|
|
9477
|
+
import { homedir as homedir3 } from "node:os";
|
|
9478
|
+
import { basename, dirname, join as join5 } from "node:path";
|
|
8261
9479
|
var TUI_PREFS_FILE_ENV = "OPENCODE_TUI_PREFERENCES_FILE";
|
|
8262
9480
|
var FILE_NAME = "tui-preferences.jsonc";
|
|
8263
9481
|
function getTuiPreferencesFile() {
|
|
8264
9482
|
const override = process.env[TUI_PREFS_FILE_ENV];
|
|
8265
9483
|
if (override)
|
|
8266
9484
|
return override;
|
|
8267
|
-
const configDir = process.env.OPENCODE_CONFIG_DIR ||
|
|
8268
|
-
return
|
|
9485
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR || join5(process.env.XDG_CONFIG_HOME || join5(homedir3(), ".config"), "opencode");
|
|
9486
|
+
return join5(configDir, FILE_NAME);
|
|
8269
9487
|
}
|
|
8270
9488
|
function isRecord(value) {
|
|
8271
9489
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -8606,7 +9824,7 @@ function collapsedHealthLights(statusBar) {
|
|
|
8606
9824
|
return { diagnostics, code, todos };
|
|
8607
9825
|
}
|
|
8608
9826
|
function resolveTuiStorageDir() {
|
|
8609
|
-
return
|
|
9827
|
+
return resolveCortexKitStorageRoot2();
|
|
8610
9828
|
}
|
|
8611
9829
|
var sidebarClients = new Map;
|
|
8612
9830
|
function getClient(directory) {
|
|
@@ -8628,16 +9846,16 @@ function shouldSuppressUninitializedDowngrade(incomingCacheRole, haveInitialized
|
|
|
8628
9846
|
return incomingCacheRole === "not_initialized" && haveInitializedForContext;
|
|
8629
9847
|
}
|
|
8630
9848
|
function isSnapshotForContext(snapshot, directory, servedDirectory) {
|
|
8631
|
-
const
|
|
9849
|
+
const canon = (p) => canonicalizeProjectRoot(p);
|
|
8632
9850
|
const roots = [snapshot.project_root, snapshot.canonical_root].filter((r) => typeof r === "string" && r.length > 0);
|
|
8633
9851
|
if (roots.length === 0)
|
|
8634
9852
|
return true;
|
|
8635
|
-
const dir =
|
|
8636
|
-
if (roots.some((r) =>
|
|
9853
|
+
const dir = canon(directory);
|
|
9854
|
+
if (roots.some((r) => canon(r) === dir))
|
|
8637
9855
|
return true;
|
|
8638
9856
|
if (typeof servedDirectory === "string" && servedDirectory.length > 0) {
|
|
8639
|
-
const served =
|
|
8640
|
-
return roots.some((r) =>
|
|
9857
|
+
const served = canon(servedDirectory);
|
|
9858
|
+
return roots.some((r) => canon(r) === served);
|
|
8641
9859
|
}
|
|
8642
9860
|
return false;
|
|
8643
9861
|
}
|
|
@@ -9123,7 +10341,6 @@ async function createAftSidebarSlot(api, pluginVersion) {
|
|
|
9123
10341
|
|
|
9124
10342
|
// src/tui/index.tsx
|
|
9125
10343
|
import { jsxDEV as jsxDEV2 } from "@opentui/solid/jsx-dev-runtime";
|
|
9126
|
-
var STATUS_COMMAND = "aft-status";
|
|
9127
10344
|
var rpcClients = new Map;
|
|
9128
10345
|
function getRpcClient(directory) {
|
|
9129
10346
|
let client = rpcClients.get(directory);
|
|
@@ -9142,6 +10359,29 @@ function getSessionId(api) {
|
|
|
9142
10359
|
} catch {}
|
|
9143
10360
|
return null;
|
|
9144
10361
|
}
|
|
10362
|
+
var lastReceivedNotificationIdBySession = new Map;
|
|
10363
|
+
async function consumeTuiMessages(client, sessionId) {
|
|
10364
|
+
try {
|
|
10365
|
+
const result = await client.call("consume-notifications", {
|
|
10366
|
+
lastReceivedId: lastReceivedNotificationIdBySession.get(sessionId) ?? 0,
|
|
10367
|
+
sessionId
|
|
10368
|
+
});
|
|
10369
|
+
return (result.messages ?? []).map((message) => ({
|
|
10370
|
+
id: message.id,
|
|
10371
|
+
type: message.type,
|
|
10372
|
+
payload: message.payload,
|
|
10373
|
+
sessionId: message.sessionId
|
|
10374
|
+
}));
|
|
10375
|
+
} catch {
|
|
10376
|
+
return [];
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
function markTuiMessagesReceived(sessionId, messages) {
|
|
10380
|
+
const previous = lastReceivedNotificationIdBySession.get(sessionId) ?? 0;
|
|
10381
|
+
const next = messages.reduce((max, message) => Math.max(max, message.id), previous);
|
|
10382
|
+
if (next > previous)
|
|
10383
|
+
lastReceivedNotificationIdBySession.set(sessionId, next);
|
|
10384
|
+
}
|
|
9145
10385
|
var POLL_INTERVAL_MS2 = 1500;
|
|
9146
10386
|
function formatCountShort(value) {
|
|
9147
10387
|
if (value == null || !Number.isFinite(value))
|
|
@@ -9214,7 +10454,7 @@ var StatusDialog = (props) => {
|
|
|
9214
10454
|
const theme = createMemo2(() => props.api.theme.current);
|
|
9215
10455
|
const t = () => theme();
|
|
9216
10456
|
const [status, setStatus] = createSignal2(props.initial);
|
|
9217
|
-
const [
|
|
10457
|
+
const [error2, setError] = createSignal2(props.initialError);
|
|
9218
10458
|
let pollGeneration = 0;
|
|
9219
10459
|
let pollController = null;
|
|
9220
10460
|
const pollStatus = async () => {
|
|
@@ -9287,12 +10527,12 @@ var StatusDialog = (props) => {
|
|
|
9287
10527
|
}, undefined, true, undefined, this)
|
|
9288
10528
|
]
|
|
9289
10529
|
}, undefined, true, undefined, this),
|
|
9290
|
-
|
|
10530
|
+
error2() ? /* @__PURE__ */ jsxDEV2("box", {
|
|
9291
10531
|
width: "100%",
|
|
9292
10532
|
marginBottom: 1,
|
|
9293
10533
|
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
9294
10534
|
fg: t().warning,
|
|
9295
|
-
children:
|
|
10535
|
+
children: error2()
|
|
9296
10536
|
}, undefined, false, undefined, this)
|
|
9297
10537
|
}, undefined, false, undefined, this) : null,
|
|
9298
10538
|
status()?.cache_role === "not_initialized" ? /* @__PURE__ */ jsxDEV2("box", {
|
|
@@ -9650,24 +10890,15 @@ async function showStatusDialog(api) {
|
|
|
9650
10890
|
} catch {
|
|
9651
10891
|
initialError = "AFT is starting up. Status will refresh automatically...";
|
|
9652
10892
|
}
|
|
9653
|
-
|
|
9654
|
-
api
|
|
9655
|
-
|
|
9656
|
-
|
|
10893
|
+
api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV2(StatusDialog, {
|
|
10894
|
+
api,
|
|
10895
|
+
client,
|
|
10896
|
+
directory,
|
|
10897
|
+
sessionID,
|
|
10898
|
+
initial,
|
|
10899
|
+
initialError
|
|
10900
|
+
}, undefined, false, undefined, this));
|
|
9657
10901
|
api.ui.dialog.setSize("large");
|
|
9658
|
-
api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV2(api.ui.Dialog, {
|
|
9659
|
-
size: "large",
|
|
9660
|
-
onClose: dismissStatusDialog,
|
|
9661
|
-
children: /* @__PURE__ */ jsxDEV2(StatusDialog, {
|
|
9662
|
-
api,
|
|
9663
|
-
client,
|
|
9664
|
-
directory,
|
|
9665
|
-
sessionID,
|
|
9666
|
-
initial,
|
|
9667
|
-
initialError,
|
|
9668
|
-
onClose: dismissStatusDialog
|
|
9669
|
-
}, undefined, false, undefined, this)
|
|
9670
|
-
}, undefined, false, undefined, this), dismissStatusDialog);
|
|
9671
10902
|
}
|
|
9672
10903
|
function registerStatusCommand(api) {
|
|
9673
10904
|
const apiAny = api;
|
|
@@ -9679,7 +10910,6 @@ function registerStatusCommand(api) {
|
|
|
9679
10910
|
name: "aft.status",
|
|
9680
10911
|
title: "AFT: Status",
|
|
9681
10912
|
category: "AFT",
|
|
9682
|
-
slashName: STATUS_COMMAND,
|
|
9683
10913
|
run() {
|
|
9684
10914
|
showStatusDialog(api);
|
|
9685
10915
|
}
|
|
@@ -9695,7 +10925,6 @@ function registerStatusCommand(api) {
|
|
|
9695
10925
|
title: "AFT: Status",
|
|
9696
10926
|
value: "aft.status",
|
|
9697
10927
|
category: "AFT",
|
|
9698
|
-
slash: { name: STATUS_COMMAND },
|
|
9699
10928
|
onSelect() {
|
|
9700
10929
|
showStatusDialog(api);
|
|
9701
10930
|
}
|
|
@@ -9753,6 +10982,47 @@ var tui = async (api) => {
|
|
|
9753
10982
|
api.slots.register(await createAftSidebarSlot(api, version));
|
|
9754
10983
|
} catch {}
|
|
9755
10984
|
registerStatusCommand(api);
|
|
10985
|
+
let pollInFlight = false;
|
|
10986
|
+
const messagePoller = setInterval(() => {
|
|
10987
|
+
if (pollInFlight)
|
|
10988
|
+
return;
|
|
10989
|
+
const requestedSessionId = getSessionId(api);
|
|
10990
|
+
if (!requestedSessionId)
|
|
10991
|
+
return;
|
|
10992
|
+
const directory = api.state.path.directory ?? "";
|
|
10993
|
+
if (!directory)
|
|
10994
|
+
return;
|
|
10995
|
+
pollInFlight = true;
|
|
10996
|
+
const client = getRpcClient(directory);
|
|
10997
|
+
consumeTuiMessages(client, requestedSessionId).then(async (messages) => {
|
|
10998
|
+
if (getSessionId(api) !== requestedSessionId)
|
|
10999
|
+
return;
|
|
11000
|
+
const orderedMessages = [...messages].sort((a, b) => a.id - b.id);
|
|
11001
|
+
for (const message of orderedMessages) {
|
|
11002
|
+
if (getSessionId(api) !== requestedSessionId)
|
|
11003
|
+
return;
|
|
11004
|
+
if (message.sessionId && message.sessionId !== requestedSessionId)
|
|
11005
|
+
continue;
|
|
11006
|
+
if (message.type !== "action")
|
|
11007
|
+
continue;
|
|
11008
|
+
if (message.payload?.action !== "show-status-dialog")
|
|
11009
|
+
continue;
|
|
11010
|
+
await showStatusDialog(api);
|
|
11011
|
+
}
|
|
11012
|
+
if (getSessionId(api) !== requestedSessionId)
|
|
11013
|
+
return;
|
|
11014
|
+
markTuiMessagesReceived(requestedSessionId, orderedMessages);
|
|
11015
|
+
}).catch(() => {}).finally(() => {
|
|
11016
|
+
pollInFlight = false;
|
|
11017
|
+
});
|
|
11018
|
+
}, 500);
|
|
11019
|
+
api.lifecycle?.onDispose?.(() => {
|
|
11020
|
+
clearInterval(messagePoller);
|
|
11021
|
+
lastReceivedNotificationIdBySession.clear();
|
|
11022
|
+
for (const client of rpcClients.values())
|
|
11023
|
+
client.reset();
|
|
11024
|
+
rpcClients.clear();
|
|
11025
|
+
});
|
|
9756
11026
|
showStartupNotifications(api);
|
|
9757
11027
|
};
|
|
9758
11028
|
var id = "aft-opencode";
|