@cortexkit/aft-opencode 0.43.1 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -97
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tui/badge-contrast.d.ts +15 -0
- package/dist/tui/badge-contrast.d.ts.map +1 -0
- package/dist/tui.js +176 -22
- package/package.json +8 -8
- package/src/tui/badge-contrast.ts +43 -0
- package/src/tui/sidebar.tsx +3 -2
package/dist/tools/ast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkDjD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0H3E"}
|
package/dist/tools/bash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAMvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuCjD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,OAAO,EAC5B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,GACpB,MAAM,CAaR;AA6ED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,aAAa,EAClB,2BAA2B,CAAC,EAAE,OAAO,GACpC,cAAc,
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAMvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuCjD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,OAAO,EAC5B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,GACpB,MAAM,CAaR;AA6ED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,aAAa,EAClB,2BAA2B,CAAC,EAAE,OAAO,GACpC,cAAc,CA2NhB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA2BvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAwBrE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the text color for a sidebar badge label drawn on a theme accent.
|
|
3
|
+
* Keep this logic in sync with Magic Context so both sidebars make the same
|
|
4
|
+
* contrast decision for shared themes.
|
|
5
|
+
*/
|
|
6
|
+
type Color = {
|
|
7
|
+
r: number;
|
|
8
|
+
g: number;
|
|
9
|
+
b: number;
|
|
10
|
+
a?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function readableTextColorOn(bg: Color): string;
|
|
13
|
+
export declare function badgeTextColor<T extends Color>(accent: T, background: T): T | string;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=badge-contrast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badge-contrast.d.ts","sourceRoot":"","sources":["../../src/tui/badge-contrast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,KAAK,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA0B7D,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,CAErD;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAMpF"}
|
package/dist/tui.js
CHANGED
|
@@ -7790,10 +7790,10 @@ 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.45.0";
|
|
7794
7794
|
|
|
7795
7795
|
// src/shared/rpc-client.ts
|
|
7796
|
-
import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
7796
|
+
import { existsSync, readdirSync, readFileSync as readFileSync2, unlinkSync } from "node:fs";
|
|
7797
7797
|
import { join as join3 } from "node:path";
|
|
7798
7798
|
|
|
7799
7799
|
// src/shared/rpc-utils.ts
|
|
@@ -7856,6 +7856,8 @@ function error(message, meta) {
|
|
|
7856
7856
|
}
|
|
7857
7857
|
// ../aft-bridge/dist/bridge.js
|
|
7858
7858
|
import { spawn } from "node:child_process";
|
|
7859
|
+
import { createHash } from "node:crypto";
|
|
7860
|
+
import { readFileSync } from "node:fs";
|
|
7859
7861
|
import { homedir } from "node:os";
|
|
7860
7862
|
import { join } from "node:path";
|
|
7861
7863
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -7912,6 +7914,22 @@ function bashTaskIdFrom(response) {
|
|
|
7912
7914
|
return camelCase;
|
|
7913
7915
|
return;
|
|
7914
7916
|
}
|
|
7917
|
+
function hashBinaryOnDisk(binaryPath) {
|
|
7918
|
+
try {
|
|
7919
|
+
return createHash("sha256").update(readFileSync(binaryPath)).digest("hex");
|
|
7920
|
+
} catch {
|
|
7921
|
+
return null;
|
|
7922
|
+
}
|
|
7923
|
+
}
|
|
7924
|
+
var binaryFingerprintReader = hashBinaryOnDisk;
|
|
7925
|
+
function readBinaryFingerprint(binaryPath) {
|
|
7926
|
+
try {
|
|
7927
|
+
const fingerprint = binaryFingerprintReader(binaryPath);
|
|
7928
|
+
return typeof fingerprint === "string" && fingerprint.length > 0 ? fingerprint : null;
|
|
7929
|
+
} catch {
|
|
7930
|
+
return null;
|
|
7931
|
+
}
|
|
7932
|
+
}
|
|
7915
7933
|
function tagStderrLine(line) {
|
|
7916
7934
|
return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
|
|
7917
7935
|
}
|
|
@@ -8003,6 +8021,9 @@ class BinaryBridge {
|
|
|
8003
8021
|
binaryPath;
|
|
8004
8022
|
cwd;
|
|
8005
8023
|
process = null;
|
|
8024
|
+
spawnedBinaryFingerprint = null;
|
|
8025
|
+
lastBinaryFingerprintCheckAt = 0;
|
|
8026
|
+
_retiringDueToBinaryChange = false;
|
|
8006
8027
|
pending = new Map;
|
|
8007
8028
|
outstandingBackgroundTaskIds = new Set;
|
|
8008
8029
|
nextId = 1;
|
|
@@ -8122,6 +8143,24 @@ class BinaryBridge {
|
|
|
8122
8143
|
hasOutstandingBackgroundTasks() {
|
|
8123
8144
|
return this.outstandingBackgroundTaskIds.size > 0;
|
|
8124
8145
|
}
|
|
8146
|
+
maybeScheduleRespawnForUpdatedBinary(checkIntervalMs, now = Date.now()) {
|
|
8147
|
+
if (this._shuttingDown || this._retiringDueToBinaryChange || !this.isAlive())
|
|
8148
|
+
return false;
|
|
8149
|
+
if (this.pending.size > 0 || this.outstandingBackgroundTaskIds.size > 0)
|
|
8150
|
+
return false;
|
|
8151
|
+
if (now - this.lastBinaryFingerprintCheckAt < checkIntervalMs)
|
|
8152
|
+
return false;
|
|
8153
|
+
this.lastBinaryFingerprintCheckAt = now;
|
|
8154
|
+
const spawnedFingerprint = this.spawnedBinaryFingerprint;
|
|
8155
|
+
if (!spawnedFingerprint)
|
|
8156
|
+
return false;
|
|
8157
|
+
const currentFingerprint = readBinaryFingerprint(this.binaryPath);
|
|
8158
|
+
if (!currentFingerprint || currentFingerprint === spawnedFingerprint)
|
|
8159
|
+
return false;
|
|
8160
|
+
this._retiringDueToBinaryChange = true;
|
|
8161
|
+
this.logVia(`Binary contents changed on disk for ${this.binaryPath}; retiring this bridge so the next tool call respawns onto the updated binary.`);
|
|
8162
|
+
return true;
|
|
8163
|
+
}
|
|
8125
8164
|
getCwd() {
|
|
8126
8165
|
return this.cwd;
|
|
8127
8166
|
}
|
|
@@ -8154,6 +8193,9 @@ class BinaryBridge {
|
|
|
8154
8193
|
}
|
|
8155
8194
|
async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
|
|
8156
8195
|
try {
|
|
8196
|
+
if (this._retiringDueToBinaryChange) {
|
|
8197
|
+
throw new Error(`${this.errorPrefix} Bridge is retiring after the on-disk binary changed; retry to respawn on the updated binary`);
|
|
8198
|
+
}
|
|
8157
8199
|
if (this._shuttingDown) {
|
|
8158
8200
|
throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
|
|
8159
8201
|
}
|
|
@@ -8352,6 +8394,7 @@ class BinaryBridge {
|
|
|
8352
8394
|
}
|
|
8353
8395
|
async shutdown() {
|
|
8354
8396
|
this._shuttingDown = true;
|
|
8397
|
+
this.spawnedBinaryFingerprint = null;
|
|
8355
8398
|
this.clearRestartResetTimer();
|
|
8356
8399
|
this.configureWarningClients.clear();
|
|
8357
8400
|
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge shutting down`));
|
|
@@ -8405,6 +8448,9 @@ class BinaryBridge {
|
|
|
8405
8448
|
async replaceCurrentBinary(newBinaryPath) {
|
|
8406
8449
|
this.binaryPath = newBinaryPath;
|
|
8407
8450
|
this.configured = false;
|
|
8451
|
+
this.spawnedBinaryFingerprint = null;
|
|
8452
|
+
this.lastBinaryFingerprintCheckAt = 0;
|
|
8453
|
+
this._retiringDueToBinaryChange = false;
|
|
8408
8454
|
this.clearRestartResetTimer();
|
|
8409
8455
|
this.rejectAllPending(new Error(`${this.errorPrefix} Bridge restarting with updated binary: ${newBinaryPath}`));
|
|
8410
8456
|
if (!this.process)
|
|
@@ -8430,6 +8476,7 @@ class BinaryBridge {
|
|
|
8430
8476
|
this.spawnProcess(triggeringSessionId);
|
|
8431
8477
|
}
|
|
8432
8478
|
spawnProcess(triggeringSessionId) {
|
|
8479
|
+
this._retiringDueToBinaryChange = false;
|
|
8433
8480
|
this.lastStatusBar = undefined;
|
|
8434
8481
|
if (triggeringSessionId) {
|
|
8435
8482
|
this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
@@ -8520,6 +8567,8 @@ class BinaryBridge {
|
|
|
8520
8567
|
this.handleCrash();
|
|
8521
8568
|
});
|
|
8522
8569
|
this.process = child;
|
|
8570
|
+
this.spawnedBinaryFingerprint = readBinaryFingerprint(this.binaryPath);
|
|
8571
|
+
this.lastBinaryFingerprintCheckAt = Date.now();
|
|
8523
8572
|
this.stdoutBuffer = "";
|
|
8524
8573
|
this.stdoutReadOffset = 0;
|
|
8525
8574
|
this.stderrBuffer = "";
|
|
@@ -8698,6 +8747,7 @@ class BinaryBridge {
|
|
|
8698
8747
|
}
|
|
8699
8748
|
handleTimeout(triggeringSessionId) {
|
|
8700
8749
|
this.consecutiveRequestTimeouts = 0;
|
|
8750
|
+
this.spawnedBinaryFingerprint = null;
|
|
8701
8751
|
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
8702
8752
|
this.outstandingBackgroundTaskIds.clear();
|
|
8703
8753
|
if (this.process) {
|
|
@@ -8724,6 +8774,7 @@ class BinaryBridge {
|
|
|
8724
8774
|
handleCrash(cause) {
|
|
8725
8775
|
const proc = this.process;
|
|
8726
8776
|
this.process = null;
|
|
8777
|
+
this.spawnedBinaryFingerprint = null;
|
|
8727
8778
|
if (proc && proc.exitCode === null && !proc.killed) {
|
|
8728
8779
|
proc.kill("SIGKILL");
|
|
8729
8780
|
}
|
|
@@ -8735,6 +8786,10 @@ class BinaryBridge {
|
|
|
8735
8786
|
this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
|
|
8736
8787
|
}
|
|
8737
8788
|
this.rejectAllPending(new Error(`${this.errorPrefix} Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""} (see ${this.getLogFilePathVia()})`));
|
|
8789
|
+
if (this._retiringDueToBinaryChange) {
|
|
8790
|
+
this.logVia("Binary exited while retiring after an on-disk update; skipping auto-restart");
|
|
8791
|
+
return;
|
|
8792
|
+
}
|
|
8738
8793
|
if (this._restartCount < this.maxRestarts) {
|
|
8739
8794
|
const delay = 100 * 2 ** this._restartCount;
|
|
8740
8795
|
this._restartCount++;
|
|
@@ -8819,7 +8874,7 @@ var ORT_PLATFORM_MAP = {
|
|
|
8819
8874
|
}
|
|
8820
8875
|
};
|
|
8821
8876
|
// ../aft-bridge/dist/project-identity.js
|
|
8822
|
-
import { createHash } from "node:crypto";
|
|
8877
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
8823
8878
|
import { realpathSync } from "node:fs";
|
|
8824
8879
|
import { resolve } from "node:path";
|
|
8825
8880
|
function canonicalizeProjectRoot(dir) {
|
|
@@ -8850,7 +8905,7 @@ function normalizeWindowsRoot(p) {
|
|
|
8850
8905
|
return s;
|
|
8851
8906
|
}
|
|
8852
8907
|
function projectRootKeyHash(dir) {
|
|
8853
|
-
return
|
|
8908
|
+
return createHash2("sha256").update(canonicalizeProjectRoot(dir)).digest("hex").slice(0, 16);
|
|
8854
8909
|
}
|
|
8855
8910
|
|
|
8856
8911
|
// ../aft-bridge/dist/pool.js
|
|
@@ -8934,6 +8989,11 @@ class BridgePool {
|
|
|
8934
8989
|
for (const [dir, entry] of this.bridges) {
|
|
8935
8990
|
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
8936
8991
|
continue;
|
|
8992
|
+
if (entry.bridge.maybeScheduleRespawnForUpdatedBinary(CLEANUP_INTERVAL_MS, now)) {
|
|
8993
|
+
this.staleBridges.add(entry.bridge);
|
|
8994
|
+
this.bridges.delete(dir);
|
|
8995
|
+
continue;
|
|
8996
|
+
}
|
|
8937
8997
|
if (now - entry.lastUsed > this.idleTimeoutMs) {
|
|
8938
8998
|
entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
|
|
8939
8999
|
this.bridges.delete(dir);
|
|
@@ -9030,10 +9090,11 @@ class BridgePool {
|
|
|
9030
9090
|
function normalizeKey(projectRoot) {
|
|
9031
9091
|
return canonicalizeProjectRoot(projectRoot);
|
|
9032
9092
|
}
|
|
9033
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9093
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
|
|
9034
9094
|
import { promises as fs2 } from "node:fs";
|
|
9095
|
+
import { debuglog } from "node:util";
|
|
9035
9096
|
|
|
9036
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9097
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/auth.ts
|
|
9037
9098
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
9038
9099
|
var NONCE_LEN = 32;
|
|
9039
9100
|
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
@@ -9097,7 +9158,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
|
|
|
9097
9158
|
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
9098
9159
|
}
|
|
9099
9160
|
|
|
9100
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9161
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/connection-file.ts
|
|
9101
9162
|
import { promises as fs } from "node:fs";
|
|
9102
9163
|
var SCHEMA_VERSION = 1;
|
|
9103
9164
|
var MIN_KEY_LEN = 32;
|
|
@@ -9166,7 +9227,7 @@ async function readConnectionFile(path) {
|
|
|
9166
9227
|
return info;
|
|
9167
9228
|
}
|
|
9168
9229
|
|
|
9169
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9230
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/envelope.ts
|
|
9170
9231
|
var PROTOCOL_VERSION = 1;
|
|
9171
9232
|
var HEADER_LEN = 17;
|
|
9172
9233
|
var FROZEN_PREFIX_LEN = 5;
|
|
@@ -9273,7 +9334,7 @@ function encodeFrame(frame) {
|
|
|
9273
9334
|
return out;
|
|
9274
9335
|
}
|
|
9275
9336
|
|
|
9276
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9337
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/socket.ts
|
|
9277
9338
|
import net from "node:net";
|
|
9278
9339
|
|
|
9279
9340
|
class SocketClosedError extends Error {
|
|
@@ -9304,6 +9365,9 @@ class SubcSocket {
|
|
|
9304
9365
|
buffered = 0;
|
|
9305
9366
|
waiter = null;
|
|
9306
9367
|
closedErr = null;
|
|
9368
|
+
bufferedBytes() {
|
|
9369
|
+
return this.buffered;
|
|
9370
|
+
}
|
|
9307
9371
|
constructor(sock) {
|
|
9308
9372
|
this.sock = sock;
|
|
9309
9373
|
sock.on("data", (chunk) => {
|
|
@@ -9320,6 +9384,9 @@ class SubcSocket {
|
|
|
9320
9384
|
sock.on("end", () => fail(new SocketClosedError("subc closed the connection")));
|
|
9321
9385
|
sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
|
|
9322
9386
|
}
|
|
9387
|
+
localPort() {
|
|
9388
|
+
return this.sock.localPort ?? null;
|
|
9389
|
+
}
|
|
9323
9390
|
static connect(host, port, deadlineMs) {
|
|
9324
9391
|
return new Promise((resolve2, reject) => {
|
|
9325
9392
|
const sock = net.connect({ host, port });
|
|
@@ -9459,9 +9526,14 @@ class SubcSocket {
|
|
|
9459
9526
|
}
|
|
9460
9527
|
}
|
|
9461
9528
|
|
|
9462
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
9529
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
|
|
9530
|
+
var debug = debuglog("subc-client");
|
|
9463
9531
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
9464
9532
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
9533
|
+
var TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
9534
|
+
var REQUEST_DEADLINE_MARKER = "request_deadline";
|
|
9535
|
+
var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
|
|
9536
|
+
var ROUTE_OPEN_RETRY_DEADLINE_MS = 1e4;
|
|
9465
9537
|
var BODY_READ_TIMEOUT_MS = 30000;
|
|
9466
9538
|
var EMPTY_BODY = new Uint8Array(0);
|
|
9467
9539
|
var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
|
|
@@ -9505,6 +9577,7 @@ class SubcClient {
|
|
|
9505
9577
|
closeStarted = false;
|
|
9506
9578
|
reconnecting = null;
|
|
9507
9579
|
generation = 1;
|
|
9580
|
+
readerActive = false;
|
|
9508
9581
|
constructor(sock, currentConn, opts) {
|
|
9509
9582
|
this.sock = sock;
|
|
9510
9583
|
this.currentConn = currentConn;
|
|
@@ -9563,7 +9636,7 @@ class SubcClient {
|
|
|
9563
9636
|
}
|
|
9564
9637
|
continue;
|
|
9565
9638
|
}
|
|
9566
|
-
if (err.kind === "outcome_unknown") {
|
|
9639
|
+
if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
|
|
9567
9640
|
this.scheduleReconnectAfterDrop(err);
|
|
9568
9641
|
}
|
|
9569
9642
|
throw err;
|
|
@@ -9686,7 +9759,7 @@ class SubcClient {
|
|
|
9686
9759
|
timer: null
|
|
9687
9760
|
};
|
|
9688
9761
|
pending.timer = setTimeout(() => {
|
|
9689
|
-
this.
|
|
9762
|
+
this.arbitrateTimeout(key, pending, channel, corr, ms);
|
|
9690
9763
|
}, ms);
|
|
9691
9764
|
this.pending.set(key, pending);
|
|
9692
9765
|
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
|
|
@@ -9696,6 +9769,23 @@ class SubcClient {
|
|
|
9696
9769
|
});
|
|
9697
9770
|
});
|
|
9698
9771
|
}
|
|
9772
|
+
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
9773
|
+
const settleAsTimeout = () => {
|
|
9774
|
+
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
9775
|
+
};
|
|
9776
|
+
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
9777
|
+
const arbitrate = () => {
|
|
9778
|
+
if (this.pending.get(key) !== pending)
|
|
9779
|
+
return;
|
|
9780
|
+
const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
|
|
9781
|
+
if (readerDraining && Date.now() < graceDeadline) {
|
|
9782
|
+
setImmediate(arbitrate);
|
|
9783
|
+
return;
|
|
9784
|
+
}
|
|
9785
|
+
settleAsTimeout();
|
|
9786
|
+
};
|
|
9787
|
+
setImmediate(arbitrate);
|
|
9788
|
+
}
|
|
9699
9789
|
async managedRequest(routeChannel, body, opts) {
|
|
9700
9790
|
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
9701
9791
|
const priority = opts.priority ?? 1 /* Interactive */;
|
|
@@ -9720,6 +9810,9 @@ class SubcClient {
|
|
|
9720
9810
|
if (!handedToSocket) {
|
|
9721
9811
|
return this.notSentCallError("request bytes were not queued to the subc socket", err);
|
|
9722
9812
|
}
|
|
9813
|
+
if (err instanceof SubcError && err.code === REQUEST_DEADLINE_MARKER) {
|
|
9814
|
+
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);
|
|
9815
|
+
}
|
|
9723
9816
|
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", err);
|
|
9724
9817
|
};
|
|
9725
9818
|
return new Promise((resolve2, reject) => {
|
|
@@ -9733,7 +9826,7 @@ class SubcClient {
|
|
|
9733
9826
|
classifyFailure
|
|
9734
9827
|
};
|
|
9735
9828
|
pending.timer = setTimeout(() => {
|
|
9736
|
-
this.
|
|
9829
|
+
this.arbitrateTimeout(key, pending, channel, corr, ms);
|
|
9737
9830
|
}, ms);
|
|
9738
9831
|
this.pending.set(key, pending);
|
|
9739
9832
|
const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
|
|
@@ -9778,6 +9871,9 @@ class SubcClient {
|
|
|
9778
9871
|
return cached.opening;
|
|
9779
9872
|
}
|
|
9780
9873
|
async openCachedRoute(cached) {
|
|
9874
|
+
const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
|
|
9875
|
+
let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
|
|
9876
|
+
let routeRetryAttempt = 0;
|
|
9781
9877
|
for (;; ) {
|
|
9782
9878
|
if (cached.closed)
|
|
9783
9879
|
throw this.routeClosedDuringOpen();
|
|
@@ -9811,6 +9907,15 @@ class SubcClient {
|
|
|
9811
9907
|
}
|
|
9812
9908
|
continue;
|
|
9813
9909
|
}
|
|
9910
|
+
if (!this.closeStarted && err instanceof SubcError && isRetryableRouteOpenCode(err.code)) {
|
|
9911
|
+
routeRetryAttempt += 1;
|
|
9912
|
+
if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
|
|
9913
|
+
await this.opts.sleep(routeRetryDelay);
|
|
9914
|
+
routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
|
|
9915
|
+
continue;
|
|
9916
|
+
}
|
|
9917
|
+
throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${err.code} (retry budget exhausted)`, err);
|
|
9918
|
+
}
|
|
9814
9919
|
throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, err);
|
|
9815
9920
|
}
|
|
9816
9921
|
}
|
|
@@ -9881,7 +9986,9 @@ class SubcClient {
|
|
|
9881
9986
|
for (const cached of this.routes.values()) {
|
|
9882
9987
|
if (cached.closed)
|
|
9883
9988
|
continue;
|
|
9884
|
-
const channel = await this.routeOpen(cached.target, cached.identity
|
|
9989
|
+
const channel = await this.routeOpen(cached.target, cached.identity, {
|
|
9990
|
+
consumerIdentity: cached.consumerIdentity ?? null
|
|
9991
|
+
});
|
|
9885
9992
|
if (cached.closed) {
|
|
9886
9993
|
this.sendRouteGoodbye(channel);
|
|
9887
9994
|
continue;
|
|
@@ -9890,6 +9997,11 @@ class SubcClient {
|
|
|
9890
9997
|
cached.generation = this.generation;
|
|
9891
9998
|
}
|
|
9892
9999
|
}
|
|
10000
|
+
timeoutMessage(channel, corr, ms) {
|
|
10001
|
+
const port = this.sock.localPort();
|
|
10002
|
+
const where = port === null ? "channel" : `local_port=${port} channel`;
|
|
10003
|
+
return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
|
|
10004
|
+
}
|
|
9893
10005
|
routeClosedDuringOpen() {
|
|
9894
10006
|
return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
|
|
9895
10007
|
}
|
|
@@ -9897,9 +10009,16 @@ class SubcClient {
|
|
|
9897
10009
|
try {
|
|
9898
10010
|
for (;; ) {
|
|
9899
10011
|
const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
10012
|
+
this.readerActive = true;
|
|
10013
|
+
try {
|
|
10014
|
+
const header = decodeHeader(headerBytes);
|
|
10015
|
+
const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
|
|
10016
|
+
if (this.sock === sock && this.generation === generation) {
|
|
10017
|
+
this.dispatch({ header, body });
|
|
10018
|
+
}
|
|
10019
|
+
} finally {
|
|
10020
|
+
this.readerActive = false;
|
|
10021
|
+
}
|
|
9903
10022
|
}
|
|
9904
10023
|
} catch (err) {
|
|
9905
10024
|
if (this.sock === sock && this.generation === generation) {
|
|
@@ -9931,13 +10050,20 @@ class SubcClient {
|
|
|
9931
10050
|
this.failChannel(frame.header.channel, new SubcError("route closed by subc (GOODBYE)"));
|
|
9932
10051
|
return;
|
|
9933
10052
|
}
|
|
10053
|
+
if (frame.header.ty === 1 /* Response */ || frame.header.ty === 5 /* Error */ || frame.header.ty === 4 /* StreamEnd */) {
|
|
10054
|
+
debug("dropped terminal frame with no waiter: type=%d channel=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.corr, this.sock.localPort() ?? "?");
|
|
10055
|
+
return;
|
|
10056
|
+
}
|
|
9934
10057
|
}
|
|
9935
10058
|
settle(key, pending, run) {
|
|
10059
|
+
if (this.pending.get(key) !== pending)
|
|
10060
|
+
return false;
|
|
9936
10061
|
this.pending.delete(key);
|
|
9937
10062
|
if (pending.timer)
|
|
9938
10063
|
clearTimeout(pending.timer);
|
|
9939
10064
|
run();
|
|
9940
10065
|
pending.onSettle?.();
|
|
10066
|
+
return true;
|
|
9941
10067
|
}
|
|
9942
10068
|
rejectPending(key, pending, err) {
|
|
9943
10069
|
this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
|
|
@@ -10001,6 +10127,9 @@ function isConsumerReconnectTransient(err) {
|
|
|
10001
10127
|
const code = errorCode(err);
|
|
10002
10128
|
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
10003
10129
|
}
|
|
10130
|
+
function isRetryableRouteOpenCode(code) {
|
|
10131
|
+
return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
|
|
10132
|
+
}
|
|
10004
10133
|
async function connectionFileExists(path) {
|
|
10005
10134
|
try {
|
|
10006
10135
|
await fs2.access(path);
|
|
@@ -10016,7 +10145,8 @@ function normalizeConnectOptions(opts) {
|
|
|
10016
10145
|
identity: opts.identity,
|
|
10017
10146
|
targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
|
|
10018
10147
|
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
10019
|
-
sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)))
|
|
10148
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
|
|
10149
|
+
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
|
|
10020
10150
|
};
|
|
10021
10151
|
}
|
|
10022
10152
|
function routeCacheKey(target, identity, consumerIdentity) {
|
|
@@ -10045,7 +10175,7 @@ function causeMessage(cause) {
|
|
|
10045
10175
|
return "";
|
|
10046
10176
|
return `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
10047
10177
|
}
|
|
10048
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
10178
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/provider.ts
|
|
10049
10179
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
10050
10180
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
|
|
10051
10181
|
var BODY_READ_TIMEOUT_MS2 = 30000;
|
|
@@ -11263,7 +11393,7 @@ class AftRpcClient {
|
|
|
11263
11393
|
}
|
|
11264
11394
|
parsePortFile(filePath) {
|
|
11265
11395
|
try {
|
|
11266
|
-
const content =
|
|
11396
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
11267
11397
|
return parseRpcPortRecord(content);
|
|
11268
11398
|
} catch {
|
|
11269
11399
|
return null;
|
|
@@ -11799,6 +11929,30 @@ async function handleSocketMessage(ws, raw, messageGeneration) {
|
|
|
11799
11929
|
// src/tui/sidebar.tsx
|
|
11800
11930
|
import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js";
|
|
11801
11931
|
|
|
11932
|
+
// src/tui/badge-contrast.ts
|
|
11933
|
+
var MIN_OPAQUE_ALPHA = 0.5;
|
|
11934
|
+
var MIN_CHANNEL_DISTANCE = 0.06;
|
|
11935
|
+
var LIGHT_ACCENT_LUMINANCE = 0.5;
|
|
11936
|
+
function srgbChannelToLinear(c) {
|
|
11937
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
11938
|
+
}
|
|
11939
|
+
function relativeLuminance(bg) {
|
|
11940
|
+
return 0.2126 * srgbChannelToLinear(bg.r) + 0.7152 * srgbChannelToLinear(bg.g) + 0.0722 * srgbChannelToLinear(bg.b);
|
|
11941
|
+
}
|
|
11942
|
+
function nearlyEqual(a, b) {
|
|
11943
|
+
return Math.abs(a.r - b.r) < MIN_CHANNEL_DISTANCE && Math.abs(a.g - b.g) < MIN_CHANNEL_DISTANCE && Math.abs(a.b - b.b) < MIN_CHANNEL_DISTANCE;
|
|
11944
|
+
}
|
|
11945
|
+
function readableTextColorOn(bg) {
|
|
11946
|
+
return relativeLuminance(bg) < LIGHT_ACCENT_LUMINANCE ? "#ffffff" : "#000000";
|
|
11947
|
+
}
|
|
11948
|
+
function badgeTextColor(accent, background) {
|
|
11949
|
+
const alpha = background.a ?? 1;
|
|
11950
|
+
if (alpha >= MIN_OPAQUE_ALPHA && !nearlyEqual(accent, background)) {
|
|
11951
|
+
return background;
|
|
11952
|
+
}
|
|
11953
|
+
return readableTextColorOn(accent);
|
|
11954
|
+
}
|
|
11955
|
+
|
|
11802
11956
|
// src/tui/preferences.ts
|
|
11803
11957
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
11804
11958
|
import { watch } from "node:fs";
|
|
@@ -12369,7 +12523,7 @@ var SidebarContent = (props) => {
|
|
|
12369
12523
|
paddingRight: 1,
|
|
12370
12524
|
backgroundColor: props.theme.accent,
|
|
12371
12525
|
children: /* @__PURE__ */ jsxDEV("text", {
|
|
12372
|
-
fg: props.theme.background,
|
|
12526
|
+
fg: badgeTextColor(props.theme.accent, props.theme.background),
|
|
12373
12527
|
children: /* @__PURE__ */ jsxDEV("b", {
|
|
12374
12528
|
children: [
|
|
12375
12529
|
notInitialized() ? "" : collapsed() ? "▶ " : "▼ ",
|
|
@@ -12384,7 +12538,7 @@ var SidebarContent = (props) => {
|
|
|
12384
12538
|
marginLeft: 1,
|
|
12385
12539
|
backgroundColor: props.theme.warning,
|
|
12386
12540
|
children: /* @__PURE__ */ jsxDEV("text", {
|
|
12387
|
-
fg: props.theme.background,
|
|
12541
|
+
fg: badgeTextColor(props.theme.warning, props.theme.background),
|
|
12388
12542
|
children: /* @__PURE__ */ jsxDEV("b", {
|
|
12389
12543
|
children: "DEGRADED"
|
|
12390
12544
|
}, undefined, false, undefined, this)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@clack/prompts": "^1.6.0",
|
|
34
|
-
"@cortexkit/aft-bridge": "0.
|
|
34
|
+
"@cortexkit/aft-bridge": "0.45.0",
|
|
35
35
|
"@opentui/core": "^0.4.2",
|
|
36
36
|
"@opentui/solid": "^0.4.2",
|
|
37
37
|
"comment-json": "^4.6.2",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"zod": "^4.4.3"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
44
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
45
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
46
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
47
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
48
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
43
|
+
"@cortexkit/aft-darwin-arm64": "0.45.0",
|
|
44
|
+
"@cortexkit/aft-darwin-x64": "0.45.0",
|
|
45
|
+
"@cortexkit/aft-linux-arm64": "0.45.0",
|
|
46
|
+
"@cortexkit/aft-linux-x64": "0.45.0",
|
|
47
|
+
"@cortexkit/aft-win32-arm64": "0.45.0",
|
|
48
|
+
"@cortexkit/aft-win32-x64": "0.45.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@opencode-ai/plugin": "^1.17.11",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the text color for a sidebar badge label drawn on a theme accent.
|
|
3
|
+
* Keep this logic in sync with Magic Context so both sidebars make the same
|
|
4
|
+
* contrast decision for shared themes.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
type Color = { r: number; g: number; b: number; a?: number };
|
|
8
|
+
|
|
9
|
+
const MIN_OPAQUE_ALPHA = 0.5;
|
|
10
|
+
const MIN_CHANNEL_DISTANCE = 0.06;
|
|
11
|
+
const LIGHT_ACCENT_LUMINANCE = 0.5;
|
|
12
|
+
|
|
13
|
+
function srgbChannelToLinear(c: number): number {
|
|
14
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function relativeLuminance(bg: Color): number {
|
|
18
|
+
return (
|
|
19
|
+
0.2126 * srgbChannelToLinear(bg.r) +
|
|
20
|
+
0.7152 * srgbChannelToLinear(bg.g) +
|
|
21
|
+
0.0722 * srgbChannelToLinear(bg.b)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function nearlyEqual(a: Color, b: Color): boolean {
|
|
26
|
+
return (
|
|
27
|
+
Math.abs(a.r - b.r) < MIN_CHANNEL_DISTANCE &&
|
|
28
|
+
Math.abs(a.g - b.g) < MIN_CHANNEL_DISTANCE &&
|
|
29
|
+
Math.abs(a.b - b.b) < MIN_CHANNEL_DISTANCE
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function readableTextColorOn(bg: Color): string {
|
|
34
|
+
return relativeLuminance(bg) < LIGHT_ACCENT_LUMINANCE ? "#ffffff" : "#000000";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function badgeTextColor<T extends Color>(accent: T, background: T): T | string {
|
|
38
|
+
const alpha = background.a ?? 1;
|
|
39
|
+
if (alpha >= MIN_OPAQUE_ALPHA && !nearlyEqual(accent, background)) {
|
|
40
|
+
return background;
|
|
41
|
+
}
|
|
42
|
+
return readableTextColorOn(accent);
|
|
43
|
+
}
|
package/src/tui/sidebar.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type StatusCompression,
|
|
20
20
|
} from "../shared/status";
|
|
21
21
|
import { resolveCortexKitStorageRoot } from "../shared/storage-paths";
|
|
22
|
+
import { badgeTextColor } from "./badge-contrast";
|
|
22
23
|
import {
|
|
23
24
|
createDebouncedStatusRefresh,
|
|
24
25
|
refreshAftTuiSocketScope,
|
|
@@ -592,7 +593,7 @@ const SidebarContent = (props: {
|
|
|
592
593
|
{/* Triangle lives inside the accent badge so the toggle reads as one
|
|
593
594
|
unit: "▶ AFT" / "▼ AFT". Hidden pre-init (nothing to collapse). */}
|
|
594
595
|
<box paddingLeft={1} paddingRight={1} backgroundColor={props.theme.accent}>
|
|
595
|
-
<text fg={props.theme.background}>
|
|
596
|
+
<text fg={badgeTextColor(props.theme.accent, props.theme.background)}>
|
|
596
597
|
<b>
|
|
597
598
|
{notInitialized() ? "" : collapsed() ? "▶ " : "▼ "}
|
|
598
599
|
{prefs().header.label}
|
|
@@ -606,7 +607,7 @@ const SidebarContent = (props: {
|
|
|
606
607
|
marginLeft={1}
|
|
607
608
|
backgroundColor={props.theme.warning}
|
|
608
609
|
>
|
|
609
|
-
<text fg={props.theme.background}>
|
|
610
|
+
<text fg={badgeTextColor(props.theme.warning, props.theme.background)}>
|
|
610
611
|
<b>DEGRADED</b>
|
|
611
612
|
</text>
|
|
612
613
|
</box>
|